【问题标题】:How can I fix a "removeConstraint() not working" problem with my UICollectionView?如何解决我的 UICollectionView 的“removeConstraint() not working”问题?
【发布时间】:2019-04-07 17:39:43
【问题描述】:

以编程方式(没有情节提要),我试图删除一组约束并在设备旋转时创建一个新集。我用来删除约束的代码执行没有错误,但实际上并没有删除约束。这会在添加新的(冲突的)约束集时导致布局错误。

我正在使用没有情节提要的 Xcode 10.1 和 Swift 4.2。在我的 MainViewController 中,我从viewDidLoad() 调用的函数中创建并放置了一个名为“ticketsView”的 UICollectionView。在addChild()addSubview() 之后,我调用了一个名为applyTicketsViewConstraints() 的函数,它确定设备的当前方向,然后(预计)删除任何现有约束并为当前设备方向应用适当的一组约束。

每当设备旋转时,我都会覆盖 viewWillTransition:toSize:withCoordinator(),它会从协调器闭包中调用相同的 applyTicketsViewConstraints() 函数。

在应用启动时,会根据初始设备方向正确应用约束。设备第一次旋转 90 度时,显示也成功更改,但之后的任何旋转都会导致“[LayoutConstraints] 无法同时满足约束”错误消息。

我尝试循环遍历现有的 ticketView 约束并一次删除它们,并且我尝试一次将它们全部删除 - 但两种方法都不会导致实际删除约束。它们似乎都运行成功,但完成后约束仍然存在。

    func createTicketCollectionWindow() {
        ticketsLayout = UICollectionViewFlowLayout()
        ticketsVC = TicketCollectionController(collectionViewLayout: ticketsLayout)
        ticketsVC?.collectionView?.backgroundColor = UIColor.darkGray
        ticketsView = ticketsVC!.view
        ticketsView.translatesAutoresizingMaskIntoConstraints = false
        addChild(ticketsVC!)
        self.view.addSubview(ticketsView)
        applyTicketsViewConstraints()
    }
    func applyTicketsViewConstraints() {
        let safe = self.view.safeAreaLayoutGuide
        let deviceSize = UIScreen.main.bounds.size
        for individualConstraint in ticketsView.constraints {
            print("TicketViewConstraints:   Removing Constraint: \(individualConstraint)" )
            // ticketsView.removeConstraint(individualConstraint)
        }
        ticketsView.removeConstraints(ticketsView.constraints)
        // self.view.removeConstraints(ticketsView.constraints)
        if deviceSize.width < deviceSize.height {
            print("TicketsView Constraint:  Device is in PORTRAIT orientation!")
            ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width).isActive = true
            ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ).isActive = true
            ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0).isActive = true
            ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75).isActive = true
        } else {
            print("TicketsView Constraint:  Device is in LANDSCAPE orientation!")
            ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width).isActive = true
            ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ).isActive = true
            ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0).isActive = true
            ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65).isActive = true
        }
        ticketsView.layoutIfNeeded()
    }
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: { (_) in
            self.applyTicketsViewConstraints()
            self.ticketsVC?.collectionView.reloadData()
        }) { (_) in
            self.ticketsView.layoutIfNeeded()
        }
    }

我希望我在上面的代码中创建的 4 个约束将被删除,以便我可以使用不同的值重新创建它们。我似乎找不到删除它们的方法(有效)。上面代码中注释掉的上面两行表明我也尝试过(不成功)。

