Swift:在超级视图内部动态堆叠子视图

时间:2019-04-01 20:32:36

标签: ios swift uiview uiviewcontroller

我正在尝试在一个看起来像下面列出的页面中创建登录名。我编写的代码应在没有徽标和[Login | Register]切换按钮的情况下产生此视图。我的盒子的高度和宽度也不同,但是我并不担心。

enter image description here

目前,我正在获取此输出。我担心单词在顶部如何相互重叠。

enter image description here

在下面列出的代码中,我创建了3个文本字段,按钮和文本字段的容器。我相信函数fieldConstraints中有问题。在此函数中,我浏览了所有文本字段的数组,并为它们分配了必要的约束。除了第一个文本字段之后的每个文本字段的topAnchor设置为等于之前放置在文本字段下方的分隔符的bottomAnchor之外,它们都具有相同的约束。文本字段之间的蓝线是分隔符。

主班

class SignIn: UIViewController {

    override func loadView() {
        super.loadView()
        let inputContainer = inputDataContainer()
        constraintsToCenterSubview(forView: inputContainer, width: 100, height: 100)
        let nameField = field(for: "Name")
        let emailField = field(for: "Email address")
        let passField = field(for: "Password")
        let fields = [nameField, emailField, passField]
        let button = loginButton()
        fieldConstraints(subviews: fields, superview: inputContainer)
        self.centerViewBelow(forView: button, whichIsBelow: inputContainer, increaseWidthBy: 0)
    }

    func inputDataContainer() -> UIView{
        let inputView = UIView(frame: CGRect(x: self.view.center.x, y: self.view.center.y, width: CGFloat(100), height: CGFloat(100)))
        inputView.backgroundColor = UIColor.white
        inputView.translatesAutoresizingMaskIntoConstraints = false
        inputView.layer.cornerRadius = 5
        inputView.layer.masksToBounds = true
        self.view.addSubview(inputView)
        //inputView = centerViewBelow(forView: inputView, whichIsBelow: self.view, increaseWidthBy: 100)
        return inputView
    }

    func loginButton() -> UIButton {
        let button = UIButton()
        button.backgroundColor = UIColor(r: 80, g: 101, b: 161)
        button.setTitle("Submit", for: [])
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitleColor(UIColor.white, for: [])
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
        self.view.addSubview(button)
        return button
    }

    func field(for name: String) -> UITextField{
        let tf = UITextField()
        tf.placeholder = name
        tf.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(tf)
        return tf
    }

    func fieldSep() -> UIView {
        let view = UIView()
        view.backgroundColor = UIColor(r: 220, g: 220, b: 220)
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }

    func fieldConstraints(subviews: [UIView], superview: UIView) {
        var sep: UIView?
        let len = subviews.endIndex
        for (idx, subview) in subviews.enumerated(){
            superview.addSubview(subview)
            subview.leftAnchor.constraint(equalTo: superview.leftAnchor)
            subview.rightAnchor.constraint(equalTo: superview.rightAnchor)
            subview.widthAnchor.constraint(equalTo: superview.widthAnchor)
            subview.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: CGFloat(1/len))
            if (sep != nil){
                subview.topAnchor.constraint(equalTo: sep!.bottomAnchor)
            }else{
                subview.topAnchor.constraint(equalTo: superview.topAnchor)
            }
            sep = fieldSep()
            if idx < subviews.endIndex-1 {
                self.view.addSubview(sep!)
                sep?.leftAnchor.constraint(equalTo: superview.leftAnchor)
                sep?.rightAnchor.constraint(equalTo: superview.rightAnchor)
                sep?.topAnchor.constraint(equalTo: subview.bottomAnchor)
            }
        }


    }
}

扩展名

extension UIColor {
    convenience init(r:CGFloat, g:CGFloat, b: CGFloat) {
        self.init(red: r/255, green: g/255, blue: b/255, alpha: 1)
    }
}

extension UIViewController {
    func centerViewBelow(forView view: UIView, whichIsBelow topView: UIView, increaseWidthBy constant: CGFloat){
        let topConstraint = NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal, toItem: topView, attribute: .bottom, multiplier: 1, constant: 20)
        let widthConstraint = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: topView, attribute: .width, multiplier: 1, constant: constant)
        let centerConstraint = NSLayoutConstraint(item: view, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
        let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50)
        NSLayoutConstraint.activate([topConstraint, widthConstraint, centerConstraint, heightConstraint])
        //return view
    }

    func constraintsToCenterSubview(forView view: UIView, width: Int, height: Int){
        let centerXConstraint = NSLayoutConstraint(item: view, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
        let centerYConstraint = NSLayoutConstraint(item: view, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
        let widthConstraint = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .width, multiplier: 1, constant: CGFloat(width))
        let heightConstraint = NSLayoutConstraint(item: view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: CGFloat(height))
        NSLayoutConstraint.activate([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])
    }

}

