Swift:如何通过代码创建含两个子StackView的水平StackView并设置布局比例
Swift水平StackView布局问题:左侧块消失与比例设置无效
问题背景
此前已掌握垂直标签栏的绘制方法,现尝试通过Swift代码创建一个包含两个子StackView的水平主StackView,但运行后遇到两个问题:
- 左侧包含多个标签的UI块无法显示
- 尝试设置左侧块占主StackView的10%、右侧图片区域占90%的布局比例,但代码未生效
用户原代码:
class ViewController: UIViewController { @IBOutlet weak var mainContainer: UIView! let colorDictionary = [ "Red":UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0), "Green":UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0), "Blue":UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0), // "Green2":UIColor(red: 1.0, green: 0.7, blue: 0.0, alpha: 1.0), ] //MARK: Instance methods func colorButton(withColor color:UIColor, title:String) -> UILabel{ let newButton = UILabel() newButton.backgroundColor = .gray newButton.text = title newButton.textAlignment = .center newButton.textColor = UIColor.white return newButton } func displayKeyboard(){ var buttonArray = [UILabel]() for (myKey,myValue) in colorDictionary{ buttonArray += [colorButton(withColor: myValue, title: myKey)] } let horizontalStack = UIStackView(arrangedSubviews: buttonArray) horizontalStack.axis = .horizontal horizontalStack.distribution = .fillEqually horizontalStack.alignment = .fill horizontalStack.translatesAutoresizingMaskIntoConstraints = false let label2 = UILabel() label2.text = "Label" label2.backgroundColor = .red label2.textColor = .white label2.textAlignment = .center label2.lineBreakMode = .byCharWrapping label2.numberOfLines = 0 label2.translatesAutoresizingMaskIntoConstraints = false let leftStack = UIStackView() leftStack.backgroundColor = .blue leftStack.axis = .vertical leftStack.distribution = .equalSpacing leftStack.translatesAutoresizingMaskIntoConstraints = false leftStack.addArrangedSubview(label2) leftStack.addArrangedSubview(horizontalStack) leftStack.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2) let bottomStackView = UIStackView(arrangedSubviews: buttonArray) bottomStackView.axis = .horizontal bottomStackView.distribution = .fillEqually bottomStackView.alignment = .fill let stackView = UIStackView() stackView.axis = .vertical stackView.translatesAutoresizingMaskIntoConstraints = false let url = URL(string: "https://picsum.photos/270")! let image = UIImageView() let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) { data, _, error in if let data = data { DispatchQueue.main.async { image.image = UIImage(data: data) image.contentMode = .scaleToFill image.translatesAutoresizingMaskIntoConstraints = false } } }.resume() stackView.addArrangedSubview(image) stackView.addArrangedSubview(bottomStackView) let mainStackView = UIStackView() mainStackView.axis = .horizontal mainStackView.addArrangedSubview(leftStack) mainStackView.addArrangedSubview(stackView) mainContainer.addSubview(mainStackView) mainStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ mainStackView.topAnchor.constraint(equalTo: mainContainer.topAnchor, constant: 5), mainStackView.leftAnchor.constraint(equalTo: mainContainer.leftAnchor), mainStackView.rightAnchor.constraint(equalTo: mainContainer.rightAnchor), mainStackView.heightAnchor.constraint(equalToConstant: 270), leftStack.widthAnchor.constraint(equalToConstant: 270), ]) } override func viewDidLoad() { super.viewDidLoad() displayKeyboard() } }
解决方案
1. 修复左侧UI块消失问题
旋转视图会交换其宽高属性,且子视图缺少必要约束,按以下方式修改:
- 移除
leftStack的固定宽度约束,改为通过比例控制 - 给
label2和标签按钮添加固定高度约束,确保旋转后能显示 - 调整
leftStack的旋转锚点,避免视图偏移 - 给
horizontalStack添加高度约束,匹配旋转后leftStack的可用空间
2. 修复布局比例设置问题
主StackView为水平方向,需通过宽度比例分配空间:
- 给
leftStack设置宽度为mainStackView宽度的10% - 给
stackView设置宽度为mainStackView宽度的90% - 确保
leftStack的高度匹配主StackView的高度,避免旋转后内容被截断
修改后的完整代码:
class ViewController: UIViewController { @IBOutlet weak var mainContainer: UIView! let colorDictionary = [ "Red":UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0), "Green":UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0), "Blue":UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0), // "Green2":UIColor(red: 1.0, green: 0.7, blue: 0.0, alpha: 1.0), ] //MARK: Instance methods func colorButton(withColor color:UIColor, title:String) -> UILabel{ let newButton = UILabel() newButton.backgroundColor = .gray newButton.text = title newButton.textAlignment = .center newButton.textColor = UIColor.white // 给标签添加固定高度,确保显示 newButton.heightAnchor.constraint(equalToConstant: 40).isActive = true return newButton } func displayKeyboard(){ var buttonArray = [UILabel]() for (myKey,myValue) in colorDictionary{ buttonArray += [colorButton(withColor: myValue, title: myKey)] } let horizontalStack = UIStackView(arrangedSubviews: buttonArray) horizontalStack.axis = .horizontal horizontalStack.distribution = .fillEqually horizontalStack.alignment = .fill horizontalStack.translatesAutoresizingMaskIntoConstraints = false let label2 = UILabel() label2.text = "Label" label2.backgroundColor = .red label2.textColor = .white label2.textAlignment = .center label2.lineBreakMode = .byCharWrapping label2.numberOfLines = 0 label2.translatesAutoresizingMaskIntoConstraints = false // 给label2添加固定高度 label2.heightAnchor.constraint(equalToConstant: 40).isActive = true let leftStack = UIStackView() leftStack.backgroundColor = .blue leftStack.axis = .vertical leftStack.distribution = .equalSpacing leftStack.translatesAutoresizingMaskIntoConstraints = false leftStack.addArrangedSubview(label2) leftStack.addArrangedSubview(horizontalStack) // 旋转视图前设置锚点,避免偏移 leftStack.layer.anchorPoint = CGPoint(x: 0.5, y: 0.5) leftStack.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2) // 给horizontalStack添加高度约束,匹配leftStack的宽度(旋转后对应高度) horizontalStack.heightAnchor.constraint(equalTo: leftStack.widthAnchor).isActive = true let bottomStackView = UIStackView(arrangedSubviews: buttonArray) bottomStackView.axis = .horizontal bottomStackView.distribution = .fillEqually bottomStackView.alignment = .fill bottomStackView.translatesAutoresizingMaskIntoConstraints = false // 给底部StackView添加固定高度 bottomStackView.heightAnchor.constraint(equalToConstant: 40).isActive = true let stackView = UIStackView() stackView.axis = .vertical stackView.translatesAutoresizingMaskIntoConstraints = false let url = URL(string: "https://picsum.photos/270")! let image = UIImageView() image.contentMode = .scaleToFill image.translatesAutoresizingMaskIntoConstraints = false let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) { data, _, error in if let data = data { DispatchQueue.main.async { image.image = UIImage(data: data) } } }.resume() stackView.addArrangedSubview(image) stackView.addArrangedSubview(bottomStackView) let mainStackView = UIStackView() mainStackView.axis = .horizontal mainStackView.translatesAutoresizingMaskIntoConstraints = false mainStackView.addArrangedSubview(leftStack) mainStackView.addArrangedSubview(stackView) mainContainer.addSubview(mainStackView) NSLayoutConstraint.activate([ mainStackView.topAnchor.constraint(equalTo: mainContainer.topAnchor, constant: 5), mainStackView.leftAnchor.constraint(equalTo: mainContainer.leftAnchor), mainStackView.rightAnchor.constraint(equalTo: mainContainer.rightAnchor), mainStackView.heightAnchor.constraint(equalToConstant: 270), // 设置比例:左侧占10%,右侧占90% leftStack.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, multiplier: 0.1).isActive = true, stackView.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, multiplier: 0.9).isActive = true, // 确保leftStack的高度匹配主StackView的高度 leftStack.heightAnchor.constraint(equalTo: mainStackView.heightAnchor).isActive = true ]) } override func viewDidLoad() { super.viewDidLoad() displayKeyboard() } }
关键修改说明
- 给所有标签和底部StackView添加固定高度,避免内容被挤压
- 调整
leftStack的旋转锚点,防止旋转后视图偏移 - 通过
mainStackView.widthAnchor的multiplier设置左右两侧的宽度比例 - 给
leftStack添加高度约束,匹配主StackView的高度,确保旋转后内容正确显示
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

