我有一个针对iOS8的应用程序,初始视图控制器是UISplitViewController。我使用故事板,因此它可以为我实现一切。
由于我的设计,我需要SplitViewController在iPhone上以纵向模式显示主视图和详细视图。所以我正在寻找一种方法来覆盖这个UISplitViewController的特征集合。
我发现我可以使用
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!) { ... }
但遗憾的是,只有方法可以覆盖子控制器特征集合:
setOverrideTraitCollection(collection: UITraitCollection!, forChildViewController childViewController: UIViewController!)
我在UISplitViewController子类中无法实现自我。
我查看了Apple的示例应用Adaptive Photos。在这个应用程序作者中,作为root使用特殊的TraitOverrideViewController,并在他的viewController setter中使用一些魔法来使它全部工作。
对我来说这看起来很可怕。是否有任何方法可以覆盖特征?或者如果没有,我怎么能设法使用与故事板相同的黑客?换句话说,如何以root用户身份注入一些viewController只是为了处理带故事板的UISplitViewController的特征?
答案 0 :(得分:6)
好的,我希望还有另一种解决方法,但是现在我只是将Apple示例中的代码转换为Swift并将其调整为与Storyboard一起使用。
它有效,但我仍然相信这是一个很好的方式来存档这个目标。
我的TraitOverride.swift:
import UIKit
class TraitOverride: UIViewController {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
var forcedTraitCollection: UITraitCollection? {
didSet {
updateForcedTraitCollection()
}
}
override func viewDidLoad() {
setForcedTraitForSize(view.bounds.size)
}
var viewController: UIViewController? {
willSet {
if let previousVC = viewController {
if newValue !== previousVC {
previousVC.willMoveToParentViewController(nil)
setOverrideTraitCollection(nil, forChildViewController: previousVC)
previousVC.view.removeFromSuperview()
previousVC.removeFromParentViewController()
}
}
}
didSet {
if let vc = viewController {
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMoveToParentViewController(self)
updateForcedTraitCollection()
}
}
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator!) {
setForcedTraitForSize(size)
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}
func setForcedTraitForSize (size: CGSize) {
let device = traitCollection.userInterfaceIdiom
var portrait: Bool {
if device == .Phone {
return size.width > 320
} else {
return size.width > 768
}
}
switch (device, portrait) {
case (.Phone, true):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .Regular)
case (.Pad, false):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .Compact)
default:
forcedTraitCollection = nil
}
}
func updateForcedTraitCollection() {
if let vc = viewController {
setOverrideTraitCollection(self.forcedTraitCollection, forChildViewController: vc)
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
performSegueWithIdentifier("toSplitVC", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "toSplitVC" {
let destinationVC = segue.destinationViewController as UIViewController
viewController = destinationVC
}
}
override func shouldAutomaticallyForwardAppearanceMethods() -> Bool {
return true
}
override func shouldAutomaticallyForwardRotationMethods() -> Bool {
return true
}
}
要使其工作,您需要在故事板上添加一个新的UIViewController并使其成为初始。将show segue从它添加到您的真实控制器,如下所示:
你需要命名segue" toSplitVC":
并将初始控制器设置为TraitOverride:
现在它也适合你。如果您发现更好的方法或任何缺陷,请告诉我。
答案 1 :(得分:5)
我知道你想在这里进行SWIFT翻译......而且你可能已经解决了这个问题。
以下是我花了相当长的时间尝试解决的问题 - 让我的SplitView在iPhone 6+上运行 - 这是一个Cocoa解决方案。
我的应用程序基于TabBar,SplitView具有导航控制器。最后,我的问题是没有将setOverrideTraitCollection发送到正确的目标。
@interface myUITabBarController ()
@property (nonatomic, retain) UITraitCollection *overrideTraitCollection;
@end
@implementation myUITabBarController
- (void)viewDidLoad
{
[super viewDidLoad];
[self performTraitCollectionOverrideForSize:self.view.bounds.size];
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
NSLog(@"myUITabBarController %@", NSStringFromSelector(_cmd));
[self performTraitCollectionOverrideForSize:size];
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}
- (void)performTraitCollectionOverrideForSize:(CGSize)size
{
NSLog(@"myUITabBarController %@", NSStringFromSelector(_cmd));
_overrideTraitCollection = nil;
if (size.width > 320.0)
{
_overrideTraitCollection = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
}
[self setOverrideTraitCollection:_overrideTraitCollection forChildViewController:self];
for (UIViewController * view in self.childViewControllers)
{
[self setOverrideTraitCollection:_overrideTraitCollection forChildViewController:view];
NSLog(@"myUITabBarController %@ AFTER viewTrait=%@", NSStringFromSelector(_cmd), [view traitCollection]);
}
}
@end
答案 2 :(得分:4)
我知道问题被问了一年多,但我认为我的答案会帮助像我这样没有取得成功的人。
如果解决方案非常简单,您可以覆盖traitCollection:
方法。以下是我的应用中的示例:
- (UITraitCollection *)traitCollection {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
return super.traitCollection;
} else {
switch (self.modalPresentationStyle) {
case UIModalPresentationFormSheet:
case UIModalPresentationPopover:
return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
default:
return super.traitCollection;
}
}
}
如果控制器显示为弹出框或表单,那么我们的想法是在iPad上强制使用Compact尺寸类。
希望它有所帮助。
更新:
Apple不建议这样做:
直接使用traitCollection属性。不要覆盖它。不要 提供自定义实现。
我不再使用这种方法了!现在我在父viewControler类中实现overrideTraitCollectionForChildViewController:
。
答案 3 :(得分:1)
是的,它必须使用自定义容器View Controller来覆盖函数viewWillTransitionToSize。您可以使用故事板将容器View Controller设置为初始值。 此外,您可以参考使用该程序的this good artical来实现它。根据它,你的判断肖像可能有一些限制:
var portrait: Bool {
if device == .Phone {
return size.width > 320
} else {
return size.width > 768
}
}
除了
if **size.width > size.height**{
self.setOverrideTraitCollection(UITraitCollection(horizontalSizeClass: UIUserInterfaceSizeClass.Regular), forChildViewController: viewController)
}
else{
self.setOverrideTraitCollection(nil, forChildViewController: viewController)
}
&#34;
答案 4 :(得分:1)
额外的顶级VC适用于简单的应用程序但它不会传播到模态呈现的VC,因为它们没有parentVC。所以你需要在不同的地方再次插入它。
我发现的一个更好的方法就是继承UINavigationController,然后在故事板和其他通常使用UINavigationController的地方使用你的子类。它可以在故事板中保存额外的VC混乱,并且还可以节省代码中的杂乱。
此示例将使所有iPhone对横向使用常规水平尺寸类。
@implementation MyNavigationController
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController
{
UIDevice *device = [UIDevice currentDevice];
if (device.userInterfaceIdiom == UIUserInterfaceIdiomPhone && CGRectGetWidth(childViewController.view.bounds) > CGRectGetHeight(childViewController.view.bounds)) {
return [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
}
return nil;
}
@end
答案 5 :(得分:1)
道具@Ilyca
Swift 3
import UIKit
class TraitOverride: UIViewController {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
var forcedTraitCollection: UITraitCollection? {
didSet {
updateForcedTraitCollection()
}
}
override func viewDidLoad() {
setForcedTraitForSize(size: view.bounds.size)
}
var viewController: UIViewController? {
willSet {
if let previousVC = viewController {
if newValue !== previousVC {
previousVC.willMove(toParentViewController: nil)
setOverrideTraitCollection(nil, forChildViewController: previousVC)
previousVC.view.removeFromSuperview()
previousVC.removeFromParentViewController()
}
}
}
didSet {
if let vc = viewController {
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
updateForcedTraitCollection()
}
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
setForcedTraitForSize(size: size)
super.viewWillTransition(to: size, with: coordinator)
}
func setForcedTraitForSize (size: CGSize) {
let device = traitCollection.userInterfaceIdiom
var portrait: Bool {
if device == .phone {
return size.width > 320
} else {
return size.width > 768
}
}
switch (device, portrait) {
case (.phone, true):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .regular)
case (.pad, false):
forcedTraitCollection = UITraitCollection(horizontalSizeClass: .compact)
default:
forcedTraitCollection = nil
}
}
func updateForcedTraitCollection() {
if let vc = viewController {
setOverrideTraitCollection(self.forcedTraitCollection, forChildViewController: vc)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
performSegue(withIdentifier: "toSplitVC", sender: self)
}
override var shouldAutomaticallyForwardAppearanceMethods: Bool {
return true
}
override func shouldAutomaticallyForwardRotationMethods() -> Bool {
return true
}
}