Off center center.x when working with stackview - ios

I've got a stack view and I'm wanting when the user clicks on one of the buttons a little "bar" view centers underneath it (the view is outside of the stack view). I had this working before I layed out the buttons with autolayout by just setting
movableView.center.x = newView.center.x
and animating it. Now with the buttons in the stack view the movableView's center X does not line up with the buttons' center x's. It's like 1/3 way off centered.
The movable view itself has no autolayout on it.
What is the cause of this and how can I align the center x of views that have auto layout in play?

Without more information this is my best guess as to what the issue may be.
The center attribute is based on the view's frame, which means that it is using the superview (in this case the UIStackView) coordinate system. If your UIStackView's frame.origin.x is not at 0, which is how it appears in your screenshot, you will need to adjust your movableView.center.x accordingly. This could be as simple as:
movableView.center.x = newView.center.x + stackView.frame.origin.x
Another option would be to convert the center to the main view's coordinate system, like this:
movableView.center.x = newView.convert(newView.center, to: self.view).x

Related

What is the proper way to animate a UIView from outside of a view controller into it's center?

I have several UIButtons that animate from the right (outside the bounds of the view controller) into the center of my view controller each time I press a button. I'm currently accomplishing this by animating the .centerX constraint constant of each UIButton from 250 to 0. It works, but the buttons are visible on the right side devices with larger screens. I could just increase the constant to 300 or something, but isn't there a proper way to accomplish this using Auto-Layout?
you can align button's leading edge with VC.view's trailing edge and then animate it by translating along x axis.

Parallax header on a one-page screen in iOS using Interface Builder

In an iOS 8 app, I'm trying to create a screen that has a "parallax header", i.e., an image header that grows as you pull down. I would like to do this using only constraints in Interface Builder, if possible.
Here is a nice guide by Pete Hare on how to do such a thing, and I've also had good help at looking at this example project by Bill Carson. However, contrary to these projects, this is not the header to a scrolling area that's taller than the screen, like a Table View; it's just one page. And for some reason, I can't get things to work in my app. I find Scroll Views in Interface Builder rather confusing to begin with. Could anyone walk me through the steps?
Why, certainly! First we'll set up a view controller with a scroll view.
Create an empty View Controller. Give its initial View the Xcode Specific Label Root View so we can tell things apart.
Add a Scroll View. Resize it to fill the view controller, and add constraints for Leading, Trailing, Top and Bottom to equal the superview's corresponding edges. (I do this by control-dragging from Scroll View to View in the Document Outline, hold down the Option and Shift key and then select all four edges, and then Add Constraints.)
Enable Bounce Vertically in the Scroll View's Attribute Inspector.
Add a simple View to the Scroll View and pin its edges to its superview -- the Scroll View -- in the exact same way as we did with the Scroll View to Root View (although this time we don't need to hold Option key when adding constraints). Give the new view the Xcode Specific Label Scrolling Content.
Xcode is not happy, it says Scroll View is missing constraints: "needs constraints for X position or width" and "needs constraints for Y position or height". Let it automatically add missing constraints, it will add constraints for the center of Scrolling Content to the center of Scroll View, in X and Y directions.
By setting a background color to the Scrolling Content, we can now run and confirm that the scroll view with vertical bounce is working as intended. Nice. Now, let's add the header.
Add a nice header image to the project assets, and drag an Image View to the Scrolling Content. Label it Header Image. Select your image asset as the Header Image's image. Drag the corners of the Image View so that it is aligned to the top, left and right edges of your view. Now let's go through the constraints to set on the Image View.
We want the top edge to be fixed to the top of the screen, regardless of how the user is scrolling. So we need it to be pinned to something outside of the Scroll View. You may try fixing it to the Root View's top edge, but unfortunately, that does not work for some reason. What does work is to pin it to the Top Layout Guide. You'll do this easiest by control-dragging in the Document Outline, between the Header Image and the Top Layout Guide and accpet the suggested constant.
The bottom edge of the Header Image needs to be pinned to something inside the Scroll View. Ultimately, we'd like it to be set to a fixed distance from the top of the Scrolling Content -- but Interface Builder won't let us do that. You can only set it to some distance from the bottom of the Scrolling Content, which is not very practical since you will then have to take the height of the device into account. What we instead do is to add another view directly beneath the Header Image, pin that view's top edge to some distance from the Scrolling Content's top edge, and then pin the Header Image's bottom edge to our new view's top edge. This could be a regular View that holds the rest of your user interface below the header. But for this example (and to demonstrate a later point in this guide), we'll use a label. Add a label directly beneath Header Image and give it three constraints: pin it's top edge to the Header Image's bottom edge, it's top edge also to the Scrolling Content's top edge, and it's center X to the Scrolling Content's center X. The distance between the label's top edge and Scrolling Content's top edge will be the height of the image in the non-dragged state. This unfortunately needs to be set to a constant in the storyboard file -- we'll have to update it programmatically. More on that later.
The last part is easy: pin the leading and trailing edges of the Header Image to the leading and trailing edges of the Scrolling Content.
Now, all our constraints are in place! If you run the app, you can see how it's working correctly constraintwise, but the image isn't scaling the way we expect it to. A couple of last tweaks before we're done.
In the Attribute Inspector, under View, set the Mode of the Image View to "Aspect Fill". That gives it the correct parallaxy behavior.
To set the height of the Header Image correctly, we need a little bit of code. First, make an outlet from the constraint between the label's top edge and the Scrolling Content's top edge -- the one we set to a fixed value -- to your View Controller's source file. Call it imageHeightConstraint. Also add an outlet from the Image View called headerImageView. A good place to update the constraint programmatically is in the viewWillLayoutSubviews View Controller delegate method. Here is some code in Objective C:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
CGSize imageSize = self.headerImageView.image.size;
CGFloat heightForWidth = imageSize.height / imageSize.width;
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat screenWidth = CGRectGetWidth(screenBounds);
self.imageHeightConstraint.constant = screenWidth * heightForWidth;
}
Finally, you may notice that when scrolling up, the label -- or whatever content is underneath the header view -- is getting covered by it. This is solved by checking Clip Supbviews on the Image View's Attribute Inspector.
Whew!
(Note: I began writing this as a question and then kept writing while I solved it for myself. I guess it would do well with a little text pruning and some images, but maybe it will help someone...)

