I have viewController in storyboard. And 4 squares. I want to place my squares on view. At first I want to show two squares. If I press on button I want to my red 2 squares move left and I show next 2 blue squares. Like this animation.
Do I need to create a scrollView or collectionView or something else to move the squares? What the best way to create this?
Use UiVew.animate and CGAffineTransform
Setup
Create a UIView that will contain your squares. Make sure that UIView has clip to bounds enabled. Then, add your four squares in, with the blue ones nested inside the UIView, but with their coordinates outside of the container so that they're getting clipped.
Then, when you want to move them, you simply translate all of your squares x to the left. By putting this movement inside of a UIView.animate block, iOS will perform the animation for you.
UIView.animate(withDuration: 2.0) {
self.redTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.redBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
}
Final result: http://g.recordit.co/SToMSZ77wu.gif
I have the following code, which basically just grows a UIImage from 0 to its intended height:
let bounds = self.tankLevelRep.bounds
UIView.transition(with: self.tankLevelRep, duration: 2.0, animations: {self.tankLevelRep.bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width, height: 118.0)}, completion: nil)
This works fine except for the fact that it grows from the middle. I would like it to grow upwards from the bottom. The attached graphic shows the animation as it nears its end. You can see that the animation is not starting from the bottom, but rather, from the center, and growing outwards. I would like for it to start at the bottom, and the image to grow upwards.
What can I change in my code to make this happen?
Notes:
• "tankLevelRep" is the outlet name for my UIImage
You should update the frame and the bounds mirror the change, in your code make sure you animate from the bottom by setting the origin to bounds.size.height instead of the origin.y. Here is a nice fill animation from another post.
So I've got three images that I want arranged randomly. I'm using the origin of the three objects for the base point. I've tried a few different things, but they won't budge.
It's important to note that I have constraints on these images. They have a padding on top & bottom with a padding of 0 on the sides. They are also constrained to be the same size, in case constraint settings limit the movement of objects.
//the starting positions of the objects
spot_1 = img1.frame.origin
spot_2 = img2.frame.origin
spot_3 = img3.frame.origin
I've tried a few different tweaks on these sets of code:
img2.frame = CGRect(x: spot_1.x, y: spot_1.y, width: box_width, height: box_width)
img3.frame = CGRect(x: spot_2.x, y: spot_2.y, width: box_width, height: box_width)
img1.frame = CGRect(x: spot_3.x, y: spot_3.y, width: box_width, height: box_width)
&
img2.frame = spot_1
img3.frame = spot_2
img1.frame = spot_3
I've tried animating, different versions of CGrect etc. Can't get it figured out. Thank you for any help!
Trying to use both (Auto Layout) constraints and frames just isn't compatible. Constraints determine and set a view's frame. You shouldn't programmatically change the frame.
If you want to move a constrained object, what you would do is programmatically modify its position constraint(s).
You can programmatically change a constraint by adding an IBOutlet and connecting the Storyboard constraint to your property.
#IBOutlet weak var image1LeadingSpaceConstraint: NSLayoutConstraint!
Then in code, you can change the constraint's constant to set a new margin for your image
image1LeadingSpaceConstraint.constant = ...
You can even animate your change so the image animates to its new location
[UIView animateWithDuration:2.0 animations:^{
[self.view layoutIfNeeded];
}];
I'm trying to create a Parallax effect on a UIView inside a UIScrollView.
The effect seems to work, but not so well.
First i add two UIView sub-views to a UIScrollView and set the UIScrollViews contentSize.
The Views sum up and create a contentSize of {320, 1000}.
Then I implemented the following in scrollViewDidScroll:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat percentage = offsetY / scrollView.contentSize.height;
NSLog(#"percent = %f", percentage);
if (offsetY < 0) {
firstView.center = CGPointMake(firstView.center.x, firstView.center.y - percentage * 10);
} else if (offsetY > 0){
firstView.center = CGPointMake(firstView.center.x, firstView.center.y + percentage * 10);
}
}
These lines of code do create a parallax effect, but as the scrolling continues, the view does not return to it's original position if i scroll to the original starting position.
I have tried manipulating the views layers and frame, all with the same results.
Any Help will be much appreciated.
The problem you have is that you are basing your secondary scrolling on a ratio of offset to size, not just on the current offset. So when you increase from an offset of 99 to 100 (out of say 100) your secondary scroll increases by 10, but when you go back down to 99 your secondary scroll only decreases by 9.9, and is thereby no longer in the same spot as it was last time you were at 99. Non-linear scrolling is possible, but not the way you are doing it.
A possible easier way to deal with this is to create a second scrollview and place it below your actual scrollview. Make it non intractable (setUserInteractionEnabled:false) and modify it's contentOffset during the main scrolling delegate instead of trying to move a UIImageView manually.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[scrollView2 setContentOffset:CGPointMake(scrollView.contentOffset.x,scrollView.contentOffset.y * someScalingFactor) animated:NO];
}
But make sure not to set a delegate for the scrollView2, otherwise you may get a circular delegate method call that will not end well for you.
Scaling Factor being the key element...
...let me offer a 1:1 calculation:
Assuming 2 UIScrollView, one in the foreground and on in the rear, assuming the foreground controls the rear, and further assuming that a full width in the foreground corresponds to a full width in the background, you then need to apply the fore ratio, not the fore offset.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let foreSpan = foreScrolView.bounds.width - foreScrolView.contentSize.width
let foreRatio = scrollView.contentOffset.x / foreSpan
let rearSpan = rearScrollView.bounds.width - rearScrollView.contentSize.width
rearScrollView.setContentOffset(
CGPoint(x: foreRatio * rearSpan, y: 0),
animated: false)
}
Final effect
The two scrollers, fore and rear, each contain a UIImageView displayed at its full width:
let foreImg = UIImageView.init(image: UIImage(named: "fore"))
foreImg.frame = CGRect(x: 0, y: 0,
width: foreImg.frame.width,
height: foreScrolView.bounds.height)
foreScrolView.contentSize = foreImg.frame.size
foreScrolView.addSubview(foreImg)
let rearImg = UIImageView.init(image: UIImage(named: "rear"))
rearImg.frame = CGRect(x: 0, y: 0,
width: rearImg.frame.width,
height: rearScrollView.bounds.height)
rearScrollView.contentSize = rearImg.frame.size
rearScrollView.addSubview(rearImg)
This will scroll both images at a different speed, covering each image in full from edge to edge.
► Find this solution on GitHub and additional details on Swift Recipes.
I'm trying to understand the functionalities of these methods. Could you provide me with a simple use case to understand their semantics?
From the documentation, for example, convertPoint:fromView: method is described as follows:
Converts a point from the coordinate system of a given view to that of the receiver.
What does the coordinate system mean? What about the receiver?
For example, does it make sense using convertPoint:fromView: like the following?
CGPoint p = [view1 convertPoint:view1.center fromView:view1];
Using NSLog utility, I've verified that p value coincides with view1's center.
Thank you in advance.
EDIT: For those interested, I've created a simple code snippet to understand these methods.
UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)];
view1.backgroundColor = [UIColor redColor];
NSLog(#"view1 frame: %#", NSStringFromCGRect(view1.frame));
NSLog(#"view1 center: %#", NSStringFromCGPoint(view1.center));
CGPoint originInWindowCoordinates = [self.window convertPoint:view1.bounds.origin fromView:view1];
NSLog(#"convertPoint:fromView: %#", NSStringFromCGPoint(originInWindowCoordinates));
CGPoint originInView1Coordinates = [self.window convertPoint:view1.frame.origin toView:view1];
NSLog(#"convertPoint:toView: %#", NSStringFromCGPoint(originInView1Coordinates));
In both cases self.window is the receiver. But there is a difference. In the first case the convertPoint parameter is expressed in view1 coordinates. The output is the following:
convertPoint:fromView: {100, 100}
In the second one, instead, convertPoint is expressed in superview (self.window) coordinates. The output is the following:
convertPoint:toView: {0, 0}
Each view has its own coordinate system - with an origin at 0,0 and a width and height. This is described in the bounds rectangle of the view. The frame of the view, however, will have its origin at the point within the bounds rectangle of its superview.
The outermost view of your view hierarchy has it's origin at 0,0 which corresponds to the top left of the screen in iOS.
If you add a subview at 20,30 to this view, then a point at 0,0 in the subview corresponds to a point at 20,30 in the superview. This conversion is what those methods are doing.
Your example above is pointless (no pun intended) since it converts a point from a view to itself, so nothing will happen. You would more commonly find out where some point of a view was in relation to its superview - to test if a view was moving off the screen, for example:
CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];
The "receiver" is a standard objective-c term for the object that is receiving the message (methods are also known as messages) so in my example here the receiver is superview.
I always find this confusing so I made a playground where you can visually explore what the convert function does. This is done in Swift 3 and Xcode 8.1b:
import UIKit
import PlaygroundSupport
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Main view
view.backgroundColor = .black
view.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
// Red view
let redView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460))
redView.backgroundColor = .red
view.addSubview(redView)
// Blue view
let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420))
blueView.backgroundColor = .blue
redView.addSubview(blueView)
// Orange view
let orangeView = UIView(frame: CGRect(x: 20, y: 20, width: 380, height: 380))
orangeView.backgroundColor = .orange
blueView.addSubview(orangeView)
// Yellow view
let yellowView = UIView(frame: CGRect(x: 20, y: 20, width: 340, height: 100))
yellowView.backgroundColor = .yellow
orangeView.addSubview(yellowView)
// Let's try to convert now
var resultFrame = CGRect.zero
let randomRect: CGRect = CGRect(x: 0, y: 0, width: 100, height: 50)
/*
func convert(CGRect, from: UIView?)
Converts a rectangle from the coordinate system of another view to that of the receiver.
*/
// The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view:
resultFrame = view.convert(randomRect, from: yellowView)
// Try also one of the following to get a feeling of how it works:
// resultFrame = view.convert(randomRect, from: orangeView)
// resultFrame = view.convert(randomRect, from: redView)
// resultFrame = view.convert(randomRect, from: nil)
/*
func convert(CGRect, to: UIView?)
Converts a rectangle from the receiver’s coordinate system to that of another view.
*/
// The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view
resultFrame = yellowView.convert(randomRect, to: view)
// Same as what we did above, using "from:"
// resultFrame = view.convert(randomRect, from: yellowView)
// Also try:
// resultFrame = orangeView.convert(randomRect, to: view)
// resultFrame = redView.convert(randomRect, to: view)
// resultFrame = orangeView.convert(randomRect, to: nil)
// Add an overlay with the calculated frame to self.view
let overlay = UIView(frame: resultFrame)
overlay.backgroundColor = UIColor(white: 1.0, alpha: 0.9)
overlay.layer.borderColor = UIColor.black.cgColor
overlay.layer.borderWidth = 1.0
view.addSubview(overlay)
}
}
var ctrl = MyViewController()
PlaygroundPage.current.liveView = ctrl.view
Remember to show the Assistant Editor (⎇⌘⏎) in order to see the views, it should look like this:
Feel free to contribute more examples here or in this gist.
Here's an explanation in plain English.
When you want to convert the rect of a subview (aView is a subview of [aView superview]) to the coordinate space of another view (self).
// So here I want to take some subview and put it in my view's coordinate space
_originalFrame = [[aView superview] convertRect: aView.frame toView: self];
Every view in iOS have a coordinate system. A coordinate system is just like a graph, which has x axis(horizontal line) and y axis(vertical line). The point at which the lines interesect is called origin. A point is represented by (x, y). For example, (2, 1) means that the point is 2 pixels left, and 1 pixel down.
You can read up more about coordinate systems here - http://en.wikipedia.org/wiki/Coordinate_system
But what you need to know is that, in iOS, every view has it's OWN coordinate system, where the top left corner is the origin. X axis goes on increasing to the right, and y axis goes on increasing down.
For the converting points question, take this example.
There is a view, called V1, which is 100 pixels wide and 100 pixels high. Now inside that, there is another view, called V2, at (10, 10, 50, 50) which means that (10, 10) is the point in V1's coordinate system where the top left corner of V2 should be located, and (50, 50) is the width and height of V2. Now, take a point INSIDE V2's coordinate system, say (20, 20). Now, what would that point be inside V1's coordinate system? That is what the methods are for(of course you can calculate themselves, but they save you extra work). For the record, the point in V1 would be (30, 30).
Hope this helps.
Thank you all for posting the question and your answers: It helped me get this sorted out.
My view controller has it's normal view.
Inside that view there are a number of grouping views that do little more than give their child views a clean interaction with auto layout constraints.
Inside one of those grouping views I have an Add button that presents a popover view controller where the user enters some information.
view
--groupingView
----addButton
During device rotation the view controller is alerted via the UIPopoverViewControllerDelegate call popoverController:willRepositionPopoverToRect:inView:
- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view
{
*rect = [self.addButton convertRect:self.addbutton.bounds toView:*view];
}
The essential part that comes from the explanation given by the first two answers above was that the rect I needed to convert from was the bounds of the add button, not its frame.
I haven't tried this with a more complex view hierarchy, but I suspect that by using the view supplied in the method call (inView:) we get around the complications of multi-tiered leaf view kinds of ugliness.
I used this post to apply in my case. Hope this will help another reader in the future.
A view can only see its immediate children and parent views. It can't see its grand parents or its grandchildren views.
So, in my case, I have a grand parent view called self.view, in this self.view I have added subviews called self.child1OfView, self.child2OfView. In self.child1OfView, I have added subviews called self.child1OfView1, self.child2OfView1.
Now if I physically move self.child1OfView1 to an area outside the boundary of self.child1OfView to anther spot on self.view, then to calculator the new position for the self.child1OfView1 within the self.view:
CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView];
You can see below code so you can understand that how it actually works.
let scrollViewTemp = UIScrollView.init(frame: CGRect.init(x: 10, y: 10, width: deviceWidth - 20, height: deviceHeight - 20))
override func viewDidLoad() {
super.viewDidLoad()
scrollViewTemp.backgroundColor = UIColor.lightGray
scrollViewTemp.contentSize = CGSize.init(width: 2000, height: 2000)
self.view.addSubview(scrollViewTemp)
let viewTemp = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 150, height: 150))
viewTemp.backgroundColor = UIColor.green
self.view.addSubview(viewTemp)
let viewSecond = UIView.init(frame: CGRect.init(x: 100, y: 700, width: 300, height: 300))
viewSecond.backgroundColor = UIColor.red
self.view.addSubview(viewSecond)
self.view.convert(viewTemp.frame, from: scrollViewTemp)
print(viewTemp.frame)
/* First take one point CGPoint(x: 10, y: 10) of viewTemp frame,then give distance from viewSecond frame to this point.
*/
let point = viewSecond.convert(CGPoint(x: 10, y: 10), from: viewTemp)
//output: (10.0, -190.0)
print(point)
/* First take one point CGPoint(x: 10, y: 10) of viewSecond frame,then give distance from viewTemp frame to this point.
*/
let point1 = viewSecond.convert(CGPoint(x: 10, y: 10), to: viewTemp)
//output: (10.0, 210.0)
print(point1)
/* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewSecond frame,then give distance from viewTemp frame to this rect.
*/
let rect1 = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), to: viewTemp)
//output: (10.0, 210.0, 20.0, 20.0)
print(rect1)
/* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewTemp frame,then give distance from viewSecond frame to this rect.
*/
let rect = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), from: viewTemp)
//output: (10.0, -190.0, 20.0, 20.0)
print(rect)
}
I read the answer and understand the mechanics but I think the final example is not correct. According to the API doc, the center property of a view contains the known center point of the view in the superview’s coordinate system.
If this is the case, than I think it would not make sense to try to ask the superview to convert the center of a subview FROM the subview coordinate system because the value is not in the subview coordinate system. What would make sense is to do the opposite i.e. convert from the superview coordinate system to that of a subview...
You can do it in two ways (both should yield the same value):
CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview];
or
CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview];
Am I way off in understanding how this should work?
One more important point about using these APIs. Be sure that the parent view chain is complete between the rect you are converting and the to/from view.
For example - aView, bView, and cView -
aView is a subview of bView
we want to convert aView.frame to cView
If we try to execute the method before bView has been added as a subview of cView, we will get back a bunk response. Unfortunately there is no protection built into the methods for this case. This may seem obvious, but it is something to be aware of in cases where the conversion goes through a long chain of parents.