22

I'm tuning my UI App, but I got an issue that I can't solve.

As I can see Compact height affects all iPhones under 4.7 inches, but my UI is fine except for the iPhone 4S (3.5 inches).

I don't want to modify the layout for all iPhones under 4.7 inches, just the iPhone 4S, at the same time I don't want to left out this device.

There's any workaround so I can set the amendments but just and only for the 3.5 inches portrait? or should I say goodbye to 100 millions devices out there?

I know it's a tough question and almost an opinion poll, but technically speaking I would like to find my best way out here.

Cœur
  • 32,421
  • 21
  • 173
  • 232
Helen Wood
  • 1,814
  • 2
  • 23
  • 39
  • 1
    Check out my answer here: http://stackoverflow.com/a/28093987/1135714/ – Cesare Mar 02 '15 at 20:48
  • Thank you @CeceXX Do you mean ask for an iPhone 4S in code, if so, set the constraints manually? – Helen Wood Mar 02 '15 at 21:12
  • I find creating `NSLayoutConstraints` constraints via code and adjusting their `constant` value depending on the device the user's using really cool. Have a try. – Cesare Mar 02 '15 at 21:14
  • 1
    What is your specific problem with the UI on a 3.5" screen? Size classes won't allow you to distinguish between the 4S and phone under 4.7", but there may be something you can do with the constraints that will make it work with all screens (like making a constraint relative to the height of the view). – rdelmar Mar 02 '15 at 23:14
  • Thank you @rdelmar would you mind expanding your approach? – Helen Wood Mar 03 '15 at 12:14
  • I can expand upon it if I know what your specific problem is. What UI elements do you have, and what specifically is wrong on the 3.5" screen? – rdelmar Mar 03 '15 at 18:44
  • @rdelmar Technically speaking, in my App all IU elements fit alright in all iPhones portrait but the iPhone4S, but size class don't make the different (I don't know why, I do...) So I like the idea of checking if it's a 4S and if so, setting the constraints specifically for it. – Helen Wood Mar 05 '15 at 13:13
  • 1
    The point of using constraints is so you don't have to check what screen you're on. You still haven't answered my question about specifics. You basically have 2 choices; you can make your UI elements get closer together, or you can make them smaller. Both of these things can be done automatically based on the height of the screen. If you can't use these automatic ways to get the correct look, you can always check the screen size in code, and use that to replace or modify a constraint. You should post an image of your UI, and describe what constraints you've tried. – rdelmar Mar 05 '15 at 16:31
  • Thank you. Yes I know I can make changes to make things different so they can fit in smaller screens. But I'm lazy, I don't want. I'm adamant about this. 2nd question, give me a moment and I'll post what I got. – Helen Wood Mar 05 '15 at 17:34
  • Thank you for your comments I came with a decent solution. Check it out. – Helen Wood Mar 05 '15 at 20:22
  • you can checkout my answer here : http://stackoverflow.com/a/37325714/2477632 – HamzaGhazouani May 23 '16 at 07:56

5 Answers5

17

There is no size class for iPhone 3.5 inch.

So I've made a class category for NSLayoutConstraint to edit it in Interface Builder which is very easy to use:

size class for iPhone 4/4s in Interface Builder

@interface NSLayoutConstraint (Extensions)

@property (nonatomic) IBInspectable CGFloat iPhone3_5_Constant;

@end

@implementation NSLayoutConstraint (Extensions)

- (CGFloat)iPhone3_5_Constant
{
    return self.constant;
}

- (void)setIPhone3_5_Constant:(CGFloat)iPhone3_5_Constant
{
    if ([UIScreen mainScreen].bounds.size.height < 500) {
        self.constant = iPhone3_5_Constant;
    }
}

@end
Pavel Alexeev
  • 5,538
  • 2
  • 38
  • 48
  • 6
    I too disagree with the down voter, actually it is an working approach. – Naveen Shan Jan 30 '17 at 23:58
  • Awesome solution! I was wondering why I couldn't use size classes to handle iPhone 4, but this solution is clean and wonderful. It works great! – Peterdk Mar 04 '17 at 22:14
  • Only thing that misses is that XCode won't update the layout in iPhone 4S mode. I guess that is not something we can control from altering `NSLayoutConstraint` using `Extensions`. Overriding existing methods from `Extensions` seems to be discouraged. – Peterdk Mar 04 '17 at 22:31
  • Haha, this is so easy and works so well, it's almost ridiculous. I'd upvote this several times, if only I could! Thanks!! – Toastor Oct 07 '17 at 07:31
