为啥我的 StackView 不工作?元素完全移位

Posted

技术标签:

【中文标题】为啥我的 StackView 不工作?元素完全移位【英文标题】:Why is my StackView not working? Elements are completely displaced为什么我的 StackView 不工作?元素完全移位 【发布时间】:2020-10-27 10:03:55 【问题描述】:

嘿,我的 StackView 什么也没做,有两个问题: 第一个是我转模拟器或者换设备的时候VC上的元素完全移位了,所以StackView没有做它应该做的! 第二件事是 StackView 覆盖了导航栏,我不知道如何使其可见。 有人可以帮我吗?

import UIKit


class RegisterViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate 
    
    let stackView = UIStackView()
    
    
    var profilePicture = UIButton()
    var profileIcon = UIImage()
    let usernameTextField = UITextField()
    let emailTextField = UITextField()
    let passswordTextField = UITextField()
    let signInButton = UIButton()
    
    
   
    

    override func viewDidLoad() 
        super.viewDidLoad()
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.title = "Create an Account"
        view.backgroundColor = .white
        
        
        
        
        
        // SetUp StackView:
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.distribution = .fillEqually
        stackView.spacing = 50
        view.addSubview(stackView)
       
        
        // SetUp Stack View Constraints:
       
        stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
        stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
               
        
        //Add Elements
        
        stackView.addArrangedSubview(profilePicture)
        stackView.addArrangedSubview(usernameTextField)
        stackView.addArrangedSubview(passswordTextField)
        stackView.addArrangedSubview(signInButton)
        
        
    
    
