获取以编程方式创建的子视图的框架

Posted

技术标签:

【中文标题】获取以编程方式创建的子视图的框架【英文标题】:Getting frame of programmatically created subviews 【发布时间】:2015-10-15 16:04:39 【问题描述】:

我有一个以编程方式创建的子视图view1。我正在向其中添加一些UIButtons,但这些按钮的尺寸取决于view1.frame.height。但是,我似乎只能得到viewDidAppear 中的值,这显然不是我想要的。此外,如果我将addButtons() 函数放入viewDidLayoutSubviews,我会得到一个无限循环。将addButtons()放入各种UIViewController函数的结果如下(在cmets中):

override func viewDidLoad() 
    super.viewDidLoad()
    addView1()


override func viewDidLayoutSubviews()
    print(view1.frame.height)  // 0.0 first time it is called, 74.0, which is right, the second time it is called, but I got into an infinite loop
    //addButtons() -> infinite loop


override func viewWillAppear(animated: Bool) 
    print(view1.frame.height) //0.0


override func  viewDidAppear(animated: Bool) 
    print(view1.frame.height) //74.0, the value I want, but obviously not where I want it

我的addButtons() 看起来像这样:

 func addButtons()

    let mon = UIButton()
    let tue = UIButton()
    let wed = UIButton()
    let thu = UIButton()
    let fri = UIButton()
    let sat = UIButton()
    let sun = UIButton()


    let buttonsArray = ["mon": mon, "tue": tue, "wed": wed, "thu": thu, "fri": fri, "sat": sat, "sun": sun]
    let daysArray1 = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
    var daysArray2 = ["mon": "M", "tue":"T", "wed": "W", "thu": "T", "fri": "F", "sat": "S", "sun": "S"]
    let buttonSide = view1.frame.height * 0.6

    let distance = (view1.frame.width - buttonSide * 7) / 8.0
    var count = 0

    for day in daysArray1
        let offSet = (buttonSide ) * CGFloat(count) + distance * CGFloat(count + 1)
        let centerX = (buttonSide / 2) + offSet



        buttonsArray[day]!.setTitle(daysArray2[day], forState: .Normal)

        buttonsArray[day]!.translatesAutoresizingMaskIntoConstraints = false

        buttonsArray[day]!.layer.cornerRadius = 5

        view1.addSubview(buttonsArray[day]!)

        view1.addConstraint(NSLayoutConstraint(item: buttonsArray[day]!, attribute: NSLayoutAttribute.CenterY, relatedBy: .Equal, toItem: view1, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0))
        view1.addConstraint(NSLayoutConstraint(item: buttonsArray[day]!, attribute: NSLayoutAttribute.CenterX, relatedBy: .Equal, toItem: view1, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: centerX))
        view1.addConstraint(NSLayoutConstraint(item: buttonsArray[day]!, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: buttonSide))
        view1.addConstraint(NSLayoutConstraint(item: buttonsArray[day]!, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: buttonSide))
        count += 1


    

【问题讨论】:

“但显然不是我想要的” .... 你还想要它在哪里? 【参考方案1】:

每次您向视图添加按钮时,系统都会调用 viewDidLayoutSubviews,您会不断添加按钮,并且系统会不断调用 viewDidLayoutSubviews。你应该在 viewDidLoad() 时添加你的按钮。 要匹配视图的高度,您可以:

将所有按钮固定到视图的尾部和前沿 将第一个按钮的顶部固定在视图的顶部边缘 将最后一个按钮的底部固定在视图的底部 将每个按钮的顶部与前一个按钮的底部边缘固定在一起(假设不是第一个按钮) 将所有按钮的 .Height 约束为与视图的 .Height 相匹配,乘数为 0.6

这是我使用PureLayout做的一个例子

    let someView = UIView(frame: CGRectZero)
    view.addSubview(someView)
    var prev: UIButton?
    for i in 0..<6 
        let button = UIButton(frame: CGRectZero)
        button.setBackgroundColor(UIColor.randomColor(), forUIControlState: .Normal)
        someView.addSubview(button)
        button.autoPinEdgeToSuperviewEdge(.Trailing)
        button.autoPinEdgeToSuperviewEdge(.Leading)
        button.autoMatchDimension(.Height, toDimension: .Height, ofView: someView, withMultiplier: 0.6)
        if let previous = prev 
            button.autoPinEdge(.Top, toEdge: .Bottom, ofView: previous)
         else 
            button.autoPinEdgeToSuperviewEdge(.Top)
        
        prev = button
    

    someView.autoPinEdgeToSuperviewEdge(.Trailing)
    someView.autoPinEdgeToSuperviewEdge(.Leading)
    someView.autoPinEdgeToSuperviewEdge(.Top)
    someView.autoSetDimension(.Height, toSize: 100)

更新(相等的水平间距)

好的,根据您的评论,我知道您还需要按钮之间的间距以及父容器的侧边相等。完整的工作视图控制器类在哪里:

import UIKit

class ViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()

        let wrapper = UIView(frame: CGRectZero)
        view.addSubview(wrapper)
        wrapper.backgroundColor = UIColor.redColor()

        var previous: (button: UIButton, spacer: UIView)?
        for day in ["M", "T", "W", "T", "F", "S", "S"] 
            let button = UIButton(frame: CGRectZero)
            let spacer = UIView(frame: CGRectZero)
            button.setTitle(day, forState: .Normal)
            button.backgroundColor = UIColor.grayColor()
            button.translatesAutoresizingMaskIntoConstraints = false
            spacer.translatesAutoresizingMaskIntoConstraints = false
            wrapper.addSubview(button)
            wrapper.addSubview(spacer)

            //set height 60% of wrapper
            wrapper.addConstraint(NSLayoutConstraint(
                item: button,
                attribute: .Height,
                relatedBy: .Equal,
                toItem: wrapper,
                attribute: .Height,
                multiplier: 0.6,
                constant: 0))

            //set width same has height
            wrapper.addConstraint(NSLayoutConstraint(
                item: button,
                attribute: .Height,
                relatedBy: .Equal,
                toItem: button,
                attribute: .Width,
                multiplier: 1,
                constant: 0))

            //pin button leading with spacer trailing
            wrapper.addConstraint(NSLayoutConstraint(
                item: button,
                attribute: .Leading,
                relatedBy: .Equal,
                toItem: spacer,
                attribute: .Trailing,
                multiplier: 1,
                constant: 0))


            //pin spacer trailing with button leading
            wrapper.addConstraint(NSLayoutConstraint(
                item: spacer,
                attribute: .Trailing,
                relatedBy: .Equal,
                toItem: button,
                attribute: .Leading,
                multiplier: 1,
                constant: 0))

            //align button to center
            wrapper.addConstraint(NSLayoutConstraint(
                item: button,
                attribute: .CenterY,
                relatedBy: .Equal,
                toItem: wrapper,
                attribute: .CenterY,
                multiplier: 1,
                constant: 0))

            if let previous = previous 
                //pin spacer Leading with previous button trailing
                wrapper.addConstraint(NSLayoutConstraint(
                    item: spacer,
                    attribute: .Leading,
                    relatedBy: .Equal,
                    toItem: previous.button,
                    attribute: .Trailing,
                    multiplier: 1,
                    constant: 0))

                //pin previous button's Trailing with spacer Leading
                wrapper.addConstraint(NSLayoutConstraint(
                    item: previous.button,
                    attribute: .Trailing,
                    relatedBy: .Equal,
                    toItem: spacer,
                    attribute: .Leading,
                    multiplier: 1,
                    constant: 0))

                //set spacer's width same as previous spacer
                wrapper.addConstraint(NSLayoutConstraint(
                    item: spacer,
                    attribute: .Width,
                    relatedBy: .Equal,
                    toItem: previous.spacer,
                    attribute: .Width,
                    multiplier: 1,
                    constant: 0))

             else 
                //this is the first item, pin the spacer Leading to wrapper Leading
                wrapper.addConstraint(NSLayoutConstraint(
                    item: spacer,
                    attribute: .Leading,
                    relatedBy: .Equal,
                    toItem: wrapper,
                    attribute: .Leading,
                    multiplier: 1,
                    constant: 0))
            

            previous = (button: button, spacer: spacer)
        
        // last spacer
        if let previous = previous 
            let spacer = UIView(frame: CGRectZero)
            spacer.translatesAutoresizingMaskIntoConstraints = false
            wrapper.addSubview(spacer)
            //pin spacer Leading with previous button trailing
            wrapper.addConstraint(NSLayoutConstraint(
                item: spacer,
                attribute: .Leading,
                relatedBy: .Equal,
                toItem: previous.button,
                attribute: .Trailing,
                multiplier: 1,
                constant: 0))


            //pin previous button's Trailing with spacer Leading
            wrapper.addConstraint(NSLayoutConstraint(
                item: previous.button,
                attribute: .Trailing,
                relatedBy: .Equal,
                toItem: spacer,
                attribute: .Leading,
                multiplier: 1,
                constant: 0))

            //set spacer's width same as previous spacer
            wrapper.addConstraint(NSLayoutConstraint(
                item: spacer,
                attribute: .Width,
                relatedBy: .Equal,
                toItem: previous.spacer,
                attribute: .Width,
                multiplier: 1,
                constant: 0))

            //pin spacer's Trailing with wrappper Trailing
            wrapper.addConstraint(NSLayoutConstraint(
                item: spacer,
                attribute: .Trailing,
                relatedBy: .Equal,
                toItem: wrapper,
                attribute: .Trailing,
                multiplier: 1,
                constant: 0))
        

        wrapper.frame = CGRect(x: 0, y: 100, width: 320, height: 40)
    



【讨论】:

感谢您的回答!但我试图将按钮的高度设置为它们超级视图高度的 60%,这就是为什么我需要先获取它们的超级视图的框架。 viewDidLoad() 有没有办法做到这一点? 啊-好的,您可以将视图的 .Height 与 0.6 的乘数匹配,而不是将 .Height 与以前匹配-我编辑了我的答案。 非常感谢!我实际上还有一个问题:在我的原始代码中,我用它来计算每个按钮之间的距离:let distance = (view1.frame.width - buttonSide * 7) / 8.0。有没有办法在不调用 view1.frame 的情况下做到这一点? @user245954 我很难想象你想要实现的布局,你能快速发布你想要的布局图像吗? 是的,这是我发布的question,第一个答案解决了按钮本身之间的相等(水平)间距,但我想要的是按钮和容器视图之间的相等间距(领先和后缘)。

以上是关于获取以编程方式创建的子视图的框架的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式添加的子视图框架不会显示在滚动视图中

以编程方式创建一个视图,将框架设置为超级视图的框架?

如何通过按下子视图中的按钮来删除以编程方式创建的子视图

当内部按钮触摸时,如何从 Superview 中删除以编程方式创建的子视图?

我们可以将具有自动布局的 XIB 加载为以编程方式创建的没有自动布局的视图的子视图,并使用父视图调整子视图的大小吗?

基于在 Storyboard 中使用自动布局创建的另一个视图,以编程方式使用框架创建视图