使用自定义 UIView class 和自动布局 returns 不正确的边界

Using custom UIView class with Auto Layout returns incorrect bounds

我正在展示一个简单的视图控制器。为了遵循 MVC,我将我的编程视图代码移到了一个单独的 UIView 子类中。我遵循了关于如何设置自定义视图的建议 here

这在 iPhone 中工作正常。但是,在 iPad 上,视图控制器会自动显示 new post-iOS 13 default modal style .pageSheet,这会导致我的其中一个按钮太宽。我查看了视图调试器,这是因为宽度约束设置为 self.bounds.width * 0.7self.bounds returns iPad(1024 点)的全宽设置了约束,而不是实际的最终视图(只有 704 点宽)。

iPhone simulator screenshot

iPad 12.9" simulator screenshot

三个问题:

  1. 一般来说,我是否正确设置了自定义视图 + 视图控制器?还是有我没有遵循的最佳实践?

  2. 如何强制更新约束,以便视图识别它在 iPad 上以 .pageSheet 模态模式呈现,并自动更新 self.bounds?我尝试了 self.view.setNeedsLayout() and self.view.layoutIfNeeded() 但它什么也没做。

  3. 为什么 Snooze 按钮正确放置在 iPad 上,而不是 Turn Off Alarm 按钮...Snooze 按钮依赖于固定到 [=21= 的约束] 和 self.trailingAnchor。为什么这些约束可以正确识别它们所呈现的模态视图,但 self.bounds.width 却不能?

代码:

Xcode 项目发布 here

使用自定义视图的视图控制器:

class CustomViewController: UIViewController {
    
    var customView: CustomView {
        return self.view as! CustomView
    }
    
    override func loadView() {
        let customView = CustomView(frame: UIScreen.main.bounds)
        self.view = customView  // Set view to our custom view
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        print("View's upon viewDidLoad is: \(self.view.bounds)") // <-- This gives incorrect bounds

        // Add actions for buttons
        customView.snoozeButton.addTarget(self, action: #selector(snoozeButtonPressed), for: .touchUpInside)
        customView.dismissButton.addTarget(self, action: #selector(dismissButtonPressed), for: .touchUpInside)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        print("View's bounds upon viewDidAppear is: \(self.view.bounds)") // <-- This gives correct bounds
        
        // This doesn't work
//      //self.view.setNeedsLayout()
//      // self.view.layoutIfNeeded()

    }

    
    @objc func snoozeButtonPressed() {
        // Do something here
    }
    
    @objc func dismissButtonPressed() {
        self.dismiss(animated: true, completion: nil)
    }

}

自定义视图代码:

class CustomView: UIView {
    
    public var snoozeButton: UIButton = {
        let button = UIButton(type: .system)
        button.isUserInteractionEnabled = true
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Snooze", for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
        button.setTitleColor(UIColor.white, for: .normal)
        button.layer.borderWidth = 1
        button.layer.borderColor = UIColor.white.cgColor
        button.layer.cornerRadius = 14
        return button
    }()
    
    public var dismissButton: UIButton = {
        let button = UIButton(type: .system)
        button.isUserInteractionEnabled = true
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("Turn off alarm", for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18)
        button.setTitleColor(UIColor.white, for: .normal)
        button.backgroundColor = UIColor.clear
        button.layer.cornerRadius = 14
        button.layer.borderWidth = 2
        button.layer.borderColor = UIColor.white.cgColor
        return button
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupUI()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupUI() {
        self.backgroundColor = .systemPurple
        
        // Add subviews
        self.addSubview(snoozeButton)
        self.addSubview(dismissButton)
    }
    
    func setupConstraints() {
        
        print("Self.bounds when setting up custom view constraints is: \(self.bounds)") // <-- This gives incorrect bounds 
        
        NSLayoutConstraint.activate([
        
            dismissButton.heightAnchor.constraint(equalToConstant: 60),
            dismissButton.widthAnchor.constraint(equalToConstant: self.bounds.width * 0.7),  // <-- This is the constraint that's wrong 
            dismissButton.centerXAnchor.constraint(equalTo: self.centerXAnchor),
            dismissButton.centerYAnchor.constraint(equalTo: self.centerYAnchor),
        
            snoozeButton.heightAnchor.constraint(equalToConstant: 60),
            snoozeButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20),
            snoozeButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20),
            snoozeButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -40)
    
        ])
            
    }
}

..最后,底层视图控制器进行呈现:

class BlankViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.backgroundColor = .white
    }
    
    override func viewDidAppear(_ animated: Bool) {
        let customVC = CustomViewController()
        self.present(customVC, animated: true, completion: nil)
    }
}

谢谢。

无需参考 bounds -- 只需使用相对宽度约束即可。

更改此行:

dismissButton.widthAnchor.constraint(equalToConstant: self.bounds.width * 0.7),  // <-- This gives incorrect bounds

对此:

dismissButton.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.7),

现在,按钮将是 视图 宽度的 70%。作为一个额外的好处,它会自行调整 if/when 视图宽度的变化,而不是停留在计算出的常量值上。