以编程方式向 Swift 中的视图添加约束时出错

Posted

技术标签:

【中文标题】以编程方式向 Swift 中的视图添加约束时出错【英文标题】:Error when programatically adding constraints to views in Swift 【发布时间】:2016-07-29 13:05:38 【问题描述】:

我在日志中收到错误,告诉我在 Swift 中以编程方式向视图添加约束时无法同时满足冲突的约束,但 UI 在运行模拟器时看起来符合我的预期您可以在屏幕截图中看到。左侧按钮的前缘与分段控件的前缘对齐,右侧按钮的后缘与分段控件的后缘对齐。

我相信是这些约束导致了问题,因为注释掉这 2 个是阻止错误被抛出的原因,但是 UI 看起来不像我想要的那样。

有人可以帮我理解我做错了什么吗?

Conversion View Controller loaded
2016-07-29 22:51:34.555 WorldTrotter[800:41503] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
(
"<NSLayoutConstraint:0x7fd836313550 UIButton:0x7fd836312e60'Current Location'.left == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.left>",
"<NSLayoutConstraint:0x7fd836313660 UIButton:0x7fd836312e60'Current Location'.trailing == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.centerX - 8>",
"<NSLayoutConstraint:0x7fd836318800 'UIView-Encapsulated-Layout-Width' H:[MKMapView:0x7fd83351d900(0)]>",
"<NSLayoutConstraint:0x7fd836312e00 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'](LTR)   (Names: '|':MKMapView:0x7fd83351d900 )>",
"<NSLayoutConstraint:0x7fd833794d30 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (Names: '|':MKMapView:0x7fd83351d900 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fd836313660 UIButton:0x7fd836312e60'Current Location'.trailing == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.centerX - 8>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2016-07-29 22:51:34.556 WorldTrotter[800:41503] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: 
    (1) look at each constraint and try to figure out which you don't expect; 
    (2) find the code that added the unwanted constraint or constraints and fix it. 
(
"<NSLayoutConstraint:0x7fd836313ee0 UIButton:0x7fd8363136b0'Next Pin'.leading == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.centerX + 8>",
"<NSLayoutConstraint:0x7fd836313fb0 UIButton:0x7fd8363136b0'Next Pin'.trailing == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.trailing>",
"<NSLayoutConstraint:0x7fd836318800 'UIView-Encapsulated-Layout-Width' H:[MKMapView:0x7fd83351d900(0)]>",
"<NSLayoutConstraint:0x7fd836312e00 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'](LTR)   (Names: '|':MKMapView:0x7fd83351d900 )>",
"<NSLayoutConstraint:0x7fd833794d30 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (Names: '|':MKMapView:0x7fd83351d900 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fd836313fb0 UIButton:0x7fd8363136b0'Next Pin'.trailing == UILayoutGuide:0x7fd83630fc20'UIViewLayoutMarginsGuide'.trailing>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

import UIKit
import MapKit

class MapViewController: UIViewController, MKMapViewDelegate 

var mapView: MKMapView!
let locationManager = CLLocationManager()
var coordinates: [CLLocationCoordinate2D] = []
var counter: Int = 0

override func loadView() 
    mapView = MKMapView()
    mapView.delegate = self
    view = mapView


    //Adding pins to map
    let firstLocation = CLLocationCoordinate2DMake(5.000000, -5.000000)
    let secondLocation = CLLocationCoordinate2DMake(-5.000000, 5.000000)
    coordinates.append(firstLocation)
    coordinates.append(secondLocation)



    let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite"])
    segmentedControl.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.5)
    segmentedControl.selectedSegmentIndex = 0
    segmentedControl.translatesAutoresizingMaskIntoConstraints = false

    segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), forControlEvents: .ValueChanged)

    view.addSubview(segmentedControl)

    let margins = view.layoutMarginsGuide

    let topConstraint = segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8)

    let leadingConstraint = segmentedControl.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)


    let trailingConstraint = segmentedControl.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
    topConstraint.active = true
    leadingConstraint.active = true
    trailingConstraint.active = true

    let userButton = UIButton()
    view.addSubview(userButton)
    userButton.translatesAutoresizingMaskIntoConstraints = false
    userButton.addTarget(self, action: #selector(MapViewController.userButtonSelected(_:)), forControlEvents: UIControlEvents.TouchUpInside)
    userButton.setTitle("Current Location", forState: .Normal)
    userButton.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.7)


    let bTopConstraint = userButton.topAnchor.constraintEqualToAnchor(segmentedControl.bottomAnchor, constant: 8)

    //Problematic constraint I think
    let bLConstraint = userButton.leftAnchor.constraintEqualToAnchor(margins.leftAnchor)

    let bTConstraint = userButton.trailingAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: -8)
    bTopConstraint.active = true
    bLConstraint.active = true
    bTConstraint.active = true

    let pinsButton = UIButton()
    view.addSubview(pinsButton)
    pinsButton.translatesAutoresizingMaskIntoConstraints = false
    pinsButton.addTarget(self, action: #selector(MapViewController.pinsButtonSelected(_:)), forControlEvents: UIControlEvents.TouchUpInside)
    pinsButton.setTitle("Next Pin", forState: .Normal)
    pinsButton.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.7)

    let pTopConstraint = pinsButton.topAnchor.constraintEqualToAnchor(segmentedControl.bottomAnchor, constant: 8)

    //Problematic constraint I think
    let pLConstraint = pinsButton.leadingAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: 8)

    let pTConstraint = pinsButton.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
    pTopConstraint.active = true
    pLConstraint.active = true
    pTConstraint.active = true



