Начиная с Xcode 8 и iOS10, представления не имеют правильного размера в viewDidLayoutSubviews
кажется, что с Xcode 8, О viewDidLoad, все подвиды viewcontroller имеют одинаковый размер 1000x1000. Странно, но ладно,viewDidLoad никогда не было лучшим местом для правильного размера представлений.
но viewDidLayoutSubviews - это!
и на моем текущем проекте, я пытаюсь напечатать размер кнопки:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"%@", self.myButton);
}
журнал показывает размер (1000x1000) для myButton! Затем, если я войду в систему нажатием кнопки, например, журнал показывает нормальный размер.
Я использую автозапуск.
это баг?
12 ответов:
теперь Interface Builder позволяет пользователю динамически изменять размер каждого контроллера вида в раскадровке, чтобы имитировать размер определенного устройства.
перед этой функцией пользователь должен вручную установить размер каждого контроллера вида. Таким образом, контроллер вида был сохранен с определенным размером, который использовался в
initWithCoderдля установки начального кадра.теперь, кажется, что
initWithCoderНе используйте размер, определенный в раскадровке, и определите размер 1000x1000 px для viewcontroller view & все его подвиды.это не проблема, потому что представления всегда должны использовать любое из этих решений компоновки:
autolayout, и все ограничения будут макет правильно ваши представления
autoresizingMask, который будет макет каждого представления, которое не имеет каких-либо ограничений, связанных с (Примечание ограничения autolayout и margin теперь совместимы в одном представлении \o/ !)
эта и проблема для всех материалов макета, связанных со слоем представления, например
cornerRadius, так как ни один autolayout, ни функциями автоматического изменения маски применяется к свойствам слоя.чтобы ответить на эту проблему, общий способ-использовать
viewDidLayoutSubviewsесли вы находитесь в контроллер, илиlayoutSubviewесли вы находитесь в представлении. В этот момент (не забудьте назвать ихsuperотносительные методы), вы уверены что все макеты вещи были сделаны!довольно уверен? Гудеть... не полностью, я заметил, и именно поэтому я задал этот вопрос, в некоторых случаях представление все еще имеет свой размер 1000x1000 на этом методе. Я думаю, нет ответа на мой вопрос. Чтобы дать максимальную информацию об этом:
1 - это происходит только при раскладывании клеток! В
UITableViewCell&UICollectionViewCellподклассы,layoutSubviewне назовут после подвиды будут правильно заложены из.2-Как заметил @EugenDimboiu (пожалуйста, озвучьте его ответ, если он вам полезен), звоните
[myView layoutIfNeeded]на не-выложенном подвиде будет макет его правильно как раз вовремя.- (void)layoutSubviews { [super layoutSubviews]; NSLog (self.myLabel); // 1000x1000 size [self.myLabel layoutIfNeeded]; NSLog (self.myLabel); // normal size }3 - На мой взгляд, это, безусловно, ошибка. Я отправил его на радар (id 28562874).
PS: Я не уроженец английского языка, поэтому не стесняйтесь редактировать мой пост, если моя грамматика должна быть исправлена ;)
PS2: если у вас есть лучшее решение, не стесняйтесь писать другое ответ. Я передвину принятый ответ.
Я знаю, что это не был ваш точный вопрос, но я столкнулся с аналогичной проблемой, когда, как и при обновлении, некоторые из моих представлений были испорчены, несмотря на правильный размер кадра в viewDidLayoutSubviews. В соответствии с iOS 10 примечания к выпуску:
" отправка layoutIfNeeded в представление не ожидается, чтобы переместить представление, но в более ранних версиях, если представление было translatesAutoresizingMaskIntoConstraints установлено в НЕТ, И если это было будучи позиционированы по ограничениям, layoutIfNeeded будет двигаться в целях сопоставьте механизм компоновки перед отправкой компоновки в поддерево. Эти изменения исправляют это поведение, а положение приемника и обычно ее размер не будет зависеть от layoutIfNeeded.
некоторые существующие коды могут полагаться на это неправильное поведение, которое теперь исправлено. Нет никаких изменений поведения для двоичных файлов, связанных ранее iOS 10, но при создании на iOS 10 вам может потребоваться исправить некоторые ситуации путем отправки-layoutIfNeeded в супервизор из translatesAutoresizingMaskIntoConstraints представление, которое было предыдущим приемник, или же позиционирование и калибровка его до (или после, в зависимости от вашего желаемого поведения) layoutIfNeeded.
сторонние приложения с пользовательскими подклассами UIView с использованием автоматического макета, который переопределите layoutSubviews и грязный макет на себя перед вызовом super есть риск запуска контура обратной связи макета, когда они перестраиваются iOS 10. Когда они правильно отправлены последующие layoutSubviews звонки они должны быть уверены, что в какой-то момент перестанут загрязнять макет на себя (Примечание что этот вызов был пропущен в выпуске до iOS 10)."
по существу вы не можете вызвать layoutIfNeeded на дочернем объекте представления, если вы используете translatesAutoresizingMaskIntoConstraints - теперь вызов layoutIfNeeded должен быть на супервизоре, и вы все еще можете вызвать это в viewDidLayoutSubviews.
устранение: обернуть все внутри
viewDidLayoutSubviewsinDispatchQueue.main.async.// swift 3 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() DispatchQueue.main.async { // do stuff here } }
это исправили (ужасно раздражает) вопрос для меня:
- (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.view.frame = CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height); }Edit / Note: это для полноэкранного ViewController.
Если кадры не корректны в layoutSubViews (что они не являются), Вы можете отправить асинхронный бит кода в основной поток. Это дает системе некоторое время, чтобы сделать макет. Когда блок, который вы отправляете, выполняется, кадры имеют свои правильные размеры.
на самом деле
viewDidLayoutSubviewsтакже не самое лучшее место для установки рамки вашего зрения. Насколько я понял, отныне единственное место, где это должно быть сделаноlayoutSubviewsметод в коде фактического представления. Я хотел бы, чтобы я не был прав, кто-то поправьте меня, пожалуйста, если это не так!
Я уже сообщал об этой проблеме в apple, эта проблема существует с давних пор, когда вы инициализируете UIViewController из Xib, но я нашел довольно хороший обходной путь. В дополнение к этому я обнаружил, что проблема в некоторых случаях, когда layoutIfNeeded на UICollectionView и UITableView, когда источник данных не установлен в начальный момент, и необходимо также swizzle его.
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; }Итак, от сделал до воли
странно
лучшее решение для меня.
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 (of layer: CALayer) вместо layoutSubviews в подвид ячейки, чтобы иметь правильные кадры

Comments