由于Xcode 8和iOS10,视图在viewDidLayoutSubviews上的大小不正确

看来,使用Xcode 8,在viewDidLoad ,所有viewcontroller子视图都具有1000×1000的相同大小。 奇怪的事情,但没关系, viewDidLoad从来没有比正确的大小的意见更好的地方。

但是viewDidLayoutSubviews是!

而在我目前的项目中,我尝试打印一个button的大小:

 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; NSLog(@"%@", self.myButton); } 

日志显示myButton的大小(1000×1000)! 然后,如果我loginbutton点击,例如,日志显示正常的大小。

我正在使用自动布局。

这是一个错误?

现在,Interface Builder允许用户dynamic更改故事板中每个视图控制器的大小,以模拟某个设备的大小。

在此function之前,用户应手动设置每个视图控制器的大小。 所以视图控制器被保存了一定的大小,在initWithCoder中被用来设置初始帧。

现在,似乎initWithCoder不使用storyboard中定义的大小,并为viewcontroller视图及其所有子视图定义一个1000×1000 px大小。

这不是问题,因为视图应该总是使用以下任一种布局解决scheme:

  • 自动布局,所有的约束将正确布局你的意见

  • autoresizingMask,它将布置没有任何约束的每个视图( 注意自动布局和边界约束现在兼容在同一个视图\ o /!

但是对于所有与视图图层相关的布局,像cornerRadius ,这一个问题,因为自动布局和自动调整遮罩都不适用于图层属性。

要回答这个问题,常见的方法是使用viewDidLayoutSubviews如果您在控制器中)或layoutSubview如果您在视图中)。 在这一点上(不要忘记调用他们的super相关方法),你很确定所有的布局已经完成了!

确定? 嗯…不完全,我已经说过了,这就是为什么我问这个问题,在某些情况下,这个方法的视图仍然有1000×1000的大小。 我觉得我自己的问题没有答案。 为了给出最大的信息:

1-只在布置细胞时才会发生! 在UITableViewCellUICollectionViewCell子类中, UICollectionViewCell子视图子视图被正确UICollectionViewCell 不会被调用。

2-正如@EugenDimboiu所说(如果对你有用,请提高他的答案),在未铺设的子视图上调用[myView layoutIfNeeded]将正确地布置它。

 - (void)layoutSubviews { [super layoutSubviews]; NSLog (self.myLabel); // 1000x1000 size [self.myLabel layoutIfNeeded]; NSLog (self.myLabel); // normal size } 

3-我认为,这绝对是一个错误。 我已经提交给雷达(编号28562874)。

PS:我不是英文原文,所以如果我的语法应该更正,请随时编辑我的文章;)

PS2:如果您有更好的解决scheme,请随时不写另一个答案。 我会移动接受的答案。

你是否在使用圆angular作为button? 尝试之前调用layoutIfNeeded()

我知道这不是你确切的问题,但我遇到了一个类似的问题,因为尽pipe在viewDidLayoutSubviews中有正确的帧大小,我的一些意见被搞砸了。 根据iOS 10发行说明:

“将layoutIfNeeded发送到视图不会移动视图,但在早期发行版中,如果视图将translatesAutoresizingMaskIntoConstraints设置为NO,并且如果按约束进行定位,则layoutIfNeeded将在发送布局之前移动视图以匹配布局引擎到子树,这些改变纠正了这种行为,接收者的位置和大小通常不会受到layoutIfNeeded的影响。

一些现有的代码可能依赖于现在纠正的这种不正确的行为。 对于在iOS 10之前链接的二进制文件没有任何行为改变,但是在iOS 10上构build时,可能需要通过将-layoutIfNeeded发送到作为前一个接收者的translatesAutoresizingMaskIntoConstraints视图的超级视图来纠正某些情况,或者在之前对其进行定位和resize或之后,取决于你想要的行为)layoutIfNeeded。

使用自动布局的自定义UIView子类的第三方应用程序在调用super之前会重写layoutSubviews和自身的布局,当它们在iOS 10上重build时,将触发布局反馈循环。当它们被正确发送后,layoutSubviews调用它们时必须确保在某些时候停止对自己的布局(请注意,这个调用在iOS 10之前的版本中被忽略)。

实际上,如果您正在使用translatesAutoresizingMaskIntoConstraints – 现在调用layoutIfNeeded必须位于superView上,并且仍然可以在viewDidLayoutSubviews中调用此方法,则不能在视图的子对象上调用layoutIfNeeded。

解决scheme:将所有内容都包含在DispatchQueue.main.async viewDidLayoutSubviews中。

 // swift 3 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() DispatchQueue.main.async { // do stuff here } } 

如果这些框架在layoutSubViews中不正确,那么你可以在主线程上调用一些代码。 这给系统一些时间做布局。 当您发送的块被执行时,帧具有适当的大小。

这固定了(可笑的讨厌)的问题对我来说:

 - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.view.frame = CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height); } 

