0

I have a view controller. Basically, I want check some conditions, and if those conditions are true, I want to set it's size class manually (override it's default size class).

To do so, I would have to override it's UITraitCollection. I did some research, and was able to find this function:

setOverrideTraitCollection(collection: UITraitCollection!, forChildViewController childViewController: UIViewController!)

The function allows me to override the trait collection of a child view controller, but I want the view controller to be able to override it's own traits.

So just to recap, I want to do the following. When a view controller loads, I want it to check some conditions. If they are true, I want the view controller to override it's size class to a custom one.

Thanks for the help!

adds
  • 1
  • 1
  • Check out the suggested answer here: http://stackoverflow.com/questions/27735341/offscreen-uitableviewcells-for-size-calculations-not-respecting-size-class/27798773#27798773 – Rory McKinnel Aug 13 '15 at 20:23
  • Here is how Apple is doing it: https://github.com/sugarso/WWDC/blob/master/WWDC/AAPLTraitOverrideViewController.m :) – sloik Aug 13 '15 at 20:34
  • @RoryMcKinnel Thanks! I'm still confused. Your answer talks about table views and cells, I'm not sure how that relates to size classes. You did mention that traitCollection can be accessed in iOS 8, but it's a read only property. Sorry, I'm completely new to iOS programming so your answer was a bit over my head. – adds Aug 13 '15 at 20:37
  • @sloik Thanks! I took a look at that, and it appears that they are setting the trait properties in `updateForcedTraitCollection`. However, for some reason they don't set the properties for the view itself, they set it for a child named "viewController". I wasn't sure why they were doing that; I want to update the size class of the view itself, not of a child view. – adds Aug 13 '15 at 20:41
  • 1
    The accepted answer suggests overriding the getter for the `traitCollection` property. `traitCollection` contains the size class information the system uses. If you override this for your UIViewController, then the system will use your returned sizeclass and not its default one. Well that is the theory at least. – Rory McKinnel Aug 13 '15 at 20:41
  • @RoryMcKinnel Oh I see. Couple of things. I'm not sure how to override the getter without having to provide a custom implementation for all devices; I just want to override the size class if the device is an iPad, if it isn't I would just like to use the default size class. Secondly, the Apple page specifically says to not override it. Do you think I should do it despite this?: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITraitEnvironment_Ref/index.html#//apple_ref/occ/intfp/UITraitEnvironment/traitCollection – adds Aug 13 '15 at 21:00
  • You are now in unexplored territory I guess. If you go by that Apple doc link then the answer to your main question is that you should not override size class. If your controllers are all in UINavigationControllers, then you could provide subclass for UINavigationController and implement `overrideTraitCollectionForChildViewController`. You would need to be able to decide on the override for every view controller you have though. To do that you could create a protocol which if implemented by a controller can be used to ask it what size class it wants to have. – Rory McKinnel Aug 13 '15 at 22:11

3 Answers3

1

I just override UIViewController traitCollection method, works well!

// This is so we can get 'Regular' size class layout on an iPhone in landscape
- (UITraitCollection *)traitCollection
{
    UITraitCollection *traitCollection = super.traitCollection;
    UIDevice *device = [UIDevice currentDevice];

    if (device.userInterfaceIdiom == UIUserInterfaceIdiomPhone && (device.orientation == UIDeviceOrientationLandscapeLeft || device.orientation == UIDeviceOrientationLandscapeRight)) {
        if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
            UITraitCollection *regularCollection = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
            traitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection, regularCollection]];
        }
    }

    return traitCollection;
}
trapper
  • 10,375
  • 6
  • 31
  • 74
1

You can use a wrapper view controller. Implement 'setOverrideTraitCollection(collection: UITraitCollection!, forChildViewController childViewController: UIViewController!)' in it, and refactor your main view controller down as a child of that wrapper.

Hlung
  • 12,632
  • 6
  • 66
  • 87
-1

This is the way it worked for me (I was searching for the same solution for iPad to differentiate portrait/landscape modes without using of childViewController):

  1. I use separate class as subclass of UIViewController

the TraitCollectionOverrideViewController.h file:

#import <UIKit/UIKit.h>
@interface TraitCollectionOverrideViewController : UIViewController
@end

the TraitCollectionOverrideViewController.m file has only getter method for traitCollection property:

- (UITraitCollection *)traitCollection {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        return super.traitCollection;
    } else {
        UITraitCollection *traitCollection_hCompact = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
        UITraitCollection *traitCollection_vRegular = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *traitCollection_CompactRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hCompact, traitCollection_vRegular]];

        UITraitCollection *traitCollection_hRegular = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
        UITraitCollection *traitCollection_RegularRegular = [UITraitCollection traitCollectionWithTraitsFromCollections:@[traitCollection_hRegular, traitCollection_vRegular]];

        BOOL willTransitionToPortrait = self.view.frame.size.height > self.view.frame.size.width;

        UITraitCollection *traitCollectionForOverride = willTransitionToPortrait ? traitCollection_CompactRegular : traitCollection_RegularRegular;
        return traitCollectionForOverride;
    } 
}

I customised it in the way to use Custom/Regular size class for iPad portrait mode and standard Regular/Regular size class for iPad landscape mode

  1. Then in my real view controller class I use just subclassing from this custom class: @interface ForecastViewController : TraitCollectionOverrideViewController

  2. And then last part you need to do - set up constrains in the storyboard related to size class you need.

Bogdan Laukhin
  • 1,394
  • 1
  • 13
  • 26