【问题讨论】:

    标签: ios swift nslayoutconstraint


    【解决方案1】:

    你可以试试

    var portrait = [NSLayoutConstraint]()
    var landscape = [NSLayoutConstraint]()
    
    let porCon1  = ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width) 
    let porCon2  =  ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ) 
    let porCon3  = ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0) 
    let porCon4  = ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75) 
    
    portrait = [porCon1,porCon2,porCon3,porCon4]
    
    let landCon1  = ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width) 
    let landCon1  = ticketsView.trailingAnchor.constraint(equalTo:safe.trailingAnchor, constant: 0 ) 
    let landCon1  = ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0) 
    let landCon1  = ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65) 
    
    landscape = [landCon1,landCon2,landCo3,landCon4]
    

    func setPortrait(_ por:Bool) {  
        portrait.forEach { $0.isActive = por }
        landscape.forEach { $0.isActive = !por } 
    }
    

    也可以

    NSLayoutConstraint.deActivate(portrait)
    NSLayoutConstraint.deActivate(landscape)
    if por {
       NSLayoutConstraint.activate(portrait)
    }
    else { 
      NSLayoutConstraint.activate(landscape)
    }
    

    【讨论】:

    • 您的解决方案有效 - 谢谢 - 但正确的方法是在您创建的整个数组上使用 NSLayoutConstraint.activate() 和 NSLayoutConstraint.deactivate() 函数,而不是设置的 forEach 循环每个 .isActive 值独立。
    • 不要忘记,在激活新约束之前,您应该始终停用当前约束。
    【解决方案2】:

    我相信您必须使用激活/停用约束,例如:

    为它们创建变量并在 setupConstrainsMethod 中设置约束值。 var notRotatedYConstraint: NSLayoutConstraint!

            var rotatedYConstraint: NSLayoutConstraint!
    

    在 viewdidload 中: NSLayoutConstraint.activate([notRotatedYConstraint])

    在 iPhone 旋转时的函数中。 NSLayoutConstraint.deactivate([notRotatedYConstraint]) NSLayoutConstraint.activate([rotatedYConstraint])

    【讨论】:

      【解决方案3】:

      发布的前 2 个答案结合在一起,为我提供了所需的答案。 @Sh_Khan 指示我使用 NSLayoutConstraint 数组。 @Wurzel 指出我应该使用 NSLayoutConstraint.activate() 和 .deactivate() 函数一次性启用/禁用整个约束组。

      所以,关键是将所有纵向约束添加到一个数组,将所有横向约束添加到另一个数组,然后每次方向更改时,我都会在两个数组上调用 deactivate 函数,然后重新创建和激活相应的数组。

      (当设备方向发生变化时,safeAreaLayoutGuide 会发生变化,这就是为什么我无法提前预先创建两组约束。我还想要一个解决方案,如果约束变得更加动态 - 例如响应给用户调整显示区域的大小)

      我原来的代码现在变成了:

          func applyTicketsViewConstraints() {
              let safe = self.view.safeAreaLayoutGuide
              let deviceSize = UIScreen.main.bounds.size
      
              NSLayoutConstraint.deactivate(portraitTicketsViewConstraints)
              NSLayoutConstraint.deactivate(landscapeTicketsViewConstraints)
      
              if deviceSize.width < deviceSize.height {
                  print("TicketsView Constraint:  Device Size is in PORTRAIT orientation!")
                  portraitTicketsViewConstraints = [
                      ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width),
                      ticketsView.trailingAnchor.constraint(equalTo: safe.trailingAnchor, constant: 0 ),
                      ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0),
                      ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.75)
                  ]
                  NSLayoutConstraint.activate(portraitTicketsViewConstraints)
              } else {
                  print("TicketsView Constraint:  Device Size is in LANDSCAPE orientation!")
                  landscapeTicketsViewConstraints = [
                      ticketsView.leadingAnchor.constraint(equalTo: safe.trailingAnchor, constant: -safe.layoutFrame.width),
                      ticketsView.trailingAnchor.constraint(equalTo: safe.trailingAnchor, constant: 0 ),
                      ticketsView.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: 0),
                      ticketsView.heightAnchor.constraint(equalToConstant: safe.layoutFrame.height * 0.65)
                  ]
                  NSLayoutConstraint.activate(landscapeTicketsViewConstraints)
              }
              ticketsView.layoutIfNeeded()
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-07
        • 1970-01-01
        • 2015-08-22
        • 1970-01-01
        • 2022-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多