Centre a UIImageView inside a CGRect

I would like to centre a UIImageView inside a rectangular region on an iOS screen. The location and dimensions of the rectangle are determined at runtime and are represented by a CGRect. The image has already been drawn but has to move to the centre of the rectangular region at runtime. How can I achieve this?
It depends. If you are using auto-layout (the default) then you would use layout constraints. You should be able to set up layout constraints in IB that would keep the image view centered even if it changes size, without requiring any custom code.
Simply Select the view in IB, choose the button at the right that gives you a popup titled "Add New Alignment Constraints" and click the checkboxes "Horizontal Center in Container" and "Vertical Center in Container". Then click the "Add constraints" button at the bottom of the popup.
Assuming you want the change to occur instantaneously, you should simply be able to change the frame for the image view to your desired CGRect:
imageView.frame = rect
This is assuming your UIImageView autosizes and/or has the correct aspect ratio. If not, you should adjust your CGRect accordingly. Alternatively, you can look into animating the frame property.
If you're just going to be doing this when the view initially loads, it would be much easier center the image with Auto Layout by horizontally and vertically centering it in its container, as Duncan C suggested.

Autoarrange subviews in a zoomed UIView

I'm trying to zoom in and out an UIView, and rearrange it content to look similar for both states: zoomed and normal.
This picture shows the default state (the view that I'm going to zoom has orange color and has 5 UIImageViews) :
When I press "Zoom in" button I change orange view frame:
_page.frame = self.view.bounds;
And I'm getting the following result:
But the goal that I want to achieve is something similar to this (same result if I would scale the view):
It means that I must change frames for each subview, but it could be complicated when view would have many objects on it.
What I'm asking for are some hints or methods how can I get desired result without accessing subviews.
There are be hacks to do this, but the proper way would be to use auto layout. You don't have to access any subviews and will be able to do it in the storyboard/IB.
If you use auto layout, you can actually create constraints which will pin the following attributes of the subviews:
Pin the top subview's top space and leading space to the container
Pin the all but the last subviews' vertical distance to its nearest neighbour and leading space to container
Pin the last subview's top vertical space to its nearest neighbour and bottom space to container and leading space to container
Set constraints for height and width but set the priority to low
In addition to setting the frame (which just changes the size of the view) you want to change the transform (scale the view) Try something like:
_page.transform = CGAffineTransformMakeScale(2.0, 2.0)
You'll probably want to calculate the scale factor based on the old view size and the new size.

Maintaining subview relative location after rotation

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

Resources