Here is my problem, I have a scroll view scrollExerciseIndex that I use only as a scrolling bar, in this scroll view I place a UIView indexesView and I want it to be always at the center of the scroll view. For this I use layout constraints :
UIView * indexesView = [[UIView alloc] initWithFrame: CGRectMake(xPosition, 0, dimension*numberIndexes, dimension)];
[self.scrollExerciseIndex addSubview:indexesView];
[self.scrollExerciseIndex setContentSize:CGSizeMake(dimension*numberIndexes, dimension)];
if (xPosition != 0) {
NSLayoutConstraint * xCenterConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.scrollExerciseIndex attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
[self.scrollExerciseIndex addConstraint:xCenterConstraint];
}
Here is the expected result :
Don't pay attention to all the element, just the bar at the bottom of the screen is my problem.
I have to create view programmatically because sometimes I will activate the constraints, sometimes not and I have to set the frame of the view dynamically. So for now I initialise the view indexesView like so :
UIView * indexesView = [[UIView alloc] initWithFrame: CGRectMake(xPosition, 0, dimension*numberIndexes, dimension)];
(I know, not very original)
I would like to know if there is a way to initialize the view programmatically but to say to auto-layout that it has no constraints on the position because right now if the screen turns in landscape mode there is a conflict as the scrollview's frame changes so the distance between the center of the scroll view (on which I set a constraint) and the position of the subview's frame (xPosition) is no longer the same.
As you can see, the view is no longer at the center of the scroll view and I have some constraints broken.
Will attempt to recover by breaking constraint
NSLayoutConstraint:0x7bed6c50 UIView:0x7bed6ad0.centerX == UIScrollView:0x7e273200.centerX
Thanks for your help.
Ok, I found what I was looking for by reading a book about Audio-Layout.
My problem was that audio layout would create constraints behind my back automatically. When using AutoLayout a type of constraints is created from non-autoLayout specifications (The used to describe interface when auto layout didn't exist). So constraints are created using the initial frame of the view. The only thing I had to do was :
[indexesView setTranslatesAutoresizingMaskIntoConstraints:NO];
to disable this creation of constraints from the frame, and then recreate explicitly the constraints for width and height if needed (which wasn't the case for me, but I still made the test) like so :
`NSLayoutConstraint * widthConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0 constant:widthValue];
NSLayoutConstraint * heightConstraint = [NSLayoutConstraint constraintWithItem:indexesView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0 constant:heightValue];
[indexesView addConstraint: heightConstraint];
[indexesView addConstraint: widthConstraint];`
When adding constraints programmatically, don't forget to call : [indexesView setNeedsUpdateConstraints]; so the constraints are recalculated only when needed.
Last info that I read and can be useful in general, when adding a lot of constraints, the apple doc specifies that it is more efficient to use the method :
[myView addConstraints:(NSArray<NSLayoutConstraints *> *)] than to call addConstraint: for each constraint.
Hope it can be useful to someone.
Related
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.
I have a view in an iPad app that on full screen mode expands to fill the entire screen and is available both in portrait and landscape.
This view also contains many subviews (like rectangular cards) and a scrollview.
[ I programmatically add these to a content view, that is created programmatically (in a scroll view) calculating frame size of the cards accordingly ]
contentView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 640, 2000)];
[contentView addSubview:firstCard];
self.formScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.formScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.formScrollView.scrollEnabled=true;
self.scrollView addSubview:contentView];
self.scrollView.contentSize=contentView.frame.size;
My requirement is that despite orientation all the cards would be aligned one after the other with equal spaces from both the left and right margin.
The cards though of varying height are all equal in width.
So far, this is what I have tried to set them centered horizontally. I added this code snippet in the viewDidLayoutSubviews
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstCard
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
where contentView refers to the contentView in the full screen view controller and first card is the first subview card
I also set the property
firstCard.translatesAutoresizingMaskIntoConstraints = NO;
On running in the simulator, I get disastrous results. The view is weirdly offset and the other cards are all scattered everywhere else.
This does not happen when I do not set the constraints. They look ugly but they are still vertically aligned the way they have to be.
I am quite new to auto layout and iOS development.
I do wonder if any of these problems have to do with the fact that my contentView is a fixed frame?
I keep the contentView with such a fixed frame keeping in mind, the non full screen mode.
Where am I going wrong? I would really appreciate any help and advice.
Thanks.
EDIT :-
I also tried aligning my content view such that it remains centered to the scroll view. Here's code for that...
(That was also a disaster, the content view doesn't even appear on screen)
[self.formScrollView addConstraint:[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.formScrollView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
}
[where formScrollView is the scroll View ]
You will also have to set the vertical constraint for card views. In your case each view's "top" (starting after first view) will have a relation with the earlier view's "bottom". So that they fall after each other vertically. Can be added as below.
float verticalSpacing = 20.0f;
nextCard.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:nextCard
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:contentView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[contentView addConstraint:[NSLayoutConstraint constraintWithItem:nextCard
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:previousCard
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:verticalSpacing]];
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.
A pretty simple question I reckon:
one UIViewController
one custom UIView
The controller only does:
-(void)loadView{
[super loadView];
self.sideMenu = [[sideMenuView alloc]init];
[self.view addSubview:self.sideMenu];
}
and in the UIView I would like to do something like:
self.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:100];
[self.superview addConstraint:constraint];
So that when I create the UIView in the controller its constraints is already set in relation to the controller.
I have tried and nothing crashes but the UIView gets realy weird x and y coords
Maby I need to update the constraints? Or maby this isnt at all possible?
I'm not sure what ui behavior you are exactly looking for since it appears that you are trying to tie the leading space of your view to the leading space of it's superview. Being the leading space, the space on the left of the view, could it be that you are looking for the more common "stick my left side 100 pixels from my parents left border"? Anyway, in either case, I would connect an outlet from the controller to the custom view (i.e. myCustomView below) and then build the constraint in the UIViewController and not the UIView by overriding:
- (void)updateViewConstraints {
[super updateViewConstraints];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:myCustomView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:myCustomView.superview
attribute:NSLayoutAttributeLeading
multiplier:1
constant:100];
[myCustomView addConstraint:constraint];
}
Apple has an interesting page with a table showing the various runtime entry points for autolayout at this address:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Articles/runtime.html#//apple_ref/doc/uid/TP40010853-CH6-SW1
You might consider adding other constraints as well. Auto layout has the the tendency to exploit any freedom you leave unchecked in the worst possible way ;-)
So leading edge is not enough.
You need enough constraints to satisfy vertical and horizontal layout.
In one direction you need at least
one edge & width (or hight)
Or
Two edges ( implicit width or height )
Or
A horizontal (or vertical) center based constraint and an explicit width ( or height respectively)
The thing about width and height is that they can also be determined by intrinsic content size.
Add constraints after adding the view to the superview.
A bit late but PureLayout is pretty handy https://github.com/smileyborg/PureLayout
In my app I want to achieve this layout:
So parent view contains two sub views. First one ends exactly in a middle (height / 2) and second starts in a middle of parent view. I have found out that it is impossible to do that in the IB with constraints. So I used this code in viewDidLoad method:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView
attribute:NSLayoutAttributeHeight
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
Now it works but only if the app runs on the iPhone. Because size of the view is like iPhone screen. If this app runs on the iPad, there is a problem because screen has different size so this parent view is longer. And constraint (code above) still takes 0.5 * size of the views size from the IB and not size from the iPad size of the view. Item toItem:self.view still takes size from the IB.
Result is that this view has a same size in the iPad as in the iPhone. In the iPad there is a large blank space and then there is a view with iPhone size.
Can you tell what I have to do to make it universal for various screen sizes? Thank you very much
This is possible using constraints, but it is made a bit fiddly by IBs rather annoying and inflexible constraint manager. Here is how I managed it:
In IB, set the two views with the correct frames
Add an equal height constraint between the two views
Reduce the priority of any default height constraints on either of the views. Unfortunately IB does not let you remove these entirely, but setting them to anything less than 1000 will make sure they are ignored.
In the view controllers viewDidLoad method, add the constraint you already tried.
eg
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.topView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
}
Thats it. Screengrabs of the IB constraints are shown below:
Try this code . It will set constraint value dynamically
In your .h file , implement this lines.
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *TopsubviewheightConstraint;
Now create this constraint's as per given screen shot
connect TopsubviewheightConstraint height constraint from screen
implement this code in .m file
if (IS_IPHONE_5)
_TopSuperViewConstraint.constant = 275;
else if(IS_IPAD)
_TopSuperViewConstraint.constant = 502;
else
_TopSuperViewConstraint.constant = 230;
I hope it will help you.
you have 2 options.
create a second IB file for iPad
do everything by programm and use [[UIScreen mainScreen] bound]; instead of getting the sizes of parent ;)
I would do it without the constraints at all and set as follow:
// self.view is my container view
CGRect frame = [[UIScreen mainScreen] bound];
frame.size.height /= 2;
// upper View
upperView.frame = frame;
// lower View
frame.origin.y = frame.size.height;
// or alternatively
//frame.origin.y = CGRectGetMaxY(frame);
lowerView.frame = frame;
here you don't need any device specific options, everything is dynamic, bound to the size of your device's screen ;)
OK so I just figured out how to do this. Simply put the code into viewDidLayoutSubviews method and not to viewDidLoad. The solution I found in the topic Unable to set frame correctly before viewDidAppear.
Here is my code:
[subView1 setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2)];
[subView2 setFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, self.view.frame.size.height / 2)];
Thanks to all for effort!
Thanks to Tark's answer, I managed to to this using constraints as well:
Add Vertical Space constraint for TobView to Top Layout Guide (Using StoryBoard)
Add Vertical Space constraint for BottomView to Bottom Layout Guide (Using StoryBoard)
Add two height constraints for each view in ViewDidLoad
Code:
NSLayoutConstraint *constraint;
constraint = [NSLayoutConstraint constraintWithItem:_viewTop
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:_viewBottom
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
You can do this with constrains (no code required!)
1.- First create two UIview and manually set it's height to half of the size of the current device, positioning one over the other, just like this:
2.- Next you must set the constraints for each one of them like this (this will allow to the container fill the whole screen, one over the another):
Top container
Bottom container
3.- Finally you must select both containers and add a new constrain that specify that they will have in the same height
(remember to clid "Add X Constrains" for each step)
now it should be ready to put the label inside each container, and you will ready