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

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.

Related

Auto Layout doesn't work as expected

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?

UIView autoResizing works sometimes

Currently, here's what's happening. If I'm in portrait mode, and I present a new modalViewController, and then rotate to landscape, autoResizing works perfectly and everything looks great. However, if I'm in landscape, and I present a new modalViewController, autoResizing does not work and everything looks funky. Can anyone think of any possible ideas as to why this could be happening? I'm desperate I've tried everything.
Maybe a way to fix this would be to figure out what code gets called by the system when I'm in portrait and I go landscape. Maybe I can call that exact code if my modalView is presented in landscape. I've tried layoutIfNeeded and setNeedsDisplay but they don't do anything. I've also tried setting the contentMode to redraw-doesn't help.
I have this in my viewDidLoad for the modal view
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
{
NSLog(#"is landscape, width:%f", self.view.frame.size.width);
}
and this outputs 320, even though I'm in landscape, when it should be 480.
UIViewAutoResizingMasks are what we refer to as 'struts' and 'springs'. Consider this: you have a large square with a small square inside. In order for that square to stay perfectly centered, you must set a fixed width from each inside edge of the large square, so as to constrain it. These are struts.
Springs, on the other hand, work more like a UIView does during rotation. Let's say our view must stay on the bottom of the screen, aligned in the center. We want to keep it's Top spring flexible so that when the view rotates from 460 px to 320 px, it keeps it's same position relative to the screen's now changed dimensions.
Keeping this in mind, when a view is loaded in portrait (as all UIViewControllers are), but the actual orientation is landscape, it's possible that the view will get 'confused' and maintain a sort of messy hybrid orientation type view. If you absolutely must (and I cannot stress how last resort-ish this is) force an orientation change beforehand, use iOS 5.x's +attemptRotationToDeviceOrientarion

iPhone ScrollView zoom problems

I'm new to iPhone dev and Obj-C and I have several problems with ScrollView/ImageView while getting close to deadline. I used IB to create interface so I access most parameters via builder.
1) I was using touch events (begin/moved/ended) on imageView to switch images. When I put ImageView to ScrollView the old gestures stopped working and I can only zoom. If scrollView of both have focus I can't use my gestures even when zoomed out. How can I use both?
2) How do I zoom only image part of view? Unfortunately I also see background area around :/ What's worse - after rotating the view keeps it's old dimensions and I have even more black areas around image. For some reason image is in top-left corner.
Code snippets I found doesn't really help me much in this case. I have various images of different sizes in imageView to switch and zoom in/out.
EDIT: Ok, a little different. How do I override scrollview touch mode so that when image is zoomed out (to screen size) "normal" gestures would work. Currenly I have either scroll view scrolling or gestures, can't use both. Anyone?
Solved by dynamically changing imageview to imagesize and switching like:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
if (scrollView.zoomScale==1.0) {
scrollView.userInteractionEnabled = NO;
imageView.userInteractionEnabled = YES;
}
Not the best solution but works good enough.

How do I properly handle rotation of a UIScrollView containing UIImageViews?

I'm developing an image viewer, much like the Photos App.
It's a UIScrollView with paging enabled with images loaded from the internet, so I've adapted portions of the LazyTableImages sample. The Scroll View and each ImageView inside of it have all of their autoresize mask flags set.
When I first observed how resizes were happening during rotation, it looked good, but once I started trying to interact with the scroll view, I realized that I also had to programmatically change the size of the contentView. I did that by implementing didRotateFromInterfaceOrientation: in my view controller.
[self.scrollView setContentSize:CGSizeMake(numberOfImages * portraitWidth, [scrollView bounds].size.height)];
With interaction behaving properly, I then discovered that, if I was viewing the second photo and rotated, portions of both the 1st and 2nd photos would be shown on the screen. I needed to change the contentOffset as well.
I've tried to fix this two ways - both by using the scrollRectToVisible:animated: method of UIScrollView, as well as trying to set the contentOffset property directly. And I've experimented by putting this code in implementations of both the "one-step" and "two-step" responses to changes in Orientation. For example:
-(void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
[self.scrollView setContentOffset:CGPointMake(currentlyViewedPhotoIndex * largeImageHeight,0) animated:YES];
In all cases though, it just looks janky as hell. Either I clearly see the scroll happen, or it just jumps. Uuuuuuuuuuugly! Is there a way to do this so that it behaves exactly like the Photos app does?
What I wound up doing instead - just before rotation starts, hide the UIScrollView and create a UIImageView that contains the currently viewed image. Rotate, that image will rotate all nice and pretty, and when rotation completes remove the ImageView and unhide the Scroll View.
Update - if you're reading this today (anytime after iOS 6), use a UIPageViewController and set transitionStyle to UIPageViewControllerTransitionStyleScroll, for crissakes.
I did something slightly different when faced with the same problem. In willRotateToInterfaceOrientation:duration:, I hide all of the UIScrollView's subviews except for the currently displayed subview, and in didRotateFromInterfaceOrientation: I unhide the subviews.

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!