首页 文章

以编程方式超出UIView约束

提问于
浏览
0

我有3个UIView层的蛋糕,具有编程约束 .

用于以编程方式设置约束的构造函数:

func setupViewConstraints(item:UIView, leadingTo:NSLayoutXAxisAnchor, leadingCon:CGFloat, 
trailingTo:NSLayoutXAxisAnchor, trailingCon:CGFloat, topTo:NSLayoutYAxisAnchor, 
topCon:CGFloat, bottomTo:NSLayoutYAxisAnchor, bottomCon:CGFloat) {
    item.translatesAutoresizingMaskIntoConstraints = false
    item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true
    item.trailingAnchor.constraint(equalTo: trailingTo, constant: trailingCon).isActive = true
    item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true
    item.bottomAnchor.constraint(equalTo: bottomTo, constant:bottomCon).isActive = true
}

最低的基础层是lightGray .

view = UIView()
view.backgroundColor = .lightGray

第二层包含带有约束的2 UIView (红色和蓝色) .

let red = UIView()
red.backgroundColor = .red
view.addSubview(red)
setupViewConstraints(item: red, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: -(view.frame.width)*0.2), topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: -(view.frame.width)*0.8)

let blue = UIView()
blue.backgroundColor = .blue
view.addSubview(blue)
setupViewConstraints(item: blue, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: -(view.frame.width)*0.2), topTo: red.bottomAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0)

3 UIView

在顶部我有黄色 UIView 层,它与所有较低层重叠 .

let yellow = UIView()
yellow.backgroundColor = .yellow
view.addSubview(yellow)
setupViewConstraints(item: yellow, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: 0, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0)

另外,我在 UIView 内有 UINavigationBarUINavigationItem .