func mapTypeChanged(segControl: UISegmentedControl) 
    switch segControl.selectedSegmentIndex 
    case 0:
        mapView.mapType = .Standard
    case 1:
        mapView.mapType = .Hybrid
    case 2:
        mapView.mapType = .Satellite
    default:
        break
    



override func viewDidLoad() 
    super.viewDidLoad()


func userButtonSelected(button: UIButton) 
    if mapView.showsUserLocation == false 
        mapView.showsUserLocation = true
     else 
        mapView.showsUserLocation = false
    


func pinsButtonSelected(button: UIButton) 
    if counter >= coordinates.count 
        counter = 0
    
    let dropPin = MKPointAnnotation()
    dropPin.coordinate = coordinates[counter]
    counter += 1
    mapView.addAnnotation(dropPin)
    mapView.setCenterCoordinate(dropPin.coordinate, animated: false)


func mapViewWillStartLocatingUser(mapView: MKMapView) 
    // Ask for Authorisation from the User.
    self.locationManager.requestAlwaysAuthorization()

    // For use in foreground
    self.locationManager.requestWhenInUseAuthorization()
    if CLLocationManager.locationServicesEnabled() 
        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        locationManager.startUpdatingLocation()
    

    mapView.setUserTrackingMode(MKUserTrackingMode.Follow, animated: true)
    print("Tracking user")


【问题讨论】:

错误消息应该包含有关该问题的更多详细信息。也发布错误。 我在这段代码中看不到任何问题。 (我只是在本地运行它,它没有错误)。可能与其他部分有关。你有没有在故事板或代码的其他部分做任何事情,特别是与 MapView 相关的布局。 不是你的问题的原因,只是一个说明,你应该为你的 userButton 使用前导约束而不是左约束 是的,我真的很想看看这里可能出了什么问题。可能是 XCode 中的错误?我对情节提要中的 MapView 没有任何限制,所有内容都已以编程方式添加。我最初是领先的,但改为向左看错误是否会消失。在发布问题之前忘记改回来。我已经添加了整个 ViewController 以查看其他地方是否存在可能导致此问题的问题,但我认为没有。谢谢:) 【参考方案1】:

您的问题来自您没有给您的MKMapView 一个正确的frame。当你像这样创建它时:

mapView = MKMapView()

您将框架设置为0 宽度和0 高度。然后,自动布局将该框架转换为视图宽度和高度的约束。

列出的冲突约束之一是:

<NSLayoutConstraint:0x7fd836318800 'UIView-Encapsulated-Layout-Width' H:[MKMapView:0x7fd83351d900(0)]>

[MKMapView:0x7fd83351d900(0)]中的0表示MKMapView的宽度是0有一个约束,这当然不是你想要的。

您可以通过在创建地图视图时为其提供适当的框架来解决此问题:

替换:

mapView = MKMapView()

与:

mapView = MKMapView(frame: UIScreen.mainScreen().bounds)

我下面的答案通过让 ios 正确设置视图来解决这个问题。


上一个答案

我最初无法重现您的问题,但是当我将您的 viewController 放入 UITabBarController 时,我也看到了 Auto Layout 错误消息。

为了让它发挥作用,我在 Storyboard 中使用了标准的 UIViewController,并将您的 loadView 代码移至 viewDidLoad。我将MKMapView 添加为self.view 的子视图以及适当的约束,使其与self.view 的大小相同:

override func viewDidLoad() 
    super.viewDidLoad()
    
    mapView = MKMapView()
    mapView.delegate = self
    mapView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(mapView)
    
    NSLayoutConstraint.activateConstraints([
        mapView.topAnchor.constraintEqualToAnchor(view.topAnchor),
        mapView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
        mapView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor),
        mapView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)
    ])
    
    //Adding pins to map
    let firstLocation = CLLocationCoordinate2DMake(5.000000, -5.000000)
    let secondLocation = CLLocationCoordinate2DMake(-5.000000, 5.000000)
    coordinates.append(firstLocation)
    coordinates.append(secondLocation)
    
    let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite"])
    segmentedControl.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.5)
    segmentedControl.selectedSegmentIndex = 0
    segmentedControl.translatesAutoresizingMaskIntoConstraints = false
    
    segmentedControl.addTarget(self, action: #selector(MapViewController.mapTypeChanged(_:)), forControlEvents: .ValueChanged)
    
    view.addSubview(segmentedControl)
    
    let margins = view.layoutMarginsGuide
    
    let topConstraint = segmentedControl.topAnchor.constraintEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 8)
    
    let leadingConstraint = segmentedControl.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)
    
    
    let trailingConstraint = segmentedControl.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
    topConstraint.active = true
    leadingConstraint.active = true
    trailingConstraint.active = true
    
    let userButton = UIButton()
    view.addSubview(userButton)
    userButton.translatesAutoresizingMaskIntoConstraints = false
    userButton.addTarget(self, action: #selector(MapViewController.userButtonSelected(_:)), forControlEvents: UIControlEvents.TouchUpInside)
    userButton.setTitle("Current Location", forState: .Normal)
    userButton.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.7)
    
    
    let bTopConstraint = userButton.topAnchor.constraintEqualToAnchor(segmentedControl.bottomAnchor, constant: 8)
    
    //Problematic constraint I think
    let bLConstraint = userButton.leftAnchor.constraintEqualToAnchor(margins.leftAnchor)
    
    let bTConstraint = userButton.trailingAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: -8)
    bTopConstraint.active = true
    bLConstraint.active = true
    bTConstraint.active = true
    
    let pinsButton = UIButton()
    view.addSubview(pinsButton)
    pinsButton.translatesAutoresizingMaskIntoConstraints = false
    pinsButton.addTarget(self, action: #selector(MapViewController.pinsButtonSelected(_:)), forControlEvents: UIControlEvents.TouchUpInside)
    pinsButton.setTitle("Next Pin", forState: .Normal)
    pinsButton.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.7)
    
    let pTopConstraint = pinsButton.topAnchor.constraintEqualToAnchor(segmentedControl.bottomAnchor, constant: 8)
    
    //Problematic constraint I think
    let pLConstraint = pinsButton.leadingAnchor.constraintEqualToAnchor(margins.centerXAnchor, constant: 8)
    
    let pTConstraint = pinsButton.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
    pTopConstraint.active = true
    pLConstraint.active = true
    pTConstraint.active = true
    


替代解决方案:

    将您的 loadView 代码移动到 viewDidLoad 并删除这些行:

    mapView = MKMapView()
    view = mapView
    

    故事板中,将ViewControllerview的类更改为MKMapView

    mapView 设为@IBOutlet

     @IBOutlet var mapView: MKMapView!
    

    故事板中连接插座。

    设置MKMapView 委托。 Control-从Storyboard中的mapView拖动到viewController顶部的ViewController图标并选择delegate em> 从弹出窗口中。你也可以通过调用来连接代理:

    mapView.delegate = self
    

    viewDidLoad.

【讨论】:

@sineil,你尝试过这些建议吗?他们对你有用吗? 我没有尝试过这些建议 - 我特别想了解为什么我会遇到错误而不是修复它们,因为这只是为了在阅读书籍时进行练习。非常感谢您花时间回复 谢谢!正是我想要的。

以上是关于以编程方式向 Swift 中的视图添加约束时出错的主要内容,如果未能解决你的问题,请参考以下文章

未能尝试在 SWIFT 中以编程方式在滚动视图中添加 UIView 约束

如何向放置在 UIStackView 中的以编程方式创建的 UIButton 添加约束

Swift - StackView 和添加约束,以编程方式添加视图到堆栈 - 加载后调整单元格高度?

以编程方式将约束添加到导航栏 Swift

StackView 中的按钮约束(以 Swift 编程方式)

Swift 中的尾随和前导约束以编程方式 (NSLayoutConstraints)