StackView中的按钮约束(Swift编程)

StackView中的按钮约束(Swift编程),swift,uiview,uibutton,nslayoutconstraint,uistackview,Swift,Uiview,Uibutton,Nslayoutconstraint,Uistackview,我不熟悉以编程方式设置StackView和按钮。我有一些奇怪的行为和我的限制,我无法找出我做错了什么。我觉得我错过了一些简单的事情。非常感谢您的帮助 我正在尝试向StackView添加两个按钮,以创建自定义选项卡栏。但是,当我将约束添加到按钮时,它们会显示在StackView底部之外。好像地球图像的顶部约束不起作用。有什么想法吗?请参阅下面的图片和代码 您的约束有一些问题 您正在计算高度/宽度并将其用作常量,但这些值可能(几乎肯定会)根据视图生命周期发生变化 最好只使用相关的约束。例如:

我不熟悉以编程方式设置StackView和按钮。我有一些奇怪的行为和我的限制,我无法找出我做错了什么。我觉得我错过了一些简单的事情。非常感谢您的帮助

我正在尝试向StackView添加两个按钮,以创建自定义选项卡栏。但是,当我将约束添加到按钮时,它们会显示在StackView底部之外。好像地球图像的顶部约束不起作用。有什么想法吗?请参阅下面的图片和代码


您的约束有一些问题

您正在计算高度/宽度并将其用作常量,但这些值可能(几乎肯定会)根据视图生命周期发生变化

最好只使用相关的约束。例如:

        // constrain profile image button top, centerX and width relative to the iconView
        profileButton.topAnchor.constraint(equalTo: profileIconView.topAnchor),
        profileButton.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor),
        profileButton.widthAnchor.constraint(equalTo: profileIconView.widthAnchor, multiplier: 1.0),

        // constrain profile text button bottom, centerX and width relative to the iconView
        profileButtonText.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor),
        profileButtonText.widthAnchor.constraint(equalTo: profileIconView.widthAnchor, multiplier: 1.0),
        profileButtonText.bottomAnchor.constraint(equalTo: profileIconView.bottomAnchor),

        // constrain bottom of image button to top of text button (with a padding of 4-pts, change to suit)
        profileButton.bottomAnchor.constraint(equalTo: profileButtonText.topAnchor, constant: -4.0),

        // constrain height of text button to 20% of height of iconView
        profileButtonText.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, multiplier: 0.2),
为了让自己更轻松,我建议创建一个
BottomTabBarView
,用于添加和约束按钮:

class BottomTabBarView: UIView {

    var theImageButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.imageView?.contentMode = .scaleAspectFit
        return v
    }()

    var theTextButton: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
        v.setTitleColor(.white, for: .normal)
        return v
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    convenience init(withImageName imageName: String, labelText: String, bkgColor: UIColor) {

        self.init()
        self.commonInit()
        theImageButton.setImage(UIImage(named: imageName), for: .normal)
        theTextButton.setTitle(labelText, for: .normal)
        backgroundColor = bkgColor

    }

    func commonInit() -> Void {
        self.translatesAutoresizingMaskIntoConstraints = false

        addSubview(theImageButton)
        addSubview(theTextButton)

        NSLayoutConstraint.activate([

            // constrain profile image button top, centerX and width of the iconView
            theImageButton.topAnchor.constraint(equalTo: topAnchor),
            theImageButton.centerXAnchor.constraint(equalTo: centerXAnchor),
            theImageButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0),

            // constrain profile text button bottom, centerX and width of the iconView
            theTextButton.centerXAnchor.constraint(equalTo: centerXAnchor),
            theTextButton.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0),
            theTextButton.bottomAnchor.constraint(equalTo: bottomAnchor),

            // constrain bottom of image button to top of text button
            theImageButton.bottomAnchor.constraint(equalTo: theTextButton.topAnchor, constant: -4.0),

            // set text button height to 20% of view height (instead of using intrinsic height)
            theTextButton.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.2),

            ])

    }

}
现在,您可以使用一条线创建每个视图,如中所示:

    profileIconView = BottomTabBarView(withImageName: "earthIcon", labelText: "Profile", bkgColor: .blue)
并且视图控制器类变得更简单/更干净:

class BenViewController: UIViewController {

