使用自定义 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.7
,self.bounds
returns iPad(1024 点)的全宽设置了约束,而不是实际的最终视图(只有 704 点宽)。
iPhone simulator screenshot
iPad 12.9" simulator screenshot
三个问题:
一般来说,我是否正确设置了自定义视图 + 视图控制器?还是有我没有遵循的最佳实践?
如何强制更新约束,以便视图识别它在 iPad 上以 .pageSheet
模态模式呈现,并自动更新 self.bounds
?我尝试了 self.view.setNeedsLayout() and self.view.layoutIfNeeded()
但它什么也没做。
为什么 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 视图宽度的变化,而不是停留在计算出的常量值上。
我正在展示一个简单的视图控制器。为了遵循 MVC,我将我的编程视图代码移到了一个单独的 UIView 子类中。我遵循了关于如何设置自定义视图的建议 here。
这在 iPhone 中工作正常。但是,在 iPad 上,视图控制器会自动显示 new post-iOS 13 default modal style .pageSheet
,这会导致我的其中一个按钮太宽。我查看了视图调试器,这是因为宽度约束设置为 self.bounds.width * 0.7
,self.bounds
returns iPad(1024 点)的全宽设置了约束,而不是实际的最终视图(只有 704 点宽)。
iPhone simulator screenshot
iPad 12.9" simulator screenshot
三个问题:
一般来说,我是否正确设置了自定义视图 + 视图控制器?还是有我没有遵循的最佳实践?
如何强制更新约束,以便视图识别它在 iPad 上以
.pageSheet
模态模式呈现,并自动更新self.bounds
?我尝试了self.view.setNeedsLayout() and self.view.layoutIfNeeded()
但它什么也没做。为什么 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 视图宽度的变化,而不是停留在计算出的常量值上。