ios - 以编程方式向 Swift 中的 View 添加约束时出错

标签 ios swift autolayout

我在日志中收到错误,告诉我在 Swift 中以编程方式向 View 添加约束时存在无法同时满足的冲突约束,但 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")
}


Screenshot of the layout, even with constraint errors in log

最佳答案

您的问题是因为您没有提供 MKMapView适当的frame .当你这样创建它时:

mapView = MKMapView()

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

列出的冲突约束之一是:

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

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

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

替换:

mapView = MKMapView()

与:

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

我在下面的回答通过让 iOS 正确设置 View 来解决这个问题。


上一个答案

最初我无法重现您的问题,但是当我将您的 viewController 放在 UITabBarController 中时,我也看到了自动布局错误消息。

为了让它工作,我使用了一个标准 UIViewControllerStoryboard 中移动了你的loadView编码为 viewDidLoad .我添加了 MKMapView作为 self.view 的 subview 加上适当的约束,使其大小与 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
    
}

备选方案:

  1. 移动您的 loadView代码为 viewDidLoad并摆脱这些行:

    mapView = MKMapView()
    view = mapView
    
  2. Storyboard中,更改 view 的类在ViewControllerMKMapView .

  3. 制作mapView一个@IBOutlet :

     @IBOutlet var mapView: MKMapView!
    
  4. Storyboard中连接 socket 。

  5. 设置MKMapView代表。 Control-从mapView拖动在 StoryboardviewController 顶部的 ViewController 图标并从弹出窗口中选择委托(delegate)。您还可以通过调用以下方式连接委托(delegate):

    mapView.delegate = self
    

    viewDidLoad .

关于ios - 以编程方式向 Swift 中的 View 添加约束时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38659695/

相关文章:

ios - 当在顶部添加新帖子时,UICollectionView 单元格自定义 FlowLayout 中断

xcode - Interface Builder 文件中的未知类 - Xcode 8 Swift 3

swift - 在停用 subview 的约束后对 tableviewcell 进行动画处理

ios - 为什么我在没有互联网的情况下通过 Game Center 发送数据时没有收到错误消息?

ios - 不兼容的库版本 : AwaitKit requires version 1. 0.0 或更高版本,但 libswiftCore.dylib 提供版本 0.0.0

ios - SceneKit 中的土星环节点

ios - 保存要解析的图像数组

ios - Segue show(push) 没有出现在 Objective C 项目中

ios - 自动调整按钮大小以匹配图形设计师在 Swift/Xcode 中的意图的好方法?

swift - 步长值大于 1 的 For 循环