iOS swift Autolayout constraint for iPhone 4s [duplicate] - ios

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.

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:
#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

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.

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;
}
}
}

#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)
}
}

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;
}
}
}

Related

UIImageView size relative to screen size

I have an UIImageView, with an image like so
imageView.image = UIImage(named: "imageName")
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
On the iPhone X this image looks great with 140x140 dimensions
Maybe needless to say, this does not look great on the iPhone5s, since 140x140 would take up too much of screen space
How can I correctly scale my image for other screen dimensions, what would be the best practice for this?
As for now, I set the image height and width to
let size = UIScreen.main.bounds.height / 5.5
imageView.heightAnchor.constraint(equalToConstant: size),
imageView.widthAnchor.constraint(equalToConstant: size),
Since that will be pretty close to what I am after. But I can't shake the feeling that there are better alternatives than this
The question is a bit broad like "What do you think guys?", but still.
It's better to change your size accordingly to your parent view, not screen.
imageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2, constant: 0)
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 1.0, constant: 0)

Size class to identify iPhone 6 and iPhone 6 plus portrait

How to uniquely identify iPhone 6 and iPhone 6 plus portrait screens using size classes?
My App looks good in iPhone 4 and iPhone 5 but the same looks with lots of empty spaces in iPhone 6 and 6 plus because of screen sizes. Though am using auto layout i can't increase the font size or view size only for iPhone 6 and 6 plus alone. I knew that we can change the font size and view size using size classes. but in my case don't know what to do.
Am using xCode 6.1 and my app supports from iOS 7 to latest iOS 8.1. Am expecting solution only in storyboards as am doing my UI designs fully in storyboard. If storyboard has limited functionality to achieve my needs please let me know how to achieve the same with code through out the app?
Another option to adjust the font size according to the iPhone type, is to use 'User Defined Runtime Attributes'.
Define an extension to UILabel:
extension UILabel {
var adjustFontToRealIPhoneSize: Bool {
set {
if newValue {
var currentFont = self.font
var sizeScale: CGFloat = 1
let model = UIDevice.CurrentDevice().modelName()
if model == "iPhone 6" {
sizeScale = 1.3
}
else if model == "iPhone 6 Plus" {
sizeScale = 1.5
}
self.font = currentFont.fontWithSize(currentFont.pointSize * sizeScale)
}
}
get {
return false
}
}
}
In order to determine the current model name, please refer to the following answer: https://stackoverflow.com/a/26962452/4165128
On the storyboard, select the label you wish to adjust, open the right pane, select the identity inspector, and add the 'adjustFontToRealIPhoneSize' property to the list of user defined runtime attributes (with type 'Boolean' and checkbox checked).
Do the same for each label you wish to adjust (copy & paste surprisingly works here).
Use Compact width and Regular Height in storyboard
Add layout constraint of hight and width relative with super view by adding multiplier.
Let's say you have image view which has size half the super view then add multiplier 0.5.
Check out adjustsFontSizeToFitWidth # UILabel Class Reference. This will allow you to do some nice adjustments based on the different devices.
label.adjustsFontSizeToFitWidth = YES;
I don't have too much idea of sizeClass for different font size in different iPhone devices but I figured out with this solution.
Add this method to your utility class.
Just pass your super view to this method, this method is recursive so if you pass self.view than all subviews are set.
Change your font size as you need.
-(void)setAllFonts:(UIView *)view
{
CGFloat fontSizeDiff = 0.0;
if (IS_IPHONE_6)
{
fontSizeDiff = 1;
}
else if (IS_IPHONE_6PLUS)
{
fontSizeDiff = 2;
}
for (UIView *vw in [view subviews])
{
if ([vw isKindOfClass:[UILabel class]] || [vw isKindOfClass:[UIButton class]])
{
if ([vw isKindOfClass:[UILabel class]])
{
UIFont *font = [(UILabel *)vw font];
[(UILabel *)vw setFont:[UIFont fontWithName:font.fontName size:font.pointSize+fontSizeDiff]];
}
else
{
UIFont *font = [(UIButton *)vw titleLabel].font;
[(UIButton *)vw titleLabel].font = [UIFont fontWithName:font.fontName size:font.pointSize+fontSizeDiff];
}
}
else if ([vw isKindOfClass:[UIView class]] || [vw isKindOfClass:[UIScrollView class]])
{
[self setAllFonts:vw];
}
}
}
Swift Version of #iBhaviks answer :
func getFontScaleForDevice() -> CGFloat {
var sizeScale = CGFloat(1.0)
if DeviceType.IS_IPHONE_6 {
sizeScale = 1.2
} else if DeviceType.IS_IPHONE_6P {
sizeScale = 1.4
} else if DeviceType.IS_IPAD {
sizeScale = 1.4
}
return sizeScale
}
func setAllFonts(targetView:UIView, scale:CGFloat) {
for vw in targetView.subviews {
if let vl = vw as? UILabel {
vl.font = vl.font.fontWithSize(round(vl.font.pointSize * scale))
} else if let vb = vw as? UIButton, vbl = vb.titleLabel {
vbl.font = vbl.font.fontWithSize(vbl.font.pointSize * scale)
} else if vw.subviews.count > 0 {
setAllFonts(vw, scale: scale)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let sizeScale = getFontScaleForDevice()
if sizeScale > CGFloat(1.0) {
setAllFonts(view, scale: sizeScale)
}
view.layoutIfNeeded()
}
Maybe you could set the perfect font size for the big screens, and then set the Autoshrink to minimum font size with the perfect size for the small screens, in that way you can have a dynamic font size without coding.
You will have to set the constraints for the label to adjust its size with the screen size anyway.
Hope this help
After struggling lot just found of something for my need so posting the same for the future readers.
As of now there is no way to uniquely identify the iPhone 6 model portraits using Size classes. However you can use compact width and regular height to design for all iPhones portrait screens
To change font size you have to identify which iPhone the app currently running on using code and set the font size based on the same
For my requirement - same layout for all the screen sizes - use multiplier in widths and heights constrains. Check Jignesh answers for this question to know more about it.
EDIT 24-SEP-2015
I recently found a way to customize your size class by using UITraitColleection only for iPhone 6 plus so you don't need to write much of code. i hope this link will help someone in future.
Bit late to this but I needed a label that would scale up to any device and used this method.
Create a new subclass of UILabel and in the .h file put this…
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface OTHD_ScalableLabel : UILabel
#property (nonatomic, assign) IBInspectable CGFloat fontScale;
#end
and in the .m file put this…
#import "OTHD_ScalableLabel.h"
#implementation OTHD_ScalableLabel
- (void) layoutSubviews
{
[super layoutSubviews];
if( self.fontScale < 0.1 || self.fontScale > 1.0 ) self.fontScale = 1.0;
CGFloat height = CGRectGetHeight(self.bounds) * self.fontScale;
if( height ) self.font = [UIFont fontWithName:self.font.fontName size:height];
}
#end
You can then just change your class in IB and using IBInspectable you will be able to scale the label up or down. Obviously, for the pedantics out there, this is not a good idea for general use but there are some cases where you might need, for example, a large label that displays full screen on an iPhone as well as full screen on an iPad.
Here is an example of a function that I use to toggle between two different font size at runtime. It decides which font to use based on the horizontal size class - which essentially splits devices into two groups "iPad" and "iPhone". Here is a good refernce on whch devices belong to which size classes: http://useyourloaf.com/blog/size-classes/
iPad and Up
iPhone Plus and Down
func chooseFont(compactFont: UIFont, regularFont: UIFont) -> UIFont {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.window!.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.compact ? compactFont : regularFont
}
Based on your screen shot I would suggest using Dynamic Type. That way the user is in charge of the size of the text.

How to specify size for iPhone 6/7 customised edge-to-edge image?

Say I want a bundled image to take up all available screen width in an iPhone app - for example a banner. I'd create my_banner.png with width 320px, my_banner#2x.png with width 640px and my_banner#3x.png for iPhone 6 plus with width 1242px. But the resolution of iPhone 6 is 750×1334 pixels. Still it shares the #2x suffix with iPhone 4 and 5 that have 640px width.
What's the recommended way or a good way to specify an image file that has been optimised for the 750px width of iPhone 6? Seems like it cannot be done in an asset catalog? Should it be done programatically? Is there some other suffix that can be used for iPhone 6?
(Image extracted from http://www.iphoneresolution.com)
It seems to me that a lot of these answers want to address how to constrain the imageView, where I think you are concerned with loading the correct media file? I would come up with my own future extensible solution, something like this:
"UIImage+DeviceSpecificMedia.h" - (a category on UIImage)
Interface:
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, thisDeviceClass) {
thisDeviceClass_iPhone,
thisDeviceClass_iPhoneRetina,
thisDeviceClass_iPhone5,
thisDeviceClass_iPhone6,
thisDeviceClass_iPhone6plus,
// we can add new devices when we become aware of them
thisDeviceClass_iPad,
thisDeviceClass_iPadRetina,
thisDeviceClass_unknown
};
thisDeviceClass currentDeviceClass();
#interface UIImage (DeviceSpecificMedia)
+ (instancetype )imageForDeviceWithName:(NSString *)fileName;
#end
Implementation:
#import "UIImage+DeviceSpecificMedia.h"
thisDeviceClass currentDeviceClass() {
CGFloat greaterPixelDimension = (CGFloat) fmaxf(((float)[[UIScreen mainScreen]bounds].size.height),
((float)[[UIScreen mainScreen]bounds].size.width));
switch ((NSInteger)greaterPixelDimension) {
case 480:
return (( [[UIScreen mainScreen]scale] > 1.0) ? thisDeviceClass_iPhoneRetina : thisDeviceClass_iPhone );
break;
case 568:
return thisDeviceClass_iPhone5;
break;
case 667:
return thisDeviceClass_iPhone6;
break;
case 736:
return thisDeviceClass_iPhone6plus;
break;
case 1024:
return (( [[UIScreen mainScreen]scale] > 1.0) ? thisDeviceClass_iPadRetina : thisDeviceClass_iPad );
break;
default:
return thisDeviceClass_unknown;
break;
}
}
#implementation UIImage (deviceSpecificMedia)
+ (NSString *)magicSuffixForDevice
{
switch (currentDeviceClass()) {
case thisDeviceClass_iPhone:
return #"";
break;
case thisDeviceClass_iPhoneRetina:
return #"#2x";
break;
case thisDeviceClass_iPhone5:
return #"-568h#2x";
break;
case thisDeviceClass_iPhone6:
return #"-667h#2x"; //or some other arbitrary string..
break;
case thisDeviceClass_iPhone6plus:
return #"-736h#3x";
break;
case thisDeviceClass_iPad:
return #"~ipad";
break;
case thisDeviceClass_iPadRetina:
return #"~ipad#2x";
break;
case thisDeviceClass_unknown:
default:
return #"";
break;
}
}
+ (instancetype )imageForDeviceWithName:(NSString *)fileName
{
UIImage *result = nil;
NSString *nameWithSuffix = [fileName stringByAppendingString:[UIImage magicSuffixForDevice]];
result = [UIImage imageNamed:nameWithSuffix];
if (!result) {
result = [UIImage imageNamed:fileName];
}
return result;
}
#end
I am using the following trick as some stuff actually works:
Asset Catalog for specific devices
Specify images for 1x, 2x on the base of 320x640
Specify images for 4 2x and 3x on the base of 320x568 (iPhone 5)
Create a new Images set for the iPhone 6 specifically (as this is the only device that makes trouble with edge to edge bindings)
Only provide 2x image for iPhone 6 in full resolution (750x1334)
Declare a constant
#define IS_IPHONE_6 [[UIScreen mainScreen]nativeBounds].size.width == 750.0 ? true : false
and use it like this:
UIImage *image = [UIImage imageNamed:#"Default_Image_Name"];
if(IS_IPHONE_^) {
image = [UIImage imageNamed:#"Iphone6_Image_Name"];
}
this might be not the most beautiful solution, but it works, at least as long as apple does not provide a better API for edge to edge bindings.
Auto Layout is supposed to help with this situation..
Now tell me #Nicklas Berglund what would you do if the device rotates? Lets say you are in landscape mode now.. How would you fill the Horizontal space which is not in the image assets any more?
Just food for thoughts.. Auto Layout supposed to take care of your screen no matter which orientation, or which device you are running your app on..
Maybe Apple should start targeting device orientations in image assets in future?
Lets go back to your question.. The solution is to replace your #2x images with 750px wide images and then have Auto Layout do its job. Oh yea, this is the tricky part..
If you just add constraints to fit it, it will squeeze it horizontally when displayed in 4" screen, but you can use multipliers to scale the image appropriately. Here's how you can do it:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageFooterView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageFooterView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[imageFooterView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(imageFooterView)]];
float aspectRatio = imageFooterView.frame.size.height/imageFooterView.frame.size.width;
[imageFooterView addConstraint:[NSLayoutConstraint constraintWithItem:imageFooterView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:imageFooterView attribute:NSLayoutAttributeWidth multiplier:aspectRatio constant:0.0f]];
I couldn't find a way to do it either, as I had a background Image that was perfectly sized with the Asset Catalog on every device except the iPhone 6. My fix (I did this in SpriteKit)?
if (bgNode.size.width != self.frame.size.width) {
bgNode.texture = [SKTexture textureWithImageNamed:#"i6bg.png"];
[bgNode runAction:[SKAction scaleXTo:self.frame.size.width/bgNode.size.width y:self.frame.size.width/bgNode.size.height duration:.1]];
}
bgNode is the background image that is pulled up by the device. If it's an iPhone 6, it won't fit the screen and so the background image width wont be the same as the screen width. When the device is recognized as an iPhone 6, I change the texture to the R4 texture (the #2x for retina) and scale it to the correct dimensions.
I tried doing the same with the regular #2x image, but the scaled image looked very bad (it was too stretched out and noticable). With the R4 texture scaled, the proportions of width/height are a bit better and so the change isn't even noticeable. I hope this gives you some idea as to what you can do before Apple adds an iPhone 6 Asset.
Hope this will solve all your issues related to customised edge to edge image.
Xcode 6 - xcassets for universal image support
Make sure if you are using auto layout then check pin is set to zero for all edges and constraints to margin is un checked.
You can also visit this links for launch screen images:
http://www.paintcodeapp.com/news/iphone-6-screens-demystified
http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
I raised the same question to apple technical support and they confirm that for fullscreen image it can't be done in asset catalog: "Currently, there is no way for the Asset Catalog to load device specific images. If your app needs to support device specific images you will need to implement your own code to detect the screen size and choose the appropriate image. You can file an enhancement request by using the following link. Be sure to explain your use case for this feature. "
I checked the naming convention of a launch image generated from an asset catalog via Xcode 6 and the landscape version for iPhone 6+, for example, had: LaunchImage-Landscape-736h#3x.png
Based on that, I'd presume it would be as follows, for retina devices, assuming a base file desert.png:
desert#2x : iPhone 4s (320 x 420)
desert-568h#2x : iPhones 5, 5C and 5S (320 x 568)
desert-667h#2x : iPhone 6 (375 x 667)
desert-736h#3x : iPhone 6+ (414 x 736)
desert#2x~ipad : iPad (1024 x 768)
There is no native Assets support for this case, so I think it would be better to do it manually as working with undocumented file names may break easily in the future.
Just measure the device dimensions and call the image that you want. ie Do it programatically
So in your appdelegate have globals
deviceHeight = self.window.frame.size.height;
deviceWidth = self.window.frame.size.width;
that you can call repeatedly.
Then check them and call the appropriate image
if (deviceWidth == 640){
image = IPHONE4IMAGE;
deviceString = #"iPhone4";
}
else...
In my case, I was interested in making my base view controller subclass have the same background image as my launch image.
NOTE: This approach will not work unless this is your specific requirement.
Also, even when I tried creating a background image that was the correct size for the iPhone 6 (750x1334), loading that image as a pattern image into a background color for a view ended up scaling the image up in an undesirable way.
This answer gave me the code that I needed to figure out a good solution for me.
Here's the code I got working to have my launch image match my UIViewController's background image (or vice versa):
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *background = [UIImage imageNamed:[self splashImageName]];
UIColor *backgroundColor = [UIColor colorWithPatternImage:background];
self.view.backgroundColor = backgroundColor;
}
- (NSString *)splashImageName {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGSize viewSize = self.view.bounds.size;
NSString *viewOrientation = #"Portrait";
if (UIDeviceOrientationIsLandscape(orientation)) {
viewSize = CGSizeMake(viewSize.height, viewSize.width);
viewOrientation = #"Landscape";
}
NSArray *imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"UILaunchImages"];
for (NSDictionary *dict in imagesDict) {
CGSize imageSize = CGSizeFromString(dict[#"UILaunchImageSize"]);
if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[#"UILaunchImageOrientation"]])
return dict[#"UILaunchImageName"];
}
return nil;
}
Please try this class to change the image name programmatically.
import UIKit
class AGTools: NSObject {
class func fullWidthImage(imageName: String!) -> String!{
let screenWidth = UIScreen.mainScreen().bounds.size.width
switch (screenWidth){
case 320:
// scale 2x or 1x
return (UIScreen.mainScreen().scale > 1.0) ? "\(imageName)#2x" : imageName
case 375:
return "\(imageName)-375w#2x"
case 414:
return "\(imageName)-414w#3x"
default:
return imageName
}
}
}
use this method like this.
_imgTest.image = UIImage(named: AGTools.fullWidthImage("imageName"))
FIRST of all, you need to configure your imageView to cover all the screen, Autolayout will help a lot for this job, take a look on the link below and find how to Pin the constraints (Leading Space, Trailing Space, Top Space and Bottom Space) using Storyboards:
http://www.thinkandbuild.it/learn-to-love-auto-layout/
SECOND step is create device specific image sets on your image assets (image below), to display different images according to device.
Check out this infographic:
http://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
It explains the differences between old iPhones, iPhone 6 and iPhone 6 Plus. You can see comparison of screen sizes in points, rendered pixels and physical pixels
That's all
Please, give a feedback if you have any trouble.

Trying to detect iPhone 6 Plus in landscape orientation

I have a today widget for my app and I have been setting the layout of objects in code (Not using auto-layout) however, I need to detect when a user is using an iPhone 6 Plus. The reason for this is because in notification centre on the 6 Plus, there is a border around the edge which cuts off the labels in my widget (in landscape).
The problem is that I have been having issues trying to figure out what height value the screen is in landscape.
This is the code I have right now:
var height = UIScreen.mainScreen().bounds.size.height
if UIDevice.currentDevice().orientation.isLandscape && height == 414 {
totalBudgetDisplay.frame.origin.x = UIScreen.mainScreen().bounds.width - 250
currentBudgetDisplay.frame.origin.x = UIScreen.mainScreen().bounds.width - 250
budgetNameDisplay.frame.origin.x = 80
warningLabel.center = view.center
button.frame = CGRectMake(0, 0, view.bounds.width, view.bounds.height)
}
Is this the correct value for the screen height in landscape? If so, why are the labels still not moving?
Your code is good but UIDevice.currentDevice().orientation.isLandscape just does not get you correct value.
Seems like there is no bullet-proof solution to detect device orientation for Extensions.
Look at this answer for possible workaround - https://stackoverflow.com/a/26023538/2797141

Maximum height of iOS 8 Today Extension?

I am working on a Today Extension with a dynamically sized table. I have been able to get the table to resize for the content using:
self.preferredContentSize = accountsTable.contentSize
However, I have found that it will not get taller than a certain size (568 px) even though I can tell the table contentSize is larger.
I'm not clear if this is a built-in limit or if there is a way around this to make a larger view. It appears that some previous extensions (Stocks widget) is able to become larger.
Anyone else running into the same behavior. Anyone know if it's possible to make an extension appear larger either immediately or using a "Show All" button like the Stock widget?
I made some tests and you can calculate the maximum height of your Today Extension with this formular:
for iPhone:
float maxHeight = [[ UIScreen mainScreen ] bounds ].size.height - 126;
for iPad:
float maxHeight = [[ UIScreen mainScreen ] bounds ].size.height - 171;
This should work for alle Screen sizes...
Ok, although you can assign to the preferredContentSize any CGSize it is reduced to max height and width.
Device Orientation (max width, max height):
iPhone 5S Portrait (272, 441.5)
iPhone 5S Landscape (520, 205.5)
iPhone 6 Portrait (327, 540.5)
iPhone 6 Landscape (586, 260.5)
iPhone 6 Plus Portrait (362, 610)
iPhone 6 Plus Landscape (585, 288)
iPad Mini Portrait (535, 853)
iPad Mini Landscape (535, 597)
iPad Portrait (711, 985)
iPad Landscape (967, 729)
How did I get these values?
#IBOutlet weak var sizerView: UIView!
inside viewDidLoad:
preferredContentSize = CGSizeMake(0, 2000)
dispatch_after(1, dispatch_get_main_queue(), { //it needs time to render itself
label.text = NSStringFromCGSize(self.sizerView.bounds.size) //here you have MAX VALUES
}
It seems, that iOS is adding an UIView-Encapsulated-Layout-Height constraint to your own constraints:
(
"<NSLayoutConstraint:0x610000280640 V:[_UILayoutGuide:0x7f92d0513810]-(500)-[UILabel:0x7f92d040cb20'Hello World']>",
"<NSLayoutConstraint:0x610000280690 V:[UILabel:0x7f92d040cb20'Hello World']-(500)-[_UILayoutGuide:0x7f92d0515100]>",
"<_UILayoutSupportConstraint:0x6100002a2f40 V:[_UILayoutGuide:0x7f92d0513810(0)]>",
"<_UILayoutSupportConstraint:0x6100002a2ee0 V:|-(0)-[_UILayoutGuide:0x7f92d0513810] (Names: '|':UIView:0x7f92d040c810 )>",
"<_UILayoutSupportConstraint:0x6100002a3000 V:[_UILayoutGuide:0x7f92d0515100(0)]>",
"<_UILayoutSupportConstraint:0x6100002a2fa0 _UILayoutGuide:0x7f92d0515100.bottom == UIView:0x7f92d040c810.bottom>",
"<NSLayoutConstraint:0x60800009e780 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7f92d040c810(628)]>"
)
This constraint forces the widget to have the maximum height. You can get it's value like this:
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
for (NSLayoutConstraint *constraint in self.view.constraints) {
if ([constraint.identifier isEqualToString:#"UIView-Encapsulated-Layout-Height"]) {
NSLog(#"Height: %#", #(constraint.constant));
}
}
completionHandler(NCUpdateResultNewData);
}
To get the max height of the widgets active display mode, do this in your UIViewController:
let context = self.extensionContext
if let context = context{
let height = context.widgetMaximumSize(for: context.widgetActiveDisplayMode).height
}
To get the max height of expanded and compact individually regardless of the current display mode, do this:
var context = self.extensionContext
if let context = context{
let compactHeight = context.widgetMaximumSize(for: .compact).height
let expandedHeight = context.widgetMaximumSize(for: .expanded).height
}
The recommended way is to use auto-layout constraints to constrain your view's height.
It seems the 568px is just the maxheight of your iPhone 5 and not the actual size. As far as I could figure out there is no way to get the real size.

Resources