Auto Layout doesn't work as expected - iphone

Given:
a 'logo' ( uiimageview with vertical space >= 10 from top of super view) and a 'log in' and vertical spaces between logo and holder view is <=31
Goal:
in the lanscape mode, logo will move up to the top due to a change from screen'size and holder view should be moving regarding as well.
However, when I rotate from portrait to lanscape, what I am getting is that the vertical constraint still stays at 81 ( somehow i can not delete this value from xcode ). Moreover, the vertical space between a 'Main View' and 'Log in' can not be deleted as well. I know I am doing something wrong here.
Please help if you have any suggestions about this problem.
Details:
+picture 1 is the portrait mode with some details about the constraints
+picture 2 is the lanscape mode with some errors for it.
Edited : Like the way I set up I want the vertical space of logo can be shrinked but it is always >= 10 from the top of the main view

I don't know if this can be done all in IB -- if so, I haven't figured it out yet. I've done it in code as I show below. I start with a constraint from the top of the image view to the top of the main view with a fixed value and also constraints between the image view and the login view. This should be enough to satisfy the system, and you can remove any other constraints to the top or bottom of the main view (I used buttons in my test which have an intrinsic height, so I didn't need to set that. If your views don't have an intrinsic or specific height set, you would have to do that also). Then, in code I remove that constraint to the top (IBOutlet conTop), and remake it to the bottom:
#implementation ViewController {
IBOutlet NSLayoutConstraint *conTop;
IBOutlet UIButton *button;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.view removeConstraint:conTop];
conTop = [NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:.5 constant:-120];
[self.view addConstraint:conTop];
[self.view layoutSubviews];
}
By using a multiplier and a constant together, you can adjust how it moves after a rotation. The numbers I chose here looked pretty good to me, but you can mess with them to see what they do. You can think of the multiplier as a sensitivity factor -- the smaller that fraction, the less change in the distance from the top you will get on rotation. If you need exact values, you can use a little algebra to calculate the values for the multiplier and constant.

You need to find a way to express the relationships declaratively – and ideally in terms which work for both cases.
You haven't quite said what you want to happen in landscape mode. The logo at the top, but how far from the top? Where do you want the holder view?
There's a WWDC video which is essential for understanding how Auto Layout works. Have you had a chance to watch it yet?

Related

Anyone auto-layout masters able to enlighten me on constraints and rotation?