//Add navigation item and buttons
        naviItem = UINavigationItem()
        naviItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem:.add, target:self, action:#selector(goToDestVC)), animated: true)
        naviItem.setLeftBarButton(UIBarButtonItem(image: UIImage(named: "hamburger_slim_30"), style: .plain, target: self, action: #selector(hamburgerBtnPressed)), animated: true)

        //Add navigation bar with transparent background
        naviBar = UINavigationBar()
        naviBar.setBackgroundImage(UIImage(), for: .default)
        naviBar.shadowImage = UIImage()
        naviBar.isTranslucent = true

// Assign the navigation item to the navigation bar
        naviBar.items = [naviItem]

        view.addSubview(naviBar)
        setupViewConstraints(item: naviBar, leadingTo: yellow.leadingAnchor, leadingCon: 0, trailingTo: yellow.trailingAnchor, trailingCon: 0, topTo: yellow.topAnchor, topCon: 0, bottomTo: yellow.bottomAnchor, bottomCon: -(view.frame.height)*0.9))

Top UIView layer with UINavigationBar

我有 hamburgerBtnPressed 函数,它应该将黄色图层向右移动80%(我将前导和尾随常量的值更改为80%), but this does not work!!!

var hamburgerMenuIsVisible = false

    @objc func hamburgerBtnPressed(_ sender: Any) {

        if !hamburgerMenuIsVisible {

            let menuWidth = (self.view.frame.width)*0.8

              setupViewConstraints(item: layoutView, leadingTo: view.leadingAnchor, leadingCon: menuWidth, trailingTo: view.trailingAnchor, trailingCon: menuWidth, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0)

            hamburgerMenuIsVisible = true

        } else {

            setupViewConstraints(item: layoutView, leadingTo: view.leadingAnchor, leadingCon: 0, trailingTo: view.trailingAnchor, trailingCon: 0, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0)

            hamburgerMenuIsVisible = false
        }

        // layoutIfNeeded() lays out the subviews immediately and forces the layout before drawing
        UIView.animate(withDuration: 0.2, delay:0.0, options: .curveEaseIn, animations: {

            self.view.layoutIfNeeded()

        }) { (animationComplete) in
            print("Animation is complete!")
        }
    }

但是如果我将前导和尾随常量的值更改为负数,则一切都会起作用,菜单将向左移动而没有任何问题 .

let menuWidth = -(self.view.frame.width)*0.8

To the left

请解释..问题是什么?为什么黄色 UIView 向左移动了约束的负值,并且不能与约束的正值一起使用?并给出一个错误:

Probably at least one of the constraints in the following list is one you don't want. 
(
    "<NSLayoutConstraint:0x6040002853c0 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing   (active)>",
    "<NSLayoutConstraint:0x604000092750 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing + 331.2   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x604000092750 UIView:0x7fa947c35850.trailing == UIView:0x7fa947e1d2d0.trailing + 331.2   (active)>

Update: 我选择 Option 2 :保留对要更改的约束的引用,并调整其常量 . 在layoutIfNeeded之前也需要调用setNeedsLayout .

更新的代码:

var leadingC: NSLayoutConstraint!
var trailingC: NSLayoutConstraint!
var yellow: UIView!

的loadView():

yellow = UIView()
        yellow.backgroundColor = .yellow
        view.addSubview(yellow)

        //Set up leading and trailing constraints for handling yellow view shift
        leadingC = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
        trailingC = yellow.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)

//Put leadingC.constant and trailingC.constant into the function
        setupViewConstraints(item: yellow, leadingTo: view.leadingAnchor, leadingCon: leadingC!.constant, trailingTo: view.trailingAnchor, trailingCon: trailingC.constant, topTo: view.topAnchor, topCon: 0, bottomTo: view.bottomAnchor, bottomCon: 0)

更新汉堡功能:

@objc func hamburgerBtnPressed(_ sender: Any) {

        if !hamburgerMenuIsVisible {

            let menuWidth = (self.view.frame.width)*0.8


            leadingC!.constant = menuWidth
            trailingC!.constant = menuWidth

            print(leadingC.constant, trailingC.constant)

            hamburgerMenuIsVisible = true

        } else {

            leadingC!.constant = 0
            trailingC!.constant = 0


            hamburgerMenuIsVisible = false
        }

        // layoutIfNeeded() lays out the subviews immediately and forces the layout before drawing
        UIView.animate(withDuration: 0.2, delay:0.0, options: .curveEaseIn, animations: {

            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()

        }) { (animationComplete) in
            print("Animation is complete!")
        }
    }
    var hamburgerMenuIsVisible = false

我没有错误和“动画完成!”也印了,但屏幕上没有任何反应,没有动画 .

Output

1 回答

  • 1

    首先,它需要负值,因为需要在正确的方向上设置约束 . 更改此设置,您可以删除所有这些负常量:

    item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true
    item.trailingAnchor.constraint(equalTo: trailingTo, constant: trailingCon).isActive = true
    item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true
    item.bottomAnchor.constraint(equalTo: bottomTo, constant:bottomCon).isActive = true
    

    item.leadingAnchor.constraint(equalTo: leadingTo, constant: leadingCon).isActive = true
    trailingTo.constraint(equalTo: item.trailingAnchor, constant: trailingCon).isActive = true
    item.topAnchor.constraint(equalTo: topTo, constant:topCon).isActive = true
    bottomTo.constraint(equalTo: item.bottomAnchor, constant:bottomCon).isActive = true
    

    其次,每次调用 setupViewConstraints 时,您都在创建并激活另一组约束 .

    选项1:

    在重新设置之前,请删除黄色视图的所有约束 .

    选项2:

    保持对要更改的约束的引用,并仅调整其常量 . 您可能需要在 layoutIfNeeded 之前调用 setNeedsLayout .

    选项3:

    添加2个约束 . 最初的主要约束,以及您想要的宽度 . 当您要显示/隐藏菜单时,将第一个约束的优先级更改为 999 (默认为 1000 )并切换另一个约束的 isActive 属性 .

    let leading = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
    leading.priority = UILayoutPriority(999)
    leading.isActive = true
    
    let otherConstraint = yellow.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: (view.frame.width)*0.8)
    otherConstraint.isActive = false // toggle this property to show/hide
    

    选项2可能是性能最佳的选择 . 来自苹果文档:

    在现有约束上设置常量比删除约束并添加一个与旧版本完全相同的新约束要好得多,只是它具有不同的常量

相关问题