Похоже, что с Xcode 8 viewDidLoad
все подвиды контроллера представления имеют одинаковый размер 1000x1000. Странно, но хорошо, viewDidLoad
никогда не было лучшего места для правильного размера просмотров.
Но viewDidLayoutSubviews
есть!
В моем текущем проекте я пытаюсь напечатать размер кнопки:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"%@", self.myButton);
}
Журнал показывает размер (1000x1000) для myButton! Затем, если я захожу, например, нажатием кнопки, журнал показывает нормальный размер.
Я использую автопрокладку.
Это ошибка?
ios
autolayout
ios10
xcode8
Мартин
источник
источник
(0, 0, 1000, 1000)
связанная инициализация - это новый способ, которым Xcode создает представления из IB. До Xcode8 представления создавались с настроенным размером в xib, а затем изменялись в соответствии с размером экрана сразу после этого. Но теперь в документе IB нет настроенного размера, так как размер зависит от вашего выбора устройства (внизу экрана). Итак, реальный вопрос: есть ли надежное место, где можно было бы проверить окончательный размер просмотров?Ответы:
Теперь Interface Builder позволяет пользователю динамически изменять размер каждого контроллера представления в раскадровке, чтобы имитировать размер определенного устройства.
Перед этой функцией пользователь должен вручную установить размер каждого контроллера представления. Таким образом, контроллер представления был сохранен с определенным размером, который использовался
initWithCoder
для установки начального кадра.Теперь кажется, что
initWithCoder
не используют размер, определенный в раскадровке, и определяют размер 1000x1000 пикселей для представления viewcontroller и всех его подвидов.Это не проблема, потому что представления всегда должны использовать одно из этих решений компоновки:
autolayout, и все ограничения будут правильно отображать ваши представления
autoresizingMask, который будет размещать каждое представление, к которому не привязаны какие-либо ограничения ( обратите внимание, что ограничения автоматического раскладки и маржи теперь совместимы в одном и том же представлении \ 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: Если у вас есть лучшее решение, не стесняйтесь писать другой ответ. Я перенесу принятый ответ.
источник
UIStackView
внутренняя частьUICollectionViewCell
не возвращала правильную высоту во времяviewDidLayoutSubviews
. ЗвонокlayoutIfNeeded
сразу устранил проблему.Вы используете закругленные углы для кнопки? Попробуйте позвонить
layoutIfNeeded()
раньше.источник
Решение: Wrap все внутри
viewDidLayoutSubviews
вDispatchQueue.main.async
.// swift 3 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() DispatchQueue.main.async { // do stuff here } }
источник
-viewDidLoad
... такой странный обходной путьЯ знаю, что это был не ваш точный вопрос, но я столкнулся с аналогичной проблемой, когда, как и при обновлении, некоторые из моих представлений были испорчены, несмотря на правильный размер кадра в viewDidLayoutSubviews. Согласно примечаниям к выпуску iOS 10:
По сути, вы не можете вызвать layoutIfNeeded для дочернего объекта View, если вы используете translatesAutoresizingMaskIntoConstraints - теперь вызов layoutIfNeeded должен быть в superView, и вы все равно можете вызывать это в viewDidLayoutSubviews.
источник
Если кадры в layoutSubViews неверны (а это не так), вы можете отправить асинхронный фрагмент кода в основной поток. Это дает системе некоторое время на создание макета. Когда отправляемый вами блок выполняется, фреймы имеют соответствующие размеры.
источник
Это устранило для меня (до смешного раздражающую) проблему:
- (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.view.frame = CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height); }
Изменить / Примечание: это для полноэкранного ViewController.
источник
[super viewDidLayoutSubviews];
этот метод из-за того, что многие функции автоматического раскладки выполняются самим представлениемUIView
s. Но если вам необходимо выполнить специальные вычисления для определенного кадра просмотра, ответ Евгения работает: вызовитеlayoutIfNeeded
его. У меня такое ощущение, что это не лучшее решение, но лучшего пока не нашел.На самом деле это
viewDidLayoutSubviews
тоже не лучшее место для установки рамки вашего обзора. Насколько я понял, с этого момента единственное место, где это нужно делать, - этоlayoutSubviews
метод в коде реального представления. Если бы я был неправ, поправьте меня, пожалуйста, если это неправда!источник
viewDidLayoutSubviews
довольно неоднозначна. Второе предложение в «дискуссиях» в чем-то противоречит последнему. developer.apple.com/reference/uikit/uiviewcontroller/…Я уже сообщал об этой проблеме в Apple, эта проблема существует уже давно, когда вы инициализируете UIViewController из Xib, но я нашел довольно хорошее решение. В дополнение к этому я обнаружил эту проблему в некоторых случаях, когда layoutIfNeeded в UICollectionView и UITableView, когда источник данных не установлен в начальный момент, и необходимо также его поменять.
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; }
Итак, от Did к Will
Супер странно
источник
Лучшее решение для меня.
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 в подпредставлении ячейки, чтобы иметь правильные фреймы
источник
Если вам нужно что-то сделать на основе кадра вашего представления - переопределите layoutSubviews и вызовите layoutIfNeeded
override func layoutSubviews() { super.layoutSubviews() yourView.layoutIfNeeded() setGradientForYourView() }
У меня была проблема с viewDidLayoutSubviews, возвращавшим неправильный кадр для моего представления, для которого мне нужно было добавить градиент. И только layoutIfNeeded правильно сделал :)
источник
Согласно новому обновлению в ios, это на самом деле ошибка, но мы можем уменьшить ее, используя -
Если вы используете xib с автоматическим размещением в своем проекте, вам нужно просто обновить фрейм в настройках автоматического размещения, пожалуйста, найдите изображение для этого.
источник