编辑/注意:这是一个全屏的ViewController。

其实viewDidLayoutSubviews也不是设置你的视图的框架的最佳场所。 据我所知,从现在起,唯一应该做的就是在实际视图的代码中使用layoutSubviews方法。 我希望我是不对的,有人纠正我,如果这不是真的!

我已经向苹果报告了这个问题,这个问题存在了很长时间,当你从Xib初始化UIViewController,但我发现相当不错的解决方法。 除此之外,我发现在某些情况下,当在初始时刻没有设置datasource时,在UICollectionView和UITableView上的layoutIfNeeded时需要调用它。

 extension UIViewController { open override class func initialize() { if self !== UIViewController.self { return } DispatchQueue.once(token: "io.inspace.uiviewcontroller.swizzle") { ins_applyFixToViewFrameWhenLoadingFromNib() } } @objc func ins_setView(view: UIView!) { // View is loaded from xib file if nibBundle != nil && storyboard == nil && !view.frame.equalTo(UIScreen.main.bounds) { view.frame = UIScreen.main.bounds view.layoutIfNeeded() } ins_setView(view: view) } private class func ins_applyFixToViewFrameWhenLoadingFromNib() { UIViewController.swizzle(originalSelector: #selector(setter: UIViewController.view), with: #selector(UIViewController.ins_setView(view:))) UICollectionView.swizzle(originalSelector: #selector(UICollectionView.layoutSubviews), with: #selector(UICollectionView.ins_layoutSubviews)) UITableView.swizzle(originalSelector: #selector(UITableView.layoutSubviews), with: #selector(UITableView.ins_layoutSubviews)) } } extension UITableView { @objc fileprivate func ins_layoutSubviews() { if dataSource == nil { super.layoutSubviews() } else { ins_layoutSubviews() } } } extension UICollectionView { @objc fileprivate func ins_layoutSubviews() { if dataSource == nil { super.layoutSubviews() } else { ins_layoutSubviews() } } } 

派遣一次延期:

 extension DispatchQueue { private static var _onceTracker = [String]() /** Executes a block of code, associated with a unique token, only once. The code is thread safe and will only execute the code once even in the presence of multithreaded calls. - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID - parameter block: Block to execute once */ public class func once(token: String, block: (Void) -> Void) { objc_sync_enter(self); defer { objc_sync_exit(self) } if _onceTracker.contains(token) { return } _onceTracker.append(token) block() } } 

Swizzle扩展:

 extension NSObject { @discardableResult class func swizzle(originalSelector: Selector, with selector: Selector) -> Bool { var originalMethod: Method? var swizzledMethod: Method? originalMethod = class_getInstanceMethod(self, originalSelector) swizzledMethod = class_getInstanceMethod(self, selector) if originalMethod != nil && swizzledMethod != nil { method_exchangeImplementations(originalMethod!, swizzledMethod!) return true } return false } } 

我的问题是通过改变用法解决的

 -(void)viewDidLayoutSubviews{ [super viewDidLayoutSubviews]; self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height; } 

 -(void)viewWillLayoutSubviews{ [super viewWillLayoutSubviews]; self.viewLoginMailTop.constant = -self.viewLoginMail.bounds.size.height; } 

所以,从做到意志

超级怪异

对我更好的解决scheme。

 protocol LayoutComplementProtocol { func didLayoutSubviews(with targetView_: UIView) } private class LayoutCaptureView: UIView { var targetView: UIView! var layoutComplements: [LayoutComplementProtocol] = [] override func layoutSubviews() { super.layoutSubviews() for layoutComplement in self.layoutComplements { layoutComplement.didLayoutSubviews(with: self.targetView) } } } extension UIView { func add(layoutComplement layoutComplement_: LayoutComplementProtocol) { func findLayoutCapture() -> LayoutCaptureView { for subView in self.subviews { if subView is LayoutCaptureView { return subView as? LayoutCaptureView } } let layoutCapture = LayoutCaptureView(frame: CGRect(x: -100, y: -100, width: 10, height: 10)) // not want to show, want to have size layoutCapture.targetView = self self.addSubview(layoutCapture) return layoutCapture } let layoutCapture = findLayoutCapture() layoutCapture.layoutComplements.append(layoutComplement_) } } 

运用

 class CircleShapeComplement: LayoutComplementProtocol { func didLayoutSubviews(with targetView_: UIView) { targetView_.layer.cornerRadius = targetView_.frame.size.height / 2 } } myButton.add(layoutComplement: CircleShapeComplement()) 

在单元格子视图中覆盖layoutSublayers(layer:CALayer)而不是layoutSubviews,以获得正确的框架

根据ios新的更新,这实际上是一个错误,但我们可以通过使用 –

如果你在你的项目中使用自动布局的xib,那么你必须在自动布局设置中更新帧 ,请为此find图像。 在这里输入图像说明