I'm trying to figure out auto layout constraints with a view in portrait and landscape. In portrait I have a container view (but any view will do) that is 320x200 pixels. When the phone is rotated to landscape, I would like that container view to fill the screen at 480x320.
Right now I'm doing this entirely in IB, and am trying to keep it that way for now. I struggle with the dang blue and purple constraint icons appearing when I try to set the the view to resize as I would like it to.
I've watched the WWDC auto-layout videos and have auto-layout working in other views, but I just can't get this seeming simple change to occur.
Any help would be appreciated. Thank you.
In the 2012 WWDC video 228 - Mastering Auto layout, he showed something like this
for (UIView *aView in [yourViewController subviews]) {
if ([aView hasAmbiguousLayout]) {
NSLog(#"View Frame %#", NSStringFromCGRect(aView.frame));
NSLog(#"%#", [aView class]);
NSLog(#"%#", [aView constraintsAffectingLayoutForAxis:1]);
NSLog(#"%#", [aView constraintsAffectingLayoutForAxis:0]);
[aView exerciseAmbiguityInLayout];
}
}
Try that and see if you have any ambiguous layout. Of course do this after the rotation and everything is not right.
If that does not produce anything then just use the constraintsAffectingLayoutForAxis method on your specific view and you should see the constraint not allowing it to change.
Most likely there is a constraint with a too high priority that you will need to reduce.
I had to use inequalities. For example a text area width in portrait needs to be 100, but the landscape width needs to be >100 so it can stretch. Other constrains are in place to pull it to the sides already for me. I also enabled landscape modes. When I rotate now my 4 lines of text reflows to 2 lines very smartly.

Correct approach for implementing vertical scrolling with UIScrollView

I know that I asked a question a few minutes ago, but I want to make a few points clearer before asking for the community's help. I'm new to iOS development, close to finishing my first app. The only thing standing in my way is this UIScrollView. I simply do not understand it, and could use your help.
This is in the detail view controller of a drill-down app with a tab bar. I have approximately 8 fields (for things like phone numbers and such) drawing from a .plist. Obviously, those take up enough room that I could use a little extra real estate. I would guess that it needs to be about the size of two views vertically, but I do not understand how to allocate that sort of space to a UIScrollView. Some tutorials I have read say that you don't even need to define it in the header, which I doubt and do not understand. Additionally, I do not understand how to simply get the app to smoothly scroll up-and down only. Apple's examples have constant move cycles that flip between horizontal pictures.
I doubt it makes very much a difference, but I have an image that is in the background. I'm going to load the view on top of that.
My question is broad, so I don't expect you to take the time to sit down and write out all of the code for me (and I wouldn't ask you to). Just an outline of quick, helpful tips would help me understand how to get this view to load in the context of my project.
Many thanks!
It's fairly simple. Add a UIScrollView to your view. Add your fields and such to the scrollview. In viewDidLoad or somewhere similar, you need to set the contentSize on your scrollview. This is the "virtual" area that will be scrollable. This will generally be larger than the frame of the scrollview. In your case, you indicated it should be roughly double the width.
For instance, if you had a scrollview with a view inside, and you wanted to make sure the entire view is visible via scrolling:
self.scrollView.contentSize = self.contentView.frame.size;
//setting scrollview zoom level
self._scrollview.minimumZoomScale=0.5;
self._scrollview.maximumZoomScale=6.0;
self._scrollview.contentSize=CGSizeMake(320,500 );
you have to outlet scroll view in .h class and #property #synthesis this scroll view.then you can able to scroll up and down,if u want only vertical scrolling ,then u have to go to interface builder and uncheck the horizontal scrolling.
You can set a few settings for your scrollview to limit the scrolling to horizontal or vertical. A few important ones are:
// pseudcode here, start typing "[scrollView " then press escape to get a intelli-sense of all // the things you can set for the scrollview.
// values could be YES/NO or TRUE/FALSE, I can't remember which one but
// I think it's YES/NO. Once you start scrolling, the phone will determine
// which way you're scrolling then lock it to that direction
[scrollView setDirectionalLockEnabled:YES];
// when you slide the view, if enough of the next part of the view is visible,
// the scrollview will snap or bounce the scrollview to fit this new "page".
// think of swiping feature to navigate the iPhone home screens
// to show different "pages" of iphone apps
[scrollView setPagingEnabled:YES];
// as a safe guard, make sure the width of your scrollview fits snuggly with the content
// it is trying to display. If the width is more than necessary to display your table of
// data vertically, sometimes the scrollview will cause the
// horizontal scrolling that you don't want to happen and you get bi-directional scrolling
Just set the content size of UIScrollView after adding all the controls/button/textfields etc. for example you add 10 textfields in UIScrollview then content size of UIScrollView will be
lastTextField.frame.origin.y+lastTextField.frame.size.height+20; 20 is for margin.
That's it let me know if you want to know something more related to your app.

Hiding elements outside of bounds on iPhone

I'm trying to get a UIView to expand (with animation), sort of like an accordion menu. I can get the animation working fine however the issue is the subviews of the UIView are expanding past the bounds of the UIView.
For example, the view has an UILabel in the top-right corner with a height of 16. Assume the UIView height is 0 at the beginning of the animation. One would expect that the content of the view be hidden and gradually reveal as the UIView grows. Once the height has reached 8, for example, half the label should be visible. However that isn't the case - rather, the label is visible the whole time, regardless if it's height extends outside of that of it's parent view.
Any ideas how to resolve this?
Okay, I had to set the clipsToBounds property to true. I spent some time googling before making the question but didn't have much luck until I saw it in the Related section of my question.

iPhone Horizontal Scrolling

I am trying to create an app with horizontal scrolling, so that one would be able to scroll horizontally through a series of images. I watched the WWDC Session 104 video on this, and while they made an interesting app, they flew through the basics of it very quickly.
I understand using the UIScrollView, and that I have to enable paging. After that they say that I should add more views as subviews of the scrollview, but I am not clear on how to do that. I am also not clear on how I add my images to those views.
As you can probably tell I am pretty new at this so any help would be appreciated.
You want to look into UIImageView. It's a view specifically for holding images.
When you add your images, you want to set their rects (probably using initWithFrame: for each UIImageView) so that:
the first image is at 0,0
the second image is at 320,0
third is at 640,0 (etc)
I.e. each image is 320 pixels right of the previous.
The final step is to set the contentSize for your UIScrollView -- this is a CGSize which describes the total size of the scroll view.
If you have 3 images, you would then set it to (320*3) * 480 using e.g.
myScrollView.contentSize = CGSizeMake(320*3, 480);
A lot of people, when they initialize the scroll view, have a for loop or similar which steps through the images they want to display. These for loops tend to look something like this:
CGFloat scrollWidth = 0.f;
for (UIImage *someImage in someNSArrayWithImages) {
UIImageView *theView = [[UIImageView alloc] initWithFrame:
CGRectMake(scrollWidth, 0, 320.f, 480.f)];
theView.image = someImage;
[myScrollView addSubview:theView];
[theView release];
scrollWidth += 320.f;
}
myScrollView.contentSize = CGSizeMake(scrollWidth, 480.f);
This way you'll get things lined up and you'll get the content size for you at the same time.
If you want to make it so that the scroll view "intelligently" scrolls to each image and stops when people slide left/right, you can do myScrollView.pagingEnabled = YES.
Hope that helps get you going.
Assuming you have "infinite" images, putting them all there at or before launch time in a huge UIScrollView will not be an option. (there is a limit to the size of a UIView)
The way I solved it: Make a UIScrollView covering the whole screen. It's content should be a UIView of 3*320 width and 480 height, extending 320px left and 320px right.
Put 3 UIImageView's in it, left, middle and right. Set paging=YES, so the uiscrollview clips to the 3 "pages" you've created.
Make sure your class is the delegate of the uiscrollview, and listen for
-(void)scrollViewDidEndDragging:(UIScrollView*)sv willDecelerate:(BOOL)notFinished
-(void)scrollViewDidEndDecelerating:(UIScrollView*)sv
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView*)sv
and make the appropriate transitions on hitting paging boundaries; shift images and set ContentOffset so you're looking at the center image again.
I suggest you make this first, and only then read on...
Then you will hit on a bug, documented here UIScrollView - (bounces = NO) seems to override (pagingEnabled = YES) and here http://www.iphonedevsdk.com/forum/iphone-sdk-development/935-paging-uiscrollview.html, which makes that you cannot disable bouncing and have paging enabled at the same time. So enable bouncing, and subclass UIScrollView, overruling setContentOffset in there to prevent bouncing. (having bouncing really enabled will make for a rather unusual user experience)
Have a look at Apple's PageControl sample code. It's fairly short and easy to follow so you'll get the gist of setting up a project where multiple view controllers are loaded as you swipe horizontally.
Once you have this setup then it's the view controller's responsibility to load its own content (in your case, an image). You should make sure you understand how to load images first (using threads, etc) before you tackle paging, etc.
Think of it as two independent tasks. The view control is responsible for loading and displaying an image. The scroll view with paging just tells the appropriate view controller when to load itself (it doesn't care what the view controller does once its loaded)
Good luck!

iPhone Dev - UIView frame and loadView

I'm learning to develop for iPhone (programmatically, I don't like IB). I only use IB for the frames of things, so I design in IB but only use it to check each control's frame. Something I've gotten very confused about is the frame of a UIView. First of all, if you do not implement loadView in a view controller, it automatically creates an empty UIView (and I've noticed that its black) and assigns that to self.view, correct? What about the frame? does it automatically figure out what the frame should be? I mean, it needs to be different depending on the if there is a status bar, if there's a tab bar, a toolbar, the orientation. In my tab bar app, it seems no matter what I set frame of my uiview is, it still looks fine. And also, the frames for UIView seem messed up in IB. Like the y value of a UIView taking up the whole window (except status bar) is 0, when it should be 20, because the status bar takes up the top 20 pixels. And in IB a view in a tab bar controller has a y value of 411, even though it begins right under the status bar. And whenever I NSLog a frame (the four numbers, view.frame.origin.x, view.frame... etc.) the x, y, width and height are always 0.. Can someone who understands it please explain it to me? Thanks!!
Ok, this is weird... This:
CGRect test = CGRectMake(0, 20, 320, 460);
NSLog(#"%d %d %d %d", test.origin.x, test.origin.y, CGRectGetWidth(test), CGRectGetHeight(test));
gives output:
0 0 0 1077149696
??? What's with that?
First thing you should note is that CGRect's origin and size members use float, so in your NSLog statement you need to use %f, not %d. So for example,
NSLog(#"%f %f %f %f", test.origin.x, test.origin.y, test.size.width, test.size.height);
Pixel sizes are floats because on the desktop you need precise math when doing scaling and want to support proportional sizing. If you stick to integers and the window size is expanded by, say 1.2x, integer round-off will throw things out of line. They kept the same thing on the iPhone because you can have subviews that get auto-sized and floats offer more control over size and position.
As for the view positions, if you go into IB and select a view and check the "View Attributes" inspector you'll see one of the options is to show "Simulated Interface Elements." By default it has the Status Bar enabled (with Gray) and it allows for its height in the interface. If you check the size tab in the inspector, the view height is actually set to 460 (allowing for the 20 pixel status bar).
You can choose to include a top bar or a bottom bar and it will "simulate" those by adjusting the content height for you. This is just a positioning simulation so you can lay out your controls. At runtime, it's assumed you'll load the view into the proper viewcontroller which will have the proper chrome so it should all look right.
A lot of IB numbers are there for relative positioning. When you add the view to a navcontroller or a tabview, this view becomes a child of those parent views and by default they autoresize subviews. So if you've "simulated" for that top bar in IB, your controls should all be lined up properly and flow into the right place and you should see what you expect (of course, depending on what parent control ends up with your subview as a child).
BTW, you're missing out a lot of convenience by not using IB to wire up the controls and connecting them to IBOutlets. It really helps take out a lot of manual effort and extra code. It's like Superman refusing to use his X-ray vision and instead deciding to punch through walls :-)
Answer: IB doesn't always show correct origin/location coordinates, and some things, like UITabBarController, resize the view to be what they want it to be no matter what you set it.