В основном я хочу, чтобы мои подзаголовки располагались по-разному в зависимости от ориентации iPad (портрет или пейзаж), используя классы калибровки, представленные в xcode 6. Я нашел множество руководств, объясняющих, как доступны различные классы прошивки для Iphones в портретном и ландшафтном на IB, но, похоже, нет никого, что бы охватывало индивидуальные пейзажные или портретные режимы для iPad на IB. Может ли кто-нибудь помочь?
Класс размера для iPad-портрета и ландшафтных режимов
Ответ 1
Похоже, что Apple намерена рассматривать ориентацию iPad как одно и то же, но, как многие из нас находят, есть очень законные причины дизайна, которые хотят изменить макет интерфейса для iPad Portrait и iPad.
К сожалению, текущая ОС, похоже, не поддерживает эту разницу... означает, что мы вернулись к манипулированию ограничениями автоматической компоновки кода или аналогичных обходных решений, чтобы добиться того, что мы в идеале должны получить бесплатно используя Adaptive UI.
Не изящное решение.
Разве не существует способа использовать магию, которую Apple уже встроила в IB и UIKit, чтобы использовать класс размера по нашему выбору для заданной ориентации?
~
В более общем понимании проблемы я понял, что "классы размера" - это просто способы решения нескольких макетов, которые хранятся в IB, чтобы они могли быть вызваны по мере необходимости во время выполнения.
На самом деле "класс размера" - это всего лишь пара значений перечисления. Из UIInterface.h:
typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
UIUserInterfaceSizeClassUnspecified = 0,
UIUserInterfaceSizeClassCompact = 1,
UIUserInterfaceSizeClassRegular = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);
Таким образом, независимо от того, что Apple решила назвать эти разные варианты, в принципе, это всего лишь пара целых чисел, используемых как уникальный идентификатор сортов, чтобы отличить один макет от другого, сохраненный в IB.
Теперь, предположим, что мы создаем альтернативный макет (используя неиспользуемый класс размера) в 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 {
_willTransitionToPortrait = size.height > size.width;
}
-(UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController {
UITraitCollection *traitCollectionForOverride = _willTransitionToPortrait ? _traitCollection_CompactRegular : _traitCollection_AnyAny;
return traitCollectionForOverride;
}
@end
В качестве быстрой демонстрации, чтобы увидеть, сработала ли она, я добавил специальные метки специально для "Регулярных/Регулярных" и "Компактных/Обычных" версий макета дочернего контроллера в IB:
И вот что это похоже на работу, когда iPad находится в обеих ориентациях:
Voila! Конфигурации пользовательского размера класса во время выполнения.
Надеюсь, Apple сделает это ненужным в следующей версии ОС. В то же время это может быть более элегантный и масштабируемый подход, чем программный беспорядок с ограничениями автоматического макета или другие манипуляции с кодом.
~
РЕДАКТИРОВАТЬ (6/4/15): Пожалуйста, имейте в виду, что приведенный выше пример кода является, по сути, доказательством концепции, демонстрирующей технику. Не стесняйтесь адаптироваться по мере необходимости для своего конкретного приложения.
~
РЕДАКТИРОВАТЬ (7/24/15): Отрадно, что приведенное выше объяснение, похоже, помогает демистифицировать проблему. Хотя я не тестировал его, код mohamede1945 [ниже] выглядит как полезная оптимизация для практических целей. Не стесняйтесь проверить это и сообщите нам, что вы думаете. (В интересах полноты, я оставлю образец кода выше как есть.)
Ответ 2
Как итог очень длинного ответа РонДиамонда. Все, что вам нужно сделать, находится в вашем контроллере корневого представления.
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.
Ответ 3
iPad имеет "регулярную" черту размера как для горизонтального, так и для вертикального размеров, не делая различия между портретом и ландшафтом.
Эти значения размера могут быть переопределены в вашем пользовательском коде подкласса 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 может использовать разные значения размера от нормальной полноэкранной работы.
Этот ответ основан на подходе, сделанном в этом сообщении в блоге с некоторыми улучшениями.
Ответ 4
Длительный и полезный ответ RonDiamond - хорошее начало для понимания принципов, однако код, который работал у меня (iOS 8+), основан на переопределяющем методе (UITraitCollection *)traitCollection
Итак, добавьте ограничения в InterfaceBuilder с вариантами для Width-Compact, например, для свойства ограничения Installed. So Width - Любой будет действителен для ландшафта, Width - Compact для Portrait.
Чтобы переключить ограничения в коде на основе текущего размера контроллера, просто добавьте следующее в свой класс 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]];
}
}
Ответ 5
Насколько отличается ваш ландшафтный режим, чем ваш портретный режим? Если это совсем другое, может быть хорошей идеей создать другой контроллер представления и загрузить его, когда устройство находится в ландшафтном
Например
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
//load landscape view controller here
Ответ 6
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
}}