Maintaining subview relative location after rotation - ios

My app is built on a UIScrollview with a UIImageView subview and various other subviews. When I rotate to landscape, I change the contentSize of the scroll view and resize the image view proportionally to take advantage of the increased horizontal width. The means the height increases as well to maintain the proportions.
My question is, in the case of the blue subview shown, what do I need to do to reposition it such that it maintains it relative position after rotation, given that it's superview is no longer the same size? I have experimented with convertRect:toView: and converPoint:toView:, but I can't seem to get it quite right.

Are you using auto layout? If so, in many cases, the judicious use of constraints can keep that subview in the right place and right size, even as you go from landscape to portrait. But you'd have to share more details about what else is on this view for us to be more specific.
If not using auto layout, you generally can set the view's autosizing mask so it moves to the correct location for you. But in the case of a scroll view subview, you might have your view controller can respond to viewWillLayoutSubviews, updating the contentSize of the scroll view and the frame of the subview to move, accordingly:
- (void)viewWillLayoutSubviews
{
// update the contentSize of the scroll view for the width of the root view, but I'm assuming the
// height won't change
self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, self.scrollView.contentSize.height);
// adjust the frame of the subview you want to move so that it is a certain offset from the bottom
// left corner of the scroll view's `contentSize` (in this case, 10 points from bottom, 10 points from right)
self.subviewToMove.frame = CGRectMake(self.scrollView.contentSize.width - self.subviewToMove.frame.size.width - 10,
self.scrollView.contentSize.height - self.subviewToMove.frame.size.height - 10,
self.subviewToMove.frame.size.width, self.subviewToMove.frame.size.height);
}
The specifics vary based upon details of (a) whether you're using autolayout or not; (b) whether you're creating this subview programmatically or not; and (c) what other content you have in your view and whether the change from portrait to landscape and back results in any change in the vertical height of the scroll view.

To reposition a view, you update its frame. The frame property is of type CGRect, which is a combination of size (CGSize) and origin (CGPoint). If size of your blue view doesn't change, then only origin should be updated.
iOS coordinate system starts from top left corner:
For your blue view you calculate it's origin from the bottom right corner, that is
origin = contentSize - blueViewSize - padding
Do this separately for x and y coordinate, make CGRect with updated origin, and update blue view's frame.
UP: This is how you do it manually, but you can (and better should) let UIKit reposition subviews for you automatically -- learn about autoresizing and autolayout in Xcode's Interface Builder, and Developer manuals

Related

UIScrollView - Need Constraints for x position/width, Need Constraints for y position/height

