iOS Constrainsts: how to adjust UIButton width and height in a view? - ios

I'm trying to make a keyboard. I have 9 buttons inside a view with: width and height <= 75. On iPhone 5 works perfectly. But the iPhone 4 buttons are stretched and the size is still 75. Could anyone help me?

The problem is that your telling the views (buttons) to have a height or width >= 75 which means you have an ambiguous layout (someone already mentioned this) - you can check for this by examining the hasAmbiguousLayout property of your view. It's likely not working on the iPhone 5 correctly either it just so happens that when you've run it autolayout has found the solution you're looking for so it appears to work. Run it enough times and eventually you'll probably get the undesired layout. Ah the joys of autolayout. Anyways one solution to this problem is to set the height and width of one button, and then tell all the other buttons to follow suite. The visual format language guide has an example of this but i'll show you what I mean.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[button1(75)[button2(==button1)[button3(==button1)]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(button1, button2, button3)]];
Then do something similar to lay them out vertically. When autolayout runs it should find the correct layout for them. The next problem comes in how you use it. If you did all this work in a subclass of UIView and then went ahead and made that views width something crazy like 400 pt wide then autolayout will break - in this case because we pinned to the left and right sides. To fix this problem I'd probably remove the the last | and not pin the right side of button3 to the right side of the superview.
The other option you have is to specify constrains using the long format. For Example
//set button1 width to 75
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:button1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:75]];
//set button2 width == button1
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:button2
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:button1
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
//pin button1 to the left
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:button1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1
constant:0]];
//pin button2 to button1
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:button2
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:button1
attribute:NSLayoutAttributeRight
multiplier:1
constant:0]];

Have you considered changing the height programmatically? Here is a stub of code I use.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
//iPad Code
}else{
if([[UIScreen mainScreen] bounds].size.height > 560){
//iPhone 5 Code
}else{
// iPhone 4 Code
}
}

First of all your basic design should be iPhone 4.
It will be much easier to increase the screen design than to shrink it.
Now, regarding your question, it depends on what you want to achieve.
(1) Do you want only the top and the bottom padding to change in different devices?
(2) Do you want the buttons to have different sizes in different devices?
(3) Do you want the spaces between the buttons to be different in different devices?
Anyway, you should set a constraints for equal width and equal height for all the buttons.
From now on, the easiest solution (1) is to put all the buttons in one container that will have a constant height and will be centered in the screen.
This way there will be lower or higher padding around the buttons on different devices.
If you want the spaces between the buttons to change (2) then you can create a matrix of views (imagine sudoku), make all the views have equal widths and heights (constraints) and zero space between them (also constraints), put a button in the center of each view (constraints).
This way the buttons will remain in the same size but the spaces between them will grow or shrink.
Let me know if you understand the above solutions.

Related

Place middle of button at the bottom of phone screen (regardless of the screen size)

I would like to ask how can one set a constraint such that the middle of a button is located at the bottom of the screen for different iOS screen size?
This is the ideal case where middle of button is placed at the bottom of the screen, and the bottom half is not shown on screen:
With the below code, this is what's happening, which is not what I wanted:
I have tried this:
[qrScanner.bottomAnchor constraintEqualToAnchor:self.bottomLayoutGuide.topAnchor constant:100].active = YES;
but it only works on iPhone 6 screen, not on other screen, such as iPad Mini.
May I know if there's any way that can generalise such formula so that all centre of buttons are nicely placed at the bottom of the screen regardless of screen size?
Please help, I have tried for days and searched everywhere but cannot find a single clue. Thanks!
[qrScanner.centerYAnchor constraintEqualToAnchor:self.bottomLayoutGuide.topAnchor constant:0].active = YES;
Based off what your mockups look like, I'm guessing that your button does not have a fixed size, but rather it grows/shrinks depending on the size of the container. Because of this, the offset of 100 only works when your button happens to have a height of 200.
I'm not sure how you determine the size of the button, but as far as positioning goes, you want to center its X coordinate, and then set a bottom constraint where the button's center Y val is equal to the container's bottom. These are the 2 constraints you will need to add for positioning:
// Center X value in the view
[NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
// Center button's Y value to the bottom of the view
[NSLayoutConstraint constraintWithItem:button
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1
constant:0];
Swift 4.2
qrScanner.centerYAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

detailCalloutAccessoryView constraints in MKAnnotationView

i have some problems sizing a detailCalloutAccessoryView that i added programmatically.
Here's the code for the view
HCSStarRatingView *annotationRating = [[HCSStarRatingView alloc] init];
annotationView.detailCalloutAccessoryView = annotationRating;
I tried to init the view with a initWithFrame but somehow that didn't work and i ended up with this.
I then discovered that i have to add NSLayoutConstraint programmatically to size the view correctly, so i added this code for constraints.
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:annotationRating attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:80];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:annotationRating attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:45];
[annotationRating addConstraint:width];
[annotationRating addConstraint:height];
And the view now looks like this
Now i want to get rid of the white space around it. I think i have to add a top and bottom constraint but i don't know how to do it because I don't know what items i have to relate to.
The excess whitespace is a function of the height that you've chosen. Your image is roughly 5 times as wide as it is tall, but you've asked to render it in a box that 80 x 45 pts (i.e. a view whose height is over half the width, rather than one fifth). If you pick the dimensions of the image view to match (adjusting for scale) the size of the image, you get something more like:
As you can see, with judicious selection of the width and height, there will be less whitespace than in your example. Note, there is some inherent whitespace between the detail accessory view that you cannot control, but by making sure you set the width and height correctly, you can reduce it to these minimal values.

Autolayout metrics differs between iOS 7 & 8 for UITableViewCells

