由于Xcode 8和iOS10,ViewDidLayoutSubView上的视图大小不正确
似乎对于Xcode 8,在由于Xcode 8和iOS10,ViewDidLayoutSubView上的视图大小不正确,ios,autolayout,ios10,xcode8,Ios,Autolayout,Ios10,Xcode8,似乎对于Xcode 8,在viewDidLoad上,所有viewcontroller子视图的大小都是1000x1000。奇怪的是,viewDidLoad从来都不是正确调整视图大小的好地方 但是viewdilayoutsubviews是 在我当前的项目中,我尝试打印按钮的大小: - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; NSLog(@"%@", self.myButton); } 日志显示myB
viewDidLoad
上,所有viewcontroller子视图的大小都是1000x1000。奇怪的是,viewDidLoad
从来都不是正确调整视图大小的好地方
但是viewdilayoutsubviews
是
在我当前的项目中,我尝试打印按钮的大小:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"%@", self.myButton);
}
日志显示myButton的大小为(1000x1000)!然后,如果我登录一个按钮,例如,单击,日志显示正常大小
我正在使用自动布局
这是一个bug吗?实际上
viewdilayoutsubviews
也不是设置视图框架的最佳位置。据我所知,从现在起,唯一应该做的是在实际视图的代码中使用layoutSubviews
方法。我希望我是对的,如果这不是真的,请有人纠正我 您的按钮是否使用圆角?
请尝试在之前调用
layoutifneed()
我知道这不是你的确切问题,但我遇到了一个类似的问题,在更新时,尽管ViewDidLayoutSubView中的帧大小正确,但我的一些视图还是被弄乱了。根据iOS 10发行说明:
“发送视图所需的布局不会移动视图,
但在早期版本中,如果视图
TranslatesAutoResizengMaskinToConstraints设置为否,如果为
通过约束定位,layoutIfNeeded会将视图移动到
在将布局发送到子树之前匹配布局引擎。这些
更改会纠正此行为,并且通常会纠正接收器的位置和位置
其大小不受所需布局的影响
某些现有代码可能依赖于以下错误行为:
现在更正。以前链接的二进制文件没有行为更改
iOS 10,但在iOS 10上构建时,可能需要更正一些错误
通过发送-layoutIfNeeded到
translatesAutoresizingMaskIntoConstraints视图是上一个
接收器,或在之前(或之后)对其进行定位和调整大小,
根据您想要的行为)需要进行布局
具有自定义UIView子类的第三方应用程序使用自动布局
在调用super之前覆盖self上的LayoutSubView和脏布局
在上重建时有触发布局反馈循环的风险
iOS 10。正确发送后,将调用layoutSubviews
他们必须确保在某个时候停止弄脏自己的布局(注意
在iOS 10之前的版本中跳过了此调用。”
如果您正在使用TranslatesAutoResizengMaskintoConstraints,则基本上无法在视图的子对象上调用LayoutFeeded-现在调用LayoutFeeded必须在superView上,并且您仍然可以在ViewDidLayoutSubView中调用它。这修复了我的(可笑的恼人)问题:
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.view.frame = CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height);
}
编辑/注意:这适用于全屏ViewController。现在,Interface Builder允许用户动态更改故事板中每个视图控制器的大小,以模拟特定设备的大小 在此功能之前,用户应手动设置每个视图控制器的大小。因此,视图控制器以一定的大小保存,在
initWithCoder
中用于设置初始帧
现在,似乎initWithCoder
没有使用故事板中定义的大小,而是为viewcontroller视图及其所有子视图定义1000x1000 px大小
这不是问题,因为视图应始终使用以下任一布局解决方案:
- 自动布局,所有约束将正确布局视图
- autoresizingMask,它将布局每个未附加任何约束的视图(注意autolayout和边距约束现在在同一视图中兼容\o/!)
拐角半径
,因为自动布局和自动调整大小遮罩都不适用于图层特性
要回答此问题,常用的方法是,如果您在控制器中,则使用viewdilayoutsubview
;如果您在视图中,则使用layoutSubview
。在这一点上(别忘了调用他们的super
relative方法),你很确定所有的布局工作都已经完成了
相当确定吗?哼不完全是这样,我已经说过了,这就是为什么我问这个问题,在某些情况下,视图在这个方法上仍然有它的1000x1000大小。我想我自己的问题没有答案。要提供有关它的最大信息,请执行以下操作:
1-只有在布置单元时才会发生这种情况!在UITableViewCell
和UICollectionViewCell
子类中,子视图正确布局后,将不会调用layoutSubview
2-正如@EugenDimboiu所说(如果对您有用,请更新他的答案),在未布局的子视图上调用[myView layoutifneed]
将及时正确布局它
- (void)layoutSubviews {
[super layoutSubviews];
NSLog (self.myLabel); // 1000x1000 size
[self.myLabel layoutIfNeeded];
NSLog (self.myLabel); // normal size
}
3-在我看来,这绝对是一个错误。我已经将它提交给雷达(id 28562874)
注:我不是英语母语,所以如果我的语法需要更正,请随意编辑我的帖子;)
PS2:如果你有更好的解决方案,请不要再写其他答案。我将移动已接受的答案。如果LayoutSubView中的帧不正确(事实并非如此),您可以在主线程上调度一点异步代码。这给了系统一些时间来进行布局。当您分派的块被执行时,帧具有适当的大小。我已经向苹果公司报告了这个问题,这个问题已经存在很长时间了,当您从Xib初始化UIViewController时,但我发现了非常好的解决方法。除此之外,我发现在某些情况下,当数据源未在初始时刻设置时,UICollectionView和UITableView上需要LayoutFneed时会出现这个问题,并且
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()
}
}
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;
}
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())
// swift 3
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
// do stuff here
}
}
override func layoutSubviews() {
super.layoutSubviews()
yourView.layoutIfNeeded()
setGradientForYourView()
}