You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift:如何通过代码创建含两个子StackView的水平StackView并设置布局比例

Swift水平StackView布局问题:左侧块消失与比例设置无效

问题背景

此前已掌握垂直标签栏的绘制方法,现尝试通过Swift代码创建一个包含两个子StackView的水平主StackView,但运行后遇到两个问题:

  1. 左侧包含多个标签的UI块无法显示
  2. 尝试设置左侧块占主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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:43:04