谢谢


--------------------------------------------------- -------------------------------------------------- ---------


更新

因此,通过将容器更改为堆栈视图,我几乎可以做到这一点。但这使我的角落不再圆滑。有人知道如何解决这个问题吗?

func inputDataContainer() -> UIStackView{
    let inputView = UIStackView(frame: CGRect(x: self.view.center.x, y: self.view.center.y, width: CGFloat(100), height: CGFloat(100)))
    inputView.backgroundColor = UIColor.white
    inputView.translatesAutoresizingMaskIntoConstraints = false
    inputView.layer.cornerRadius = 5
    inputView.layer.masksToBounds = true
    inputView.distribution = .fillEqually
    inputView.axis = .vertical
    inputView.spacing = 1
    self.view.addSubview(inputView)
    //inputView = centerViewBelow(forView: inputView, whichIsBelow: self.view, increaseWidthBy: 100)
    return inputView
}

func fieldConstraints(subviews: [UIView], superview: UIStackView) {
    for subview in subviews{
        superview.addArrangedSubview(subview)
        subview.clipsToBounds = true
    }
}

当前应用的屏幕截图示例

enter image description here

3 个答案:

答案 0 :(得分:1)

尝试给予高度

sep?.heightAnchor.constraint(equalToConstant:1.0).isActive = true

对于fieldConstraints中的所有约束,您也会忘记

.isActive = true

或使用NSLayoutConstraint.activate,例如

NSLayoutConstraint.activate([
     subview.leftAnchor.constraint(equalTo: superview.leftAnchor),
     subview.rightAnchor.constraint(equalTo: superview.rightAnchor)  
     subview.widthAnchor.constraint(equalTo: superview.widthAnchor),
     subview.heightAnchor.constraint(equalTo: superview.heightAnchor, multiplier: CGFloat(1/len))
])

这种方法行得通,但是最好使用带有分布.fillEqually的垂直堆栈视图,它将对其进行分区并添加

 fileds.forEach { stackview.addArrangedSubview($0) }

答案 1 :(得分:0)

使用UIStackView。它将帮助您节省构建复杂UI的时间。

答案 2 :(得分:0)

使用UIStackView会遇到一些麻烦。您可以将其嵌入UIView中,并为该视图提供圆角,但随后您必须在堆栈视图中的 内的字段中添加填充,以获取分隔线。

这是另一种可能更适合您的方法。它更接近您的原始代码,将字段和分隔符视图添加到UIView

extension UIColor {
    convenience init(r:CGFloat, g:CGFloat, b: CGFloat) {
        self.init(red: r/255, green: g/255, blue: b/255, alpha: 1)
    }
}

class SampleViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor(r: 65, g: 92, b: 144)

        let nameField = field(for: "Name")
        let sep1 = fieldSep()
        let emailField = field(for: "Email address")
        let sep2 = fieldSep()
        let passField = field(for: "Password")
        let views = [nameField, sep1, emailField, sep2, passField]
        let inputView = inputDataContainer(with: views)

        view.addSubview(inputView)

        NSLayoutConstraint.activate([
            inputView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
            inputView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
            inputView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
            ])

    }

    func field(for s: String) -> UITextField {
        let f = UITextField()
        f.translatesAutoresizingMaskIntoConstraints = false
        f.placeholder = s
        f.borderStyle = .none
        return f
    }

    func fieldSep() -> UIView {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.heightAnchor.constraint(equalToConstant: 1.0).isActive = true
        v.backgroundColor = UIColor(r: 220, g: 220, b: 220)
        return v
    }

    func inputDataContainer(with subviews: [UIView]) -> UIView {

        let horizontalPadding: CGFloat = 8.0
        let verticalSpacing: CGFloat = 8.0

        let containerView = UIView()
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.backgroundColor = .white
        containerView.layer.cornerRadius = 5

        var previousView: UIView?

        for subview in subviews{
            containerView.addSubview(subview)

            // if it's a text field, we want padding on left and right
            if subview is UITextField {
                subview.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: horizontalPadding).isActive = true
                subview.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -horizontalPadding).isActive = true
            } else {
                subview.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 0.0).isActive = true
                subview.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0.0).isActive = true
            }

            if subview == subviews.first {
                // if it's the first subview, constrain to top of container
                subview.topAnchor.constraint(equalTo: containerView.topAnchor, constant: verticalSpacing).isActive = true
            } else {
                // unwrap previousView and constrain subview to bottom of previous subview
                if let pv = previousView {
                    subview.topAnchor.constraint(equalTo: pv.bottomAnchor, constant: verticalSpacing).isActive = true
                }
            }
            if subview == subviews.last {
                // if it's the last subview, constrain to bottom of container
                subview.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -verticalSpacing).isActive = true
            }

            // save reference to current subview
            previousView = subview

        }

        return containerView
    }

}

结果:

enter image description here