// MARK: - Set-Up View-Elements
    
    
    // SetUp ProfileIcon:
        profileIcon = UIImage(named: "characteer")!
        profilePicture.setImage(profileIcon, for: .normal)
        profilePicture.imageView?.contentMode = .scaleAspectFill
        let cornerRadius: CGFloat
         cornerRadius = 75 // half of widht/height
        profilePicture.layer.cornerRadius = cornerRadius
        profilePicture.layer.masksToBounds = true
        profilePicture.layer.borderWidth = 1
        profilePicture.layer.borderColor = UIColor.white.cgColor
        profilePicture.addTarget(self, action: #selector(handleSelectedPhoto), for: .touchUpInside)
        
        
        view.addSubview(profilePicture)
        
       profilePicture.translatesAutoresizingMaskIntoConstraints = false
       profilePicture.heightAnchor.constraint(equalToConstant: 150).isActive = true
       profilePicture.widthAnchor.constraint(equalToConstant: 150).isActive = true
       profilePicture.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        profilePicture.topAnchor.constraint(equalTo: view.topAnchor, constant: 110).isActive = true
        
     
        
        // SetUp UsernameTextfield:
    usernameTextField.backgroundColor = .white
    usernameTextField.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
    usernameTextField.textAlignment = NSTextAlignment.center
    usernameTextField.layer.cornerRadius = 8
    usernameTextField.layer.borderWidth = 1
    usernameTextField.layer.borderColor = UIColor.lightGray.cgColor
    self.view.addSubview(usernameTextField)
    let username = usernameTextField.text
    
    usernameTextField.translatesAutoresizingMaskIntoConstraints = false
    
    usernameTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
    usernameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
    usernameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
    usernameTextField.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    
        
    
// SetUpEmailTextfield:
    emailTextField.backgroundColor = .white
           emailTextField.attributedPlaceholder = NSAttributedString(string: "Email", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
           emailTextField.textAlignment = NSTextAlignment.center
           emailTextField.layer.cornerRadius = 8
           emailTextField.layer.borderWidth = 1
           emailTextField.layer.borderColor = UIColor.lightGray.cgColor
           self.view.addSubview(emailTextField)
           
           emailTextField.translatesAutoresizingMaskIntoConstraints = false
           emailTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
           emailTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
           emailTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
           emailTextField.topAnchor.constraint(equalTo: usernameTextField.bottomAnchor, constant: 20).isActive = true
        
        ```

【问题讨论】:

【参考方案1】:

我只是为 profilePicture 和 usernameTextField 设置,但对于其他人来说是一样的。您的代码的错误方面是关于约束,并且您添加了两个不同的视图对象。有解决办法。

    let stackView = UIStackView()
    
    
    var profilePicture = UIButton()
    var profileIcon = UIImage()
    let usernameTextField = UITextField()
    let emailTextField = UITextField()
    let passswordTextField = UITextField()
    let signInButton = UIButton()
    
    
   
    

    override func viewDidLoad() 
        super.viewDidLoad()
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.title = "Create an Account"
        view.backgroundColor = .white
        
        
        
        
        
        // SetUp StackView:
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.alignment = .center
        stackView.distribution = .fillEqually
        stackView.spacing = 50
        view.addSubview(stackView)
       
        
        // SetUp Stack View Constraints:
       
        stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
        stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
               
        
        //Add Elements
        
        stackView.addArrangedSubview(profilePicture)
        stackView.addArrangedSubview(usernameTextField)
        
        
    
    
// MARK: - Set-Up View-Elements
    
    
    // SetUp ProfileIcon:
       profileIcon = UIImage(named: "characteer")!
        profilePicture.setImage(profileIcon, for: .normal)
        profilePicture.imageView?.contentMode = .scaleAspectFill
        let cornerRadius: CGFloat
         cornerRadius = 75 // half of widht/height
        profilePicture.layer.cornerRadius = cornerRadius
        profilePicture.layer.masksToBounds = true
        profilePicture.layer.borderWidth = 1
        profilePicture.layer.borderColor = UIColor.white.cgColor
        //profilePicture.addTarget(self, action: #selector(handleSelectedPhoto), for: .touchUpInside)
        
        
       profilePicture.translatesAutoresizingMaskIntoConstraints = false
       profilePicture.heightAnchor.constraint(equalToConstant: 150).isActive = true
       profilePicture.widthAnchor.constraint(equalToConstant: 150).isActive = true
        
     
        
        // SetUp UsernameTextfield:
    usernameTextField.backgroundColor = .white
    usernameTextField.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
    usernameTextField.textAlignment = NSTextAlignment.center
    usernameTextField.layer.cornerRadius = 8
    usernameTextField.layer.borderWidth = 1
    usernameTextField.layer.borderColor = UIColor.lightGray.cgColor
    let username = usernameTextField.text
    
    usernameTextField.translatesAutoresizingMaskIntoConstraints = false
        usernameTextField.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width-40).isActive = true
    usernameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true

【讨论】:

【参考方案2】:

您做错了很多事情...阅读一些有关自动布局和使用 UIStackView 的教程非常值得。

首先,如果您将视图(图像视图、文本字段、标签、按钮等)添加到堆栈视图,不要也给这些视图位置约束.这就是堆栈视图正在做的事情。

其次,添加视图后:

stackView.addArrangedSubview(profilePicture)

不要然后像这样将其添加为子视图:

view.addSubview(profilePicture)

这样做将从堆栈视图中删除 profilePicture

查看您的代码——查看我进行更改的 cmets:

class RegisterViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate 
    
    let stackView = UIStackView()
    
    var profilePicture = UIButton()
    var profileIcon = UIImage()
    let usernameTextField = UITextField()
    let emailTextField = UITextField()
    let passswordTextField = UITextField()
    let signInButton = UIButton()
    
    override func viewDidLoad() 
        super.viewDidLoad()
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.title = "Create an Account"
        view.backgroundColor = .white
        
        
        // SetUp StackView:
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.alignment = .center
        
        // distribution should be .fill  NOT  .fillEqually
        stackView.distribution = .fill
        
        stackView.spacing = 50
        view.addSubview(stackView)
        
        // SetUp Stack View Constraints:
        
        stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
        stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        
        //Add Elements
        
        stackView.addArrangedSubview(profilePicture)
        stackView.addArrangedSubview(usernameTextField)
        stackView.addArrangedSubview(emailTextField)
        stackView.addArrangedSubview(passswordTextField)
        stackView.addArrangedSubview(signInButton)
        
        // MARK: - Set-Up View-Elements
        
        // SetUp ProfileIcon:
        //profileIcon = UIImage(named: "characteer")!
        profileIcon = UIImage(named: "pro1")!
        profilePicture.setImage(profileIcon, for: .normal)
        profilePicture.imageView?.contentMode = .scaleAspectFill
        let cornerRadius: CGFloat
        cornerRadius = 75 // half of widht/height
        profilePicture.layer.cornerRadius = cornerRadius
        profilePicture.layer.masksToBounds = true
        profilePicture.layer.borderWidth = 1
        profilePicture.layer.borderColor = UIColor.white.cgColor
        //profilePicture.addTarget(self, action: #selector(handleSelectedPhoto), for: .touchUpInside)
        
        // NO - it's already in the stack view
        //view.addSubview(profilePicture)
        
        // Set Only Width and Height - position is managed by the stack view
        profilePicture.heightAnchor.constraint(equalToConstant: 150).isActive = true
        profilePicture.widthAnchor.constraint(equalToConstant: 150).isActive = true
        
        // SetUp UsernameTextfield:
        usernameTextField.backgroundColor = .white
        usernameTextField.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
        usernameTextField.textAlignment = NSTextAlignment.center
        usernameTextField.layer.cornerRadius = 8
        usernameTextField.layer.borderWidth = 1
        usernameTextField.layer.borderColor = UIColor.lightGray.cgColor

        // NO - it's already in the stack view
        //  self.view.addSubview(usernameTextField)
        let username = usernameTextField.text
        
        // Set Only Width and Height - position is managed by the stack view
        usernameTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true
        usernameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
        
        // SetUpEmailTextfield:
        emailTextField.backgroundColor = .white
        emailTextField.attributedPlaceholder = NSAttributedString(string: "Email", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
        emailTextField.textAlignment = NSTextAlignment.center
        emailTextField.layer.cornerRadius = 8
        emailTextField.layer.borderWidth = 1
        emailTextField.layer.borderColor = UIColor.lightGray.cgColor

        // NO - it's already in the stack view
        //  self.view.addSubview(emailTextField)
        
        // Set Only Width and Height - position is managed by the stack view
        emailTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true
        emailTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
        
        // SetUp PasswordTextfield:
        passswordTextField.backgroundColor = .white
        passswordTextField.attributedPlaceholder = NSAttributedString(string: "Password", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
        passswordTextField.textAlignment = NSTextAlignment.center
        passswordTextField.layer.cornerRadius = 8
        passswordTextField.layer.borderWidth = 1
        passswordTextField.layer.borderColor = UIColor.lightGray.cgColor

        // NO - it's already in the stack view
        //  self.view.addSubview(emailTextField)
        
        // Set Only Width and Height - position is managed by the stack view
        passswordTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true
        passswordTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
        
        signInButton.setTitle("Sign In", for: [])
        signInButton.backgroundColor = .blue

        // Set Only Width and Height - position is managed by the stack view
        signInButton.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true
        signInButton.heightAnchor.constraint(equalToConstant: 50).isActive = true

    


当然,使用该布局 - 您在元素之间指定了 50 磅的垂直间距,并且为每个元素设置了明确的高度,您可能会发现它并不“完全适合”不同的设备/屏幕尺寸。

因此,正如您在上一个问题中向您指出的那样:Why is my VC displaced after changing the Simulator? AutoLayout - 您可能希望将堆栈视图的分布更改为等间距并添加底部约束:

    stackView.distribution = .equalSpacing
    stackView.spacing = 0

    stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true

这可能会或可能不会完全满足您的需求,但这是一个起点。

【讨论】:

【参考方案3】:

我认为你的约束有问题。

profilePicture.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true profilePicture.topAnchor.constraint(equalTo: view.topAnchor, constant: 110).isActive = true

试图在屏幕的中心,但它们在堆栈视图内。在这种情况下,您还必须为堆栈视图提供静态高度。

【讨论】:

以上是关于为啥我的 StackView 不工作?元素完全移位的主要内容,如果未能解决你的问题,请参考以下文章

如何禁用 BottomNavigationView 移位模式?

在 StackView 中按内容对齐两个元素

WPF)为啥完全相同的绑定在一个地方工作,而在另一个地方却不行?

水平stackview标签随着固定高度元素增长

StackView

Xcode Preview 和 Simulator 之间的 StackView 不同