13

An approach that just worked for me was to use the same constraints for all compact size classes but to use a combination of a greater than or equal to constraint and priorities to modify how the views were positioned on the iPhone 4's smaller screen.

I've got a constraint between the top of a numeric keypad view and its superview that is set to be greater than or equal to 160 (with a priority of 1000) and a constraint between the bottom of the keypad view and the bottom of the superview that is set to a constant of 30 (but with a lower priority of 750).

This means that on the iPhone 4 where there's not enough room for 160+ points above the keypad and 30 points below then it's the space below that goes.

Whilst this approach may not work in all cases, I'd encourage you to think about whether there's a set of priorities that will allow your views to adjust in the way you want on the smaller screen.

Jake MacMullin
  • 229
  • 3
  • 3
  • I think the right solution is this one. I had the same problem with the 3.5" screen, and using 2 constraints with different priorities help me solving this. – Ran Jan 26 '16 at 20:27
  • I agree, my personal preference is to avoid "magic numbers" in code (for better maintainability). – LorenzoValentijn Feb 01 '16 at 12:54
  • 1
    This worked for me along with proportional constraints (changing the typical 1 multiplier to e.g. 0.80) for fiine tuning. – AMC08 Jul 21 '16 at 20:28
  • thanks but your explanation is a bit ambiguous. "... and 30 points below then it's the space below that goes. " what do you mean by that? do you mean 30 points constraint would be preferred because there isn't enought space between the top of the keypad and top of the superview? if so, why should this be the case? – Özgür Sep 04 '16 at 00:46
  • This is an interesting approach that I've found helpful, though it's a little tricky – tarrball Nov 15 '16 at 15:47
13

Swift 3 version of Pavel Alexeev's solution. In Swift you can't use stored properties in extensions, so we apply it directly to the constant property.

extension NSLayoutConstraint
{
    //We use a simple inspectable to allow us to set a value for iphone 4.
    @IBInspectable var iPhone4_Constant: CGFloat
        {
        set{
            //Only apply value to iphone 4 devices.
            if (UIScreen.mainScreen().bounds.size.height < 500)
            {
                self.constant = newValue;
            }
        }
        get
        {
            return self.constant;
        }
    }
}
Peterdk
  • 14,529
  • 18
  • 97
  • 136
6

@all I can't make things smaller, because I'm dealing with pickerviews which happen to have only three valid heights for UIPickerView (162.0, 180.0 and 216.0). Sizes and constraints apart.

iPhone Sizes: http://www.idev101.com/code/User_Interface/sizes.html , 4S is unique.

So although my approach it's a little bit ugly get the things done, nearly on my point.

So I know it's far from Goodville, don't hit me down, just for sharing:

func checkForiPhone4S()
{
    if (UIScreen.mainScreen().bounds.size.height == 480) {
        println("It is an iPhone 4S - Set constraints")

        listPickerView.transform = CGAffineTransformMakeScale(1, 0.8);

        var constraintHeight = NSLayoutConstraint(item: listPickerView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)

        self.view.addConstraint(constraintHeight)

        datePickerView.transform = CGAffineTransformMakeScale(1, 0.8);

        var constraintHeightDate = NSLayoutConstraint(item: datePickerView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)

        self.view.addConstraint(constraintHeightDate)

    }
}
Helen Wood
  • 1,814
  • 2
  • 23
  • 39
1

Swift 5.0 code for Pavel Alexeev's solution., accounting for some syntax updates and also screen width because I've found that if the device is being held in the landscape orientation when the app is launched, the screen height is not the actual portrait height, but the current landscape height. So, I check that the width is accounted for, too. If the height is less than 660 AND the width is less than 375, we have a portrait SE or 5s.

extension NSLayoutConstraint
{
    //We use a simple inspectable to allow us to set a value for iphoneSE / 5s.
    @IBInspectable var iPhoneSE_PortraitConstant: CGFloat
        {
        set{
            //Only apply value to iphone SE and 5s devices.
             if (UIScreen.main.bounds.size.height < 660 && UIScreen.main.bounds.size.width < 330)
            {
                self.constant = newValue;
            }
        }
        get
        {
            return self.constant;
        }
    }
}
Rillieux
  • 267
  • 3
  • 15