Схоже, наміром Apple трактувати обидві орієнтації iPad як однакові - але, як багато хто з нас виявляє, є дуже законні причини дизайну, які хочуть змінити макет інтерфейсу для iPad Portrait проти iPad Landscape.
На жаль, нинішня ОС не забезпечує підтримку цього розрізнення ... це означає, що ми знову маніпулюємо обмеженнями автоматичної компонування в коді чи подібними способами вирішення, щоб досягти того, що ми в ідеалі повинні отримати безкоштовно за допомогою адаптивного інтерфейсу користувача .
Не елегантне рішення.
Чи не існує способу використовувати магію, яку Apple вже вбудував у IB та UIKit, щоб використовувати клас розмірів, обраний нами для заданої орієнтації?
~
Розмірковуючи про проблему більш загально, я зрозумів, що "класи розмірів" - це просто способи вирішення кількох макетів, які зберігаються в ІБ, щоб їх можна було викликати за потреби під час виконання.
Насправді "клас розмірів" - це просто пара значень перерахувань. З UIInterface.h:
typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
UIUserInterfaceSizeClassUnspecified = 0,
UIUserInterfaceSizeClassCompact = 1,
UIUserInterfaceSizeClassRegular = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);
Тож незалежно від того, що Apple вирішила назвати ці різні варіанти, по суті, вони є лише парою цілих чисел, які використовуються як унікальний різновид ідентифікаторів, щоб відрізнити один макет від іншого, що зберігається в ІБ.
Тепер, припустимо, що ми створимо альтернативну компоновку (використовуючи невикористаний клас розмірів) в IB - скажімо, для iPad Portrait ... чи є спосіб, щоб пристрій використовував наш вибір класу розмірів (макет інтерфейсу), як це потрібно під час виконання ?
Спробувавши декілька різних (менш елегантних) підходів до проблеми, я підозрював, що може існувати спосіб програмного перекриття класу розмірів за замовчуванням. І є (в UIViewController.h):
// Call to modify the trait collection for child view controllers.
- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
Таким чином, якщо ви можете упакувати ієрархію контролера перегляду як "дочірній" контролер перегляду та додати його до контролера подання вищого рівня ... тоді ви можете умовно перекрити дитину, думаючи, що це інший розмір класу, ніж за замовчуванням з ОС.
Ось приклад реалізації, який робить це, у контролері подання "батьків":
@interface RDTraitCollectionOverrideViewController : UIViewController {
BOOL _willTransitionToPortrait;
UITraitCollection *_traitCollection_CompactRegular;
UITraitCollection *_traitCollection_AnyAny;
}
@end
@implementation RDTraitCollectionOverrideViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setUpReferenceSizeClasses];
}
- (void)setUpReferenceSizeClasses {
UITraitCollection *traitCollection_hCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection *traitCollection_vRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
_traitCollection_CompactRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hCompact, traitCollection_vRegular]];
UITraitCollection *traitCollection_hAny = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified];
UITraitCollection *traitCollection_vAny = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassUnspecified];
_traitCollection_AnyAny = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hAny, traitCollection_vAny]];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]
_willTransitionToPortrait = size.height > size.width;
}
-(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController {
UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny;
return traitCollectionForOverride;
}
@end
Як швидка демонстрація того, чи працює вона, я додав спеціальні мітки спеціально до версій "Regular / Regular" та "Compact / Regular" дочірнього макета контролера в IB:
А ось як виглядає запуск, коли iPad знаходиться в обох напрямках:
Вуаля! Спеціальні конфігурації класу розмірів під час виконання.
Сподіваємось, Apple зробить це непотрібним у наступній версії ОС. Тим часом це може бути більш елегантним і масштабованим підходом, ніж програмно возитися з обмеженнями автоматичного розміщення або робити інші маніпуляції в коді.
~
EDIT (6/4/15): Будь ласка, майте на увазі, що наведений вище зразок коду є фактично доказом концепції для демонстрації методики. Не соромтеся адаптуватися за потребою для вашої власної програми.
~
EDIT (24.07.2015): Приємно, що вищезазначене пояснення допомагає демістифікувати проблему. Поки я не перевіряв його, код mohamede1945 [нижче] виглядає як корисна оптимізація для практичних цілей. Не соромтесь перевірити це і дайте нам знати, що ви думаєте. (В інтересах повноти я залиште зразок коду вище таким, як є.)