StackView 中的按钮约束(以 Swift 编程方式)
Posted
技术标签:
【中文标题】StackView 中的按钮约束(以 Swift 编程方式)【英文标题】:Button Constraints within a StackView (Swift Programmatically) 【发布时间】:2019-02-03 22:31:02 【问题描述】:我不熟悉以编程方式设置 StackViews 和 Buttons。我的约束出现了一些奇怪的行为,我无法弄清楚我做错了什么。感觉就像我错过了一些简单的东西。非常感谢任何帮助!
我正在尝试向 StackView 添加两个按钮以创建自定义标签栏。但是,当我将约束添加到按钮时,它们会显示在 StackView 底部之外。这就像地球图像的顶部约束不起作用。有任何想法吗?请参阅下面的图像和代码。
// View to put in the StackView
class ProfileBottomTabBarView: UIView
override init(frame: CGRect)
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = .blue
required init?(coder aDecoder: NSCoder)
fatalError("init(coder:) has not been implemented")
// Calculate the screen height
public var screenHeight: CGFloat
return UIScreen.main.bounds.height
// StackView height set to a proporation of screen height
let stackViewHeight = screenHeight * 0.07
// Views to put in the StackView
let profileIconView = ProfileBottomTabBarView()
let actIconView = ActBottomTabBarView()
let achieveIconView = AchieveBottomTabBarView()
let growIconView = GrowBottomTabBarView()
// Buttons to put in the Views
let profileButton = UIButton(type: .system)
let actButton = UIButton(type: .system)
let achieveButton = UIButton(type: .system)
let growButton = UIButton(type: .system)
let profileButtonText = UIButton(type: .system)
let actButtonText = UIButton(type: .system)
let achieveButtonText = UIButton(type: .system)
let growButtonText = UIButton(type: .system)
// 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
// Add StackView
view.addSubview(stackView)
stackView.bottomAnchor.constraint(equalTo: view.safeBottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeLeadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeTrailingAnchor).isActive = true
// Set height of the bottom tab bar as a proportion of the screen height.
stackView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true
profileIconView.topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
profileIconView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
profileIconView.heightAnchor.constraint(equalToConstant: stackViewHeight).isActive = true
// Add Buttons to the View
profileIconView.addSubview(profileButton)
profileIconView.addSubview(profileButtonText)
profileButton.translatesAutoresizingMaskIntoConstraints = false
profileButtonText.translatesAutoresizingMaskIntoConstraints = false
// Profile Button with Earth Image Setup
profileButton.setImage(UIImage(named: "earthIcon"), for: .normal)
profileButton.imageView?.contentMode = .scaleAspectFit
profileButton.topAnchor.constraint(equalTo: profileIconView.topAnchor).isActive = true
profileButton.bottomAnchor.constraint(equalTo: profileButtonText.topAnchor).isActive = true
profileButton.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor).isActive = true
//Set height of icon to a proportion of the stackview height
let profileButtonHeight = stackViewHeight * 0.8
profileButton.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, constant: profileButtonHeight).isActive = true
profileButton.widthAnchor.constraint(equalToConstant: profileButtonHeight).isActive = true
profileButton.imageView?.widthAnchor.constraint(equalToConstant: profileButtonHeight)
profileButton.imageView?.heightAnchor.constraint(equalToConstant: profileButtonHeight)
// Profile Text Button Setup
profileButtonText.setTitle("Profile", for: .normal)
profileButtonText.titleLabel?.font = UIFont.boldSystemFont(ofSize: 12)
profileButtonText.setTitleColor(.white, for: .normal)
profileButtonText.topAnchor.constraint(equalTo: profileButton.bottomAnchor).isActive = true
profileButtonText.bottomAnchor.constraint(equalTo: profileIconView.bottomAnchor).isActive = true
profileButtonText.centerXAnchor.constraint(equalTo: profileIconView.centerXAnchor).isActive = true
//Set height of icon to a proportion of the stackview height
let profileButtonTextHeight = stackViewHeight * 0.2
profileButton.heightAnchor.constraint(equalTo: profileIconView.heightAnchor, constant: profileButtonTextHeight).isActive = true
profileButtonText.widthAnchor.constraint(equalToConstant: 40).isActive = true
【问题讨论】:
离题但collectionview可能会做得更好 抱歉,我需要澄清一下...您希望地球符号在框中居中吗?连同文字?你到底想达到什么目的......你说它们在堆栈视图的底部......但我不明白......请澄清 当然。 “地球”和“配置文件”按钮都应该在框中。我尝试将 Earth 的顶部约束设置为框的顶部,将 Profile 按钮的底部设置为框的底部。 如果以后有人遇到这个问题,下面的答案是一种更简洁的方法。上面还缺少一对“.isActive = true” 【参考方案1】:您的约束有一些问题...
您正在计算高度/宽度并将它们用作常量,但这些值可能(几乎肯定会)根据视图生命周期而改变。
最好只使用相关的约束。例如:
// 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),
])
【讨论】:
使用底部标签栏视图可以自定义动画吗?这就是我一开始没有这样设置的原因。谢谢! 为了我自己的学习,设备屏幕尺寸会如何变化?我假设它始终是一个常数,因此可以以此为基础,加上图标的大小会根据设备大小动态变化。 1.取决于你想用“自定义动画”做什么......如果它们在按钮之间不一致,你可以继承公共部分并添加独特的东西,例如class ProfileIconView: BottomTabBarView ...
。 2. 如果视图高度有很大不同,使用屏幕高度可能无法满足您的需求 - 例如将此“标签栏”作为较小子视图的子视图。此外,您可能希望处理方向更改 - 当然,这适用于屏幕或视图,但使用视图可能更容易管理。
感谢您的想法!屏幕不会旋转。他们将是一致的。当用户点击特定按钮时,就像动画一样简单。可能隐藏文本或提高。类似的东西。在另一个项目中,我使用自动布局和 Tabview 沿着这条路走下去,但遇到了问题。那可能更多的是关于我,然后是什么可能。哈哈!以上是关于StackView 中的按钮约束(以 Swift 编程方式)的主要内容,如果未能解决你的问题,请参考以下文章
Swift 3 - 自定义 UIButton 半径删除约束?