Класс размера для iPad-портрета и ландшафтных режимов
В основном я хочу, чтобы мои подсмотры располагались по-разному в зависимости от ориентации iPad (портрет или пейзаж) с использованием classов калибровки, введенных в xcode 6. Я нашел множество руководств, объясняющих, как различные classы прорисовки доступны для Iphones в портретном и ландшафтном отношении на IB но, похоже, нет никого, что бы охватывало отдельные ландшафтные или портретные режимы для iPad на IB. Может ли кто-нибудь помочь?
- Как Apple знает, что вы используете частный API?
- Установка UIDatePicker в таблицу UIActionSheet
- NSDateFormatter и yyyy-MM-dd
- iOS8 - ограничения двусмысленно указывают на высоту нуля
- Лучший способ заставить NSRunLoop ждать установки флага?
- Округлые углы только на вершине UIView
- Что такое двойная звезда (например, NSError **)?
- Прокрутка UITableView внутри UIScrollView
Похоже, что намерение Apple относиться к ориентации iPad как к одному – но, как и многие из нас, есть очень законные причины дизайна, которые хотят изменить макет интерфейса для iPad Portrait и iPad.
К сожалению, текущая ОС, похоже, не поддерживает эту разницу … означает, что мы вернулись к манипулированию ограничениями автоматической компоновки кода или аналогичных обходных решений для достижения того, чего мы в идеале должны получить бесплатно, используя Adaptive UI ,
Не изящное решение.
Разве не существует способа использовать магию, которую Apple уже встроила в IB и UIKit, чтобы использовать class размеров для нашего выбора для определенной ориентации?
~
Рассматривая проблему в более общем плане, я понял, что «classы размеров» – это просто способы решения нескольких макетов, которые хранятся в IB, чтобы они могли быть вызваны по мере необходимости во время выполнения.
Фактически, class «размер» – это всего лишь пара значений enums. Из UIInterface.h:
typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) { UIUserInterfaceSizeClassUnspecified = 0, UIUserInterfaceSizeClassCompact = 1, UIUserInterfaceSizeClassRegular = 2, } NS_ENUM_AVAILABLE_IOS(8_0);
Поэтому, независимо от того, что Apple решила назвать эти разные варианты, в принципе, это всего лишь пара целых чисел, используемых как уникальный идентификатор сортов, чтобы отличить один макет от другого, сохраненный в IB.
Теперь предположим, что мы создаем альтернативный макет (используя неиспользуемый class размера) в IB – скажем, для iPad Portrait … есть способ, чтобы устройство использовало наш выбор размера (макет интерфейса) по мере необходимости во время выполнения ?
Попробовав несколько разных (менее элегантных) подходов к проблеме, я подозревал, что может быть способ переопределить class размера по умолчанию программным путем. И есть (в 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);
Таким образом, если вы можете упаковать иерархию controllerа представления в виде «дочернего» controllerа представлений и добавить его в controller родительского представления верхнего уровня … тогда вы можете условно переопределить ребенка, полагая, что это другой class размеров, чем стандартный из ОС.
Вот пример реализации, который делает это, в «родительском» controllerе представления:
@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)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
В качестве быстрой демонстрации, чтобы увидеть, сработала ли она, я добавил специальные метки специально для «Регулярных / Регулярных» и «Компактных / Обычных» версий макета дочернего controllerа в IB:
И вот что это похоже на работу, когда iPad находится в обеих ориентациях:
Вуаля! Конфигурации пользовательского размера classа во время выполнения.
Надеюсь, Apple сделает это ненужным в следующей версии ОС. В то же время это может быть более элегантный и масштабируемый подход, чем программный беспорядок с ограничениями автоматического макета или другие манипуляции с кодом.
~
EDIT (6/4/15): Пожалуйста, имейте в виду, что приведенный выше пример кода является, по сути, доказательством концепции для демонстрации техники. Не стесняйтесь адаптироваться по мере необходимости для своего конкретного приложения.
~
EDIT (7/24/15): Отрадно, что приведенное выше объяснение, похоже, помогает демистифицировать проблему. Хотя я не тестировал его, код mohamede1945 [ниже] выглядит как полезная оптимизация для практических целей. Не стесняйтесь проверить это и сообщите нам, что вы думаете. (В интересах полноты, я оставлю образец кода выше как есть).
Как итог очень длинного ответа РонДиамонда. Все, что вам нужно сделать, – это ваш controller корневого представления.
Objective-C
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController { if (CGRectGetWidth(self.view.bounds) < CGRectGetHeight(self.view.bounds)) { return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact]; } else { return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular]; } }
Swift:
override func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection! { if view.bounds.width < view.bounds.height { return UITraitCollection(horizontalSizeClass: .Compact) } else { return UITraitCollection(horizontalSizeClass: .Regular) } }
Затем в storyborad используйте компактную ширину для Portrait и Regular width для Landscape.
IPad имеет «обычную» черту размера как для горизонтальных, так и для вертикальных размеров, не давая разницы между портретом и пейзажем.
Эти значения размера могут быть переопределены в вашем пользовательском UIViewController
подclassа UIViewController
помощью метода traitCollection
, например:
- (UITraitCollection *)traitCollection { // Distinguish portrait and landscape size traits for iPad, similar to iPhone 7 Plus. UITraitCollection *superTraits = [super traitCollection]; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { UITraitCollection *horizontalRegular = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular]; UITraitCollection *horizontalCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact]; UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular]; UITraitCollection *verticalCompact = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact]; UITraitCollection *regular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalRegular]]; UITraitCollection *portrait = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalCompact, verticalRegular]]; UITraitCollection *landscape = [UITraitCollection traitCollectionWithTraitsFromCollections:@[horizontalRegular, verticalCompact]]; if ([superTraits containsTraitsInCollection:regular]) { if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) return portrait; else return landscape; } } return superTraits; }
Это придает iPad такие же черты, как iPhone 7 Plus. Обратите внимание, что другие модели iPhone обычно имеют свойство «компактной ширины» (а не регулярную ширину) независимо от ориентации.
Подражание iPhone 7 Plus таким образом позволяет использовать эту модель как стенд для iPad в XCode Interface Builder, который не знает о настройках в коде.
Имейте в виду, что Split View на iPad может использовать разные черты размера от нормальной полноэкранной работы.
Этот ответ основан на подходе, сделанном в этом сообщении в блоге , с некоторыми улучшениями.
Длительный и полезный ответ RonDiamond – хорошее начало для понимания принципов, однако код, который работал для меня (iOS 8+), основан на переопределении метода (UITraitCollection *)traitCollection
Итак, добавьте ограничения в InterfaceBuilder с вариантами для Width-Compact, например, для свойства ограничения Installed. So Width – Любой будет действителен для ландшафта, Width – Compact для Portrait.
Чтобы переключить ограничения в коде на основе текущего размера controllerа, просто добавьте следующее в свой class UIViewController:
- (UITraitCollection *)traitCollection { UITraitCollection *verticalRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular]; if (self.view.bounds.size.width < self.view.bounds.size.height) { // wCompact, hRegular return [UITraitCollection traitCollectionWithTraitsFromCollections: @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact], verticalRegular]]; } else { // wRegular, hRegular return [UITraitCollection traitCollectionWithTraitsFromCollections: @[[UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular], verticalRegular]]; } }
Насколько отличается ваш ландшафтный режим, чем ваш портретный режим? Если это совсем другое, может быть хорошей идеей создать другой controller представления и загрузить его, когда устройство находится в ландшафтном
Например
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) //load landscape view controller here
Swift 3.0 для решения @RonDiamond
class Test : UIViewController { var _willTransitionToPortrait: Bool? var _traitCollection_CompactRegular: UITraitCollection? var _traitCollection_AnyAny: UITraitCollection? func viewDidLoad() { super.viewDidLoad() self.upReferenceSizeClasses = null } func setUpReferenceSizeClasses() { var traitCollection_hCompact: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassCompact) var traitCollection_vRegular: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassRegular) _traitCollection_CompactRegular = UITraitCollection(traitsFromCollections: [traitCollection_hCompact,traitCollection_vRegular]) var traitCollection_hAny: UITraitCollection = UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClassUnspecified) var traitCollection_vAny: UITraitCollection = UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClassUnspecified) _traitCollection_AnyAny = UITraitCollection(traitsFromCollections: [traitCollection_hAny,traitCollection_vAny]) } func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) _willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width } func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { _willTransitionToPortrait = size.height > size.width } func overrideTraitCollectionForChildViewController(childViewController: UIViewController) -> UITraitCollection { var traitCollectionForOverride: UITraitCollection = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny return traitCollectionForOverride }}