I have a custom UITableViewCell that contains only one UILabel that holds a static text, I wrote two Autolayout constraints in code (using the regular way but not visual format) to pin the leading and top edges of the label to the container view of the cell, and it works on both iOS 7 and iOS 8 devices but with a weird problem in iOS 8: the top edge of the label is 12 pixels far from the top edge of the container view (and this is the correct and expected metrics) but in iOS 8 is more than that, about 20 pixels, and the same shifting happens for the leading edge of the label.
what does this means ? is that related to UIKit changes between iOS 7 and iOS 8 for UITableViewCells which now contains a hidden scrollView in its hierarchy ?
p.s. the constraints are :
constraint = [NSLayoutConstraint constraintWithItem:self.myLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier: 1.0 constant:12];
[self.contentView addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.myLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:12];
[self.contentView addConstraint:constraint];
any help would be highly appreciated ...
In my case I needed to add a specific constraints to an imageview manually at runtime, the result was a different Leading alignment in iOS7 (completely attached to the left side of the cell) and iOS8 (correctly aligned). The issue was not only in the cell but even in the UITableView constraints:
1) check your tableview leading and trailing constraints, must be like those ones:
Both for Leading and Trailing, sometimes when you add manually a table view to the view controller the natural way to align the tableview's left and right sides is set it to -16, that works well for iOS 8 but not for iOS 7.
2) If you amend/add some constraints check if the cell respond to setLayoutMargins (only iOS8) and in case change the constraint's constant value:
CGFloat leftMargin = 8.0;
if ([self respondsToSelector:#selector(setLayoutMargins:)]) {
leftMargin = 0.0;
}
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.userImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeadingMargin multiplier:1 constant:leftMargin]];

Constraints not working with a UITextView

I have a View controller, in which the view has two image views and two text views. I turned off auto layout, and I programmatically set the distance between the first text view and the first image view by using this code:
The following code is in the viewDidLoad method of my custom view controller class. I have set the autoresizing mask to no in both cases, so I have no idea why the code doesn't work.
(tf2_logo is the image view and itemName is the text view)
self.tf2_logo.translatesAutoresizingMaskIntoConstraints = NO;
[self.backpackBackground addConstraint:[NSLayoutConstraint constraintWithItem:self.itemName attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.tf2_logo attribute:NSLayoutAttributeTop multiplier:1.0 constant:-1.0]];
[self.backpackBackground addConstraint:[NSLayoutConstraint constraintWithItem:self.tf2_logo attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.backpackBackground attribute:NSLayoutAttributeLeft multiplier:1.0 constant:17]];
Now I want to do the same thing with my other text view, basically I wanted to keep the distance between the itemName text view and the text view at a certain distance. I used this code:
(tf2 is my other text view)
self.tf2.translatesAutoresizingMaskIntoConstraints = NO;
[self.backpackBackground addConstraint:[NSLayoutConstraint constraintWithItem:self.itemName attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.tf2 attribute:NSLayoutAttributeTop multiplier:1.0 constant:-3.0]];
[self.backpackBackground addConstraint:[NSLayoutConstraint constraintWithItem:self.tf2 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.tf2_logo attribute:NSLayoutAttributeRight multiplier:1.0 constant:20]];
After implementing this code, the tf2 text view doesn't even show up in the view controller. What is the problem?
EDIT: You can download the whole project here: https://www.dropbox.com/sh/u820u2ndyrncuz8/P4atI-9CAx
EDIT#2:
You mentioned that you turned off auto layout, because UITextView has that little gap on top in iOS7. To remove the gap, try this:
self.tf1.textContainerInset = UIEdgeInsetsZero;
When you log the original value of the textContainerInset it shows: {8, 0, 8, 0} . The two 8's are responsible for the gap (one at the top). The line above sets all values to zero and the content is nicely aligned to the top of the frame.
(EDIT#1: Completely changed the answer)
I assume you primarily want to have a flexible height of the imageName UITextView. First I suggest to use auto layout. You can set constraints in Xcode according to the following image:
The red lines are the constraints. The green line is special: It shall be a height constraint and you create an outlet for it in the view controller. (Open the document outline view, locate the height constraint in the tree and control-drag it to the code.)
Then in the viewDidLoad method:
CGSize size = [self.tf1 sizeThatFits:self.tf1.frame.size];
self.tf1Height.constant = size.height;
The height of the "lore ipsum" field now adjusts to its content.
Have you tried using frames instead of constraints? If your not using autolayout I think frames might be easier to read/implement.
sample:
// tf2 will be placed at (0,0) in superview and have width of 100 and height of 20
tf2.frame = CGRectMake(0, 0, 100, 20);
you can play around with different values to get your layout as desired.

Using Autolayout in IB how do I position a UIView in the center of the screen programmatically?

I have a UIView that I would like to center horizontally over the main view and then center vertically over the main view, minus about 14 pixels.
If I set it up with IB it works on the Retina 3.5, but when running on Retina 4 it is of course off by roughly 40 pixels.
I am thinking the best solution is establishing these constraints programmatically based on screen height?
Use the centerX and centerY constants, and put in a constant of 14 (or -14 not sure which way you want it to be),
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:14]];
In order to make your view in Center(Horizontally & Vertically). Select the view in Interface Builder, then from Top Menu, Select Editor> Align> Horizontal Center in Container/ Vertical Center in Container.
You can also add more constraints to satisfy view's Geometry.
You don't need to set constraints programmatically to align a view to horizontal & vertical center of the screen. It can be done via Interface builder itself. However, you still can set constraints using NSLayoutAnchor apis in code, like the below example. NSLayoutAnchor apis are simple, elegant and, concise. And are available since iOS 9.
myView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
myView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)

Resources