I have a view hierarchy that looks like this (based on other answers and Apple's advanced AutoLayout guide for working with UIScrollView):
The 2 steps required for ScrollView are:
Set up constraints for position and size (frame) of ScrollView: The same way you do it with any other view.
Set up constraints for content size: By making sure that there are constraints from the ScrollView's subviews touching all edges (leading, trailing, top, bottom) of the ScrollView, if you're doing this in interface builder and not programmatically.
Step 1 worked fine at first and this was the result:
No problems with position and size as expected. I just need to define the content size (content height and content width) now in Interface Builder with Step 2.
Now I add the constraints touching all 4 edges of the ScrollView like so:
Suddenly, the ScrollView doesn't know it's position and size (frame) anymore.
I've looked at other answers and followed the various steps, but can't seem to resolve this issue. Sorry I'm not able to post Storyboard screenshots because of privacy issues
Set the scroll view's top, bottom, leading, trailing constraints to its superview to 0.
Set the view that is inside the scroll view and set its top, bottom, leading, trailing constraints to its superview to 0.
Select the view that is inside the scroll view, go to the size inspector and set the "Intrinsic size" to "Placeholder".
Scroll views can be a little tricky at first. You really have 3 parts:
Actual frame of the scroll view
Actual frame of the subview(s) contained in the scroll view
The contentSize of the scroll view - that is, how far it should scroll in either direction
So, 1. is pretty straight-forward.
The second part also seems straight-forward, except that we tend to "pin" subviews to the inside edges of their superviews. In the case of scroll view subviews, those constraints are what defines the contentSize. You also have to make sure the subviews have a "size".
Starting with just one subview, you would:
set the scroll view's constraints as "normal".
set the size of the subview - just for demo purposes, set it to 100 x 100
pin all four edges of the subview to the four edges of the scroll view
Run the app. Assuming you set background colors so you know what you're looking at, you should see the scroll view positioned and sized as you'd expect... you should see the subview of 100 x 100 sitting somewhere inside the scroll view... and you will likely not be able to do any actual scrolling.
If you go back and change the subview to, say, 100 x 800, and still have its bottom constraint pinned to the bottom of the scroll view (the subview's superview), and run the app again... You should be able to scroll up and down for the full 800 pt height of the subview.
The way to think about it is: the scroll view's content - whether it's one or many subviews - has to define its own size, which will define the scrollable area (the scroll view's contentSize).
Hope that makes sense!

UIScrollView content size fixed to 600x600(Main.storyboard size)

I've been facing this issue from past 2 weeks and not yet got a solution.
I'm using UIScrollview in my application where the problem exists.
Generally in my app, there is dynamic text and images with different sizes will come from webservices. For example, imagine the Facebook Newsfeed. My application is similar to the Facebook newsfeed. Sometimes, there will be only text, sometimes there will be text and images. And comments for that post.
As i've seen in many links, the heirrarchy i'm following is SuperView-->UIScrollView-->Content View and the elements are placed in that content view. I'm assigning the constraints from the elements in the contentview to the Superview (ContentView --- constraints -- SuperView). When the content in the view exceeds the size of the superview, it has to scroll. But the scrollview content size is limited to the size of 600x600 i.e., in main.storyboard, when we design for W any x H any size. I've seen many tutorials and searched many sites. But following them gives me no luck.
Any help is appreciable.
Here is how to set up a scrollView in Interface Builder from scratch that works with Auto Layout.
Start with a new ViewController. Drag out a scrollView such that it fills the view. Untick Constrain to margins and constrain the left, top, right, and bottom edges of this scrollView to the left, top, right, and bottom of its superview with offsets of 0. This allows the scollView to fill the screen on any device in any orientation. You can make your scrollView take up less of the screen if you like, just make sure it is fully contrained.
Add a view to your scrollView. This should be the only top level view on your scrollView and it will serve as your contentView.
Constrain the left, top, right, and bottom edges of this contentView to the left, top, right, and bottom of the scrollView with offsets of 0. At this point, you will see warnings about ambiguous content size. That is because you haven't told it yet how big your content view will be.
To size the contentView, add width and height constraints to the contentView. If you want it to scroll, the width and height must be larger than the width and height of the scrollView itself. If you only want to scroll vertically, set the width of the contentView to be equal to the width of the scrollView. To do this, in the Document Outline view, control-drag from the contentView to the scrollView and select Equal Widths from the pop up.
If you want to be able to change the height of your contentView from code (to account for dynamic content), first create a height constraint for your contentView by control-dragging within the contentView and selecting Height from the pop up. Create an IBOutlet to the height constraint by control-dragging from the height constraint (found in the Document Outline view) to your ViewController's code. Give the outlet a name like scrollViewHeight then set the height with a value like scrollViewHeight.constant = 2000 when you need to change the scrollView's height.
Simply add your UIScrollView to your UIViewController's view in the storyboard and add the appropriate constraints in the interface builder.
Then, build your content view in the code and give it any frame you want, now start adding your controls to this content view and calculate the size of each one (especially the height), and at the end you have the total height of your controls.
Now set the frame of the content view to match that height and add it as a sub view to your scroll view, then set the content size in the code like this:
_scrollView.contentSize = contentView.frame.size;
Another note, do this changes to the sizes in the viewDidLayoutSubviews to avoid any problems with different screen sizes but be careful, this event is called many times so have a BOOL or something to ensure that the code that creates the view and add it to the scroll view is executed once, e.g. like this
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (! built) {
// Do everything I explained above here
built = YES;
[self.view layoutSubviews];
}
}

UIScrollview won't scroll when Autolayout is used

I have a scrollview which has two subviews (iPad). The view hierarchy is as follows:
-- UIScrollView
-- UIView1
-- UIView2
The frame of the UIScrollView is the size of the screen, the frame of UIView1 is also the size of the screen, but the frame of UIView2 is (0,0,768,2000).
The scrollview doesn't scroll vertically. According to the apple documentation, the scrollview should automatically set its content size. Can anyone help me out with this issue as why the content size is not being set properly ?
P:S: When I use a single view inside the scrollview and set a proper vertical constraint, it scrolls properly.
Scrollview won't scroll if you enable autolayouts. Technically when you scroll, all the elements in the scrollview change their position. So use auto layout if you are fixing the position of the elements in the scrollview.
Instead use a UIView as a container view inside a scrollview which contains all other objects like button , label, imageview etc. And then you will be able to scroll.
Check the below link for more details:
https://developer.apple.com/library/ios/technotes/tn2154/_index.html
In your problem try to put proper values of content size. Also check if the vertical scrolling is enabled or not.
The question is old, but the correct answer would be to make sure the autolayout constraints are all set.
Vertically, you should have some constraint pinning your View1 to the Scroll View Top, another one pinning your View2 to the Scroll View Bottom, and one third one setting the vertical space between View1 and View2. Also check that the views themselves (View1 and View2) have constraints for their heights.
Horizontally, pin one of the views to the Scroll View in both leading and trailing space, and the other view left and right to the first one (so they will have equal widths).
Once all of those are set, the content should scroll correctly.
Use contentSize property of scroll view in code. The best way is to use 'viewDidLayoutSubviews' method
(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
_scrollView.contentSize = CGSizeMake(568, 594);
}
2.Always make horizontal space >=0 between lowest of inner views and a scroll view. Automatically, auto layout make suggestions like '-160' and this negative space will not scroll.
If the Content size is more than the scroll view frame then only scroll view scrolls otherwise not .
Main Point : Mistake is in set 'contentSize' of 'UIScrollView', you need to set manually.
As seem in your Question
scroll view frame = screen frame,
view 1 frame = screen frame,
Its okay but
view 2 frame = (0,0,768,2000), then it overlapping on first view because your view2 x,y position is (0,0), so may be your first view will not display.
If you want to vertically add view 1 after view 2 then your view 2 frame should be
view2.frame = CGRectMake(0,view1.frame.origin.y + view1.frame.size.height , 768,2000);
And after added both of view then you need to manage contentSize of UIScrollView , in your case such like
scrollView.contentSize = CGSizeMake(768, view1.frame.origin.y + view1.frame.size.height + 2000)
I put simple logic, That may be helpful in your case:
How to enable scrollview scroll automatically when used with AutoLayout:
Please see the below link which help you to fix it correctly:
https://medium.com/#pradeep_chauhan/how-to-configure-a-uiscrollview-with-auto-layout-in-interface-builder-218dcb4022d7
Question for above help guide:
Which view is parent view?
scrollView or parent of scrollView?
Answer:
- Parent Of ScrollView (Give equal with and Equal height to view which is parent of scrollview)

iOS: Frame and picture autolayout

Say I have two UIViews. One is a UIImageView of a frame, and one is a UIImageView of a picture. I want the picture to stay inside the frame regardless of screen size using autolayout. The picture is NOT a subview of the frame - they're just two imageviews that I want to remain superimposed in the right proportions.
I can't pin the heights, because I want them to change when rotating (for arguments sake say I pinned the frames bottom to the bottom of the main view). I can't make the heights equal, because they're not, the picture should be smaller. I can pin the horizontal spacing, but that will just make sure the picture's x,y are right, but not height and width.
I want it so that if for whatever reason I change the frames size (via transform, for example), the picture will follow.
Can I do this in autolayout, or is coding required?
You can make constraints pinning the centerX and centerY of the two views to be the same. Then you can make constraints to specify the width and the height. It depends on how big your frame is. You might try
picture.width = frame.width - 20
or
picture.width = 0.8 * frame.width
(and do something similar for the height).
If you want them both to rotate, you need to either set the rotation transform on both views (not recommended) or put them both in the same superview and set the rotation transform on that superview (recommended). Remember, if a view has a transform set then you can't make a constraint that goes from within that view to something outside that view. They are like separate worlds.

iOS autolayout-move a view located inside a tableviewcell to the center of the screen

I have a tableview with cells containing text views as well as imageviews. My project is currently using AutoLayout. My goal is to get the imageview to display in fullscreen when it is tapped. One option is to use a modal view controller, but I want to have this work sort of like the way tapping on images in the facebook app works, the app centers the image and fades the background.
Since I'm using autolayout, I cannot simply set the frame of the imageview to fill the screen. Instead, I need to use autolayout constraints. My image view has 5 constraints, a constraint setting a distance from the bottom of the cell, as well as the left an right sides, and one controlling the image height. The last is a vertical space constraint between the textview above the image view and the top of the image. While this would appear to conflict with the height and bottom constraints, for some reason interface builder forces me to have this. To avoid problems, I set this constraint's priority to be less than 1000 (the image should never overlap the textview anyways, since the tableview cell height is set so everything will fit perfectly).
To center the image, I set the distance from the left and right to be zero and remove the vertical space constraint. In order to center the image, I replace the bottom space constraint with a center y alignment constraint to the UIWindow as opposed to the tableviewcell. I want to have it be in the center of the screen, not the cell.
To get the main window I use this:
AppDelegate* myDelegate = (((AppDelegate*) [UIApplication sharedApplication].delegate));
//access main window using myDelegate.window
Then, to set the constraint:
//currently sets the distance from the bottom of the cell to 14
//changing it...
[cellselected removeConstraint:cellselected.imagebottomspace];
cellselected.imagebottomspace = [NSLayoutConstraint constraintWithItem:cellselected.viewimage attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:myDelegate.window attribute:NSLayoutAttributeCenterY multiplier:0 constant:0];
[cellselected addConstraint:cellselected.imagebottomspace];
However, this doesn't work. The changes in the width and height of the image view apply just fine. However, when readding the imagebottomspace constraint, I get an unsatisfiable layout--apparently the constraint conflicts with another constraint which sets the distance between the bottom and the image view to 14, the very constraint I just removed. So it seems that it isn't actually removing the constraint.
When I proceed and let the app break a constraint, the imageview moves, but to the wrong place. It isn't centering in the screen. It moves way up and off the screen.
Obviously what I'm doing isn't right. What am I doing wrong?
So I guess you want something like this:
First, you need to know that as of Xcode 4.6.3, the nib editor (“Interface Builder”) has a bug when setting up constraints in a table view cell. It should create the constraints between the subviews and the cell's content view, but instead it creates the constraints between the subviews and the cell itself. This tends to screw up layout at runtime. (This bug is fixed in Xcode 5 and later.)
The consequence of this is that you should either remove all of the constraints that were in the nib and recreate them in code, or just get rid of the nib and create the cell's entire view hierarchy in code.
Second, there's an easier way to do the image zooming. Here's the basic procedure when a cell is selected:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
Create a new image view just for zooming and set its frame to that CGRect. Set its userInteractionEnabled to YES. Set its autoresizingMask to flexible width and height. Add a tap gesture recognizer.
Add the new image view as a subview of the top-level view.
Set the cell's image view's hidden property to YES.
In an animation block, set the new image view's frame to the top-level view's bounds.
Disable the table view's panGestureRecognizer.
When the new image view is tapped, reverse the procedure:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
In an animation block, set the zoomed image view's frame to that CGRect.
In the animation completion block:
Remove the zoomed image view from its superview.
Set the cell's image view's hidden property to NO.
Enable the table view's panGestureRecognizer.
Since you're not moving the original image view, you don't have to mess with its constraints. Hidden views still participate in layout.
Since you're creating the new image view in code, it will have translatesAutoresizingMaskIntoConstraints set to YES by default. This means that you can just set its frame. Auto layout will automatically turn the frame into constraints.
You can find the full source code in this github repository.
I've just come across a similar issue. I think that the reason for these problems are that the views embedded in UIScrollViews exist in a different bounds system to those of the views outside it. This is effectively how scrolling works in the first place, think of it as just applying a variable offset to the views it contains. Autolayout doesn't know how to translate between these different coordinate systems so any constraints that bridge across aren't going to be applied the way you expect.
To quote from Erica Sadun's excellent book iOS Auto Layout Demystified (from the section 'Constraints, Hierarchies, and Bounds Systems'):
"Be aware of bounds systems. You should not relate a button on some
view, for example, with a text field inside a separate collection
view. If there's some sort of content view with its own bounds system
(such as collection views, scroll views, and table views), don’t hop
out of that to an entirely different bounds system in another view."

Resources