iosipadxcode8ipad-3

Different UI for different iPAD sizes


Our project runs for all iPAD's and we came across a problem as an example we have 8 buttons vertically sitting on a screen with constraints being added as vertical spacing they look fine on a iPAD 9.7 inch, but they look really big on iPAD 12.9, so the question is, is there any good way to actually use the screen space for something better, as in add an extra UIView if it is iPAD 12.9. I have looked into working with size classes, but I believe there is one size class for all iPADs, what I want is if there is a way to have different UI for different iPAD sizes using the Interface builder


Solution

  • The way I imagine your situation is that this specific ViewController has a lot of shared stuff, (like a common top bar, or a navigation bar) but just the middle content doesn't seem to fit properly.

    In cases like this it is recommended to have a custom UIView that will load a different xib file based on the height or width of the current device.

    The key point here is that, you actually only need one UIView subclass.

    For this you can use @IBDesignable as well to preview it in real time inside the interface builder.

    To achieve this you have to follow the next steps.

    1) Create a .xib file for each of your "UIViews" based on the sizes.

    2) Create an UIView subclass.

    3) Hook the properties from the interface builder to this subclass. Note that you have to repeat this process for each of the xib files you want to use. IMPORTANT: Even though they are different xibs, they all get hooked into the same class.

    4) Mark the created class as @IBDesignable like this.

    @IBDesignable class CustomView: UIView {
        ...
    }
    

    5) Add the following code inside your class that will load a different xib based on whatever criteria you chose.

    ////////// View Logic //////////
    
    // Our custom view from the XIB file
    var view: UIView!
    
    func xibSetup() {
        view = loadViewFromNib()
    
        // use bounds not frame or it'll be offset
        view.frame = bounds
    
        // Make the view stretch with containing view
        view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(view)
    }
    
    func setup()
    {
        // Extra setup goes here
    
    }
    
    func loadViewFromNib() -> UIView {
    
        let bundle = Bundle(for: type(of: self))
    
        let nib : UINib
    
        let screenRect : CGRect =  UIScreen.main.bounds;
    
        // Use a different Nib based on the current screen height
    
        if screenRect.size.height == 1024 {
            // iPad Air & Pro
    
            nib = UINib(nibName: "iPadNormal", bundle: bundle)
        }
        else if screenRect.size.height == 1366 {
            // Large iPad Pro
    
            nib = UINib(nibName: "iPadLarge", bundle: bundle)
        }
        else {
            // Fall back
    
            nib = UINib(nibName: "iPadFallback", bundle: bundle)
        }
    
        let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
    
        return view
    }
    
    
    override init(frame: CGRect) {
        // 1. setup any properties here
    
        // 2. call super.init(frame:)
        super.init(frame: frame)
    
        // 3. Setup view from .xib file
        xibSetup()
    
        // 4. Other Setup
        setup()
    }
    
    required init?(coder aDecoder: NSCoder) {
        // 1. setup any properties here
    
        // 2. call super.init(coder:)
        super.init(coder: aDecoder)
    
        // 3. Setup view from .xib file
        xibSetup()  
    
        // 4. Other Setup
        setup()
    }
    
    
    ////////////////
    

    IMPORTANT:

    This approach is ONLY recommended when the contents of the custom view are the same, or with very minimal changes (layout doesn't matter). If the contents change a lot between sizes (like you actually wanna display different things) then you should create a "BaseViewController" with your shared logic, and make a subclass per iPad Size, so that each of them has it's own ViewController + Interface Builder Screen. Then just load the required screen just as if it was a completely different screen.