    // Views to put in the StackView
    var profileIconView = BottomTabBarView()
    var actIconView = BottomTabBarView()
    var achieveIconView = BottomTabBarView()
    var growIconView = BottomTabBarView()

    // Stackview setup
    lazy var stackView: UIStackView = {
        let stackV = UIStackView(arrangedSubviews: [profileIconView, actIconView, achieveIconView, growIconView])

        stackV.translatesAutoresizingMaskIntoConstraints = false
        stackV.axis = .horizontal
        stackV.spacing = 20
        stackV.distribution = .fillEqually

        return stackV
    }()


    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .black

        profileIconView = BottomTabBarView(withImageName: "earthIcon", labelText: "Profile", bkgColor: .blue)
        actIconView = BottomTabBarView(withImageName: "actIcon", labelText: "Action", bkgColor: .brown)
        achieveIconView = BottomTabBarView(withImageName: "achieveIcon", labelText: "Achieve", bkgColor: .red)
        growIconView = BottomTabBarView(withImageName: "growIcon", labelText: "Grow", bkgColor: .purple)

        // Add StackView
        view.addSubview(stackView)

        NSLayoutConstraint.activate([

            // constrain stackView to bottom, leading and trailing (to safeArea)
            stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),

            // Set height of the stackView (the bottom tab bar) as a proportion of the view height (7%).
            stackView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.07),

            ])

    }
}

主题外,但collectionview可能会更好。对不起,但我需要澄清……你想让地球符号在方框中居中吗?和文本一起?你到底想实现什么…你说它们在堆栈底部视图之外。。。但我不明白。。。请澄清一下。“接地”和“配置文件”按钮都应位于框中。我试图将地球的顶部约束设置为长方体的顶部,将“轮廓”按钮的底部设置为长方体的底部。如果稍后有人遇到此问题,下面的答案是一种更简洁的方法。上面还缺少一对“.isActive=true”和底部选项卡栏视图。您可以自定义动画吗?这就是我当初没有这样设置的原因。谢谢为了我自己的学习,设备的屏幕大小会发生怎样的变化?我假设这始终是一个常数,因此可以在此基础上再加上图标的大小将根据设备大小动态变化。1。取决于你想用“自定义动画”做什么。。。如果按钮之间不一致,您可以对公共部分进行子类化,并添加唯一的内容,例如
class ProfileIconView:BottomTabBarView{…}
。2.如果视图高度相差很大,使用屏幕高度可能无法满足您的需要,例如将此“选项卡栏”作为较小子视图的子视图。此外,您可能希望处理方向更改-当然,这适用于屏幕或视图,但使用视图可能更容易管理。谢谢您的想法!屏幕将不会旋转。它们将是一致的。当用户点击一个特定的按钮时,它会像动画一样简单。可能会隐藏文本或引发错误。差不多吧。在另一个项目中,我使用autolayout和Tabview沿着这条道路前进,遇到了一些问题。这可能更多的是关于我的,但这是可能的。哈哈!
class BenViewController: UIViewController {

    // Views to put in the StackView
    var profileIconView = BottomTabBarView()
    var actIconView = BottomTabBarView()
    var achieveIconView = BottomTabBarView()
    var growIconView = BottomTabBarView()

    // Stackview setup
    lazy var stackView: UIStackView = {
        let stackV = UIStackView(arrangedSubviews: [profileIconView, actIconView, achieveIconView, growIconView])

        stackV.translatesAutoresizingMaskIntoConstraints = false
        stackV.axis = .horizontal
        stackV.spacing = 20
        stackV.distribution = .fillEqually

        return stackV
    }()


    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .black

        profileIconView = BottomTabBarView(withImageName: "earthIcon", labelText: "Profile", bkgColor: .blue)
        actIconView = BottomTabBarView(withImageName: "actIcon", labelText: "Action", bkgColor: .brown)
        achieveIconView = BottomTabBarView(withImageName: "achieveIcon", labelText: "Achieve", bkgColor: .red)
        growIconView = BottomTabBarView(withImageName: "growIcon", labelText: "Grow", bkgColor: .purple)

        // Add StackView
        view.addSubview(stackView)

        NSLayoutConstraint.activate([

            // constrain stackView to bottom, leading and trailing (to safeArea)
            stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),

            // Set height of the stackView (the bottom tab bar) as a proportion of the view height (7%).
            stackView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.07),

            ])

    }
}