I want to create similar animation for all screen sizes. I have UIView in center and want to move at right and distance between UIView and screen should be = 10.
How to do it?
My code for one screen(iPhone 5s):
UIView.animate(withDuration: 1.0, delay: 0.0, options: [], animations: {
self.imageRevealed.center.x += 200
})
in this case distance between UIView and screen = 10. But for other screens I should write new code with new coordinates. How to write universal code?
I think you need the trailing space between image view and view of 10px.
let trailingSpace: CGFloat = 10
UIView.animate(withDuration: 1.0, delay: 0.0, options: [], animations: {
let screenSize = UIScreen.main.bounds.size
let x = screenSize.width - self.imageRevealed.frame.size.width - trailingSpace
let y = self.imageRevealed.frame.origin.y
self.imageRevealed.frame = CGRect(x: x, y: y, width: self.imageRevealed.frame.size.width, height: self.imageRevealed.frame.size.height)
})
Related
I have made a subclass of UIView that automatically adds a AVPlayerLayer along with the video controls. I need to implement a full screen button that takes the UIView and rotates it into fullscreen just like the YouTube app. The problem is the UIView does not cover the whole screen. I have a XLPagerTab below the UIView that contains the overview and stuff for the video.
This is my code to make the UIView full screen. It is inside the VideoPlayer Class which is a subclass of UIView. isExpanded is a boolean to know the state, smallScreenCenter is used to store the initial center of the view to switch back to the small screen.
func fullScreenTapped() {
if !isExpanded {
//Expand the video
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.height, height: UIScreen.main.bounds.width)
self.center = CGPoint(x: UIScreen.main.bounds.size.width * 0.5, y: UIScreen.main.bounds.size.height * 0.5)
self.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
self.layoutSubviews()
}, completion: nil)
} else {
//Shrink the video again
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
//16 x 9 is the aspect ratio of all HD videos
self.transform = CGAffineTransform.identity
self.center = self.smallScreenCenter!
let height = UIScreen.main.bounds.width * 9 / 16
let videoPlayerFrame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: height)
self.frame = videoPlayerFrame
self.layoutSubviews()
}, completion: nil)
}
isExpanded = !isExpanded
}
The view rotates and all but does not cover the whole screen, it just looks like this.
Is the XLPagerTab interfering with the fullscreen? I am confused.
For my personal project, I try to make an image of a leaf go up for 5 seconds and then lower it for 5 seconds.
It works well, only after raising and lowering the sheet, it changes position and is transported well to the bottom of the screen.
I would like it to go up from the place of the end of the descent. I have tried several things but nothing works. I wish I could restore the original position after descent but I think it will make the image flicker on the screen.
Any ideas ?
func upLeaf(){
let xPosition = imageLeaf.frame.origin.x
let yPosition = imageLeaf.frame.origin.y - 100 // Slide Up - 20px
let width = imageLeaf.frame.size.width
let height = imageLeaf.frame.size.height
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
})
}
func downLeaf(){
let xPosition = imageLeaf.frame.origin.x
let yPosition = imageLeaf.frame.origin.y + 100 // Slide Up - 20px
let width = imageLeaf.frame.size.width
let height = imageLeaf.frame.size.height
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame = CGRect(x: xPosition, y: yPosition, width: width, height: height)
})
secondUp = true
}
Instead of using frame to perform the animation you could use CGAffineTransform. It's more powerful and equally easy:
UIView.animate(withDuration: 5.0, animations: {
line3.transform = CGAffineTransform(translationX: 0, y: 100)
})
When you want to return to the initial state you should type:
UIView.animate(withDuration: 5.0, animations: {
line3.transform = CGAffineTransform.identity
})
CGAffineTransform remembers the initial parameters for your view, so that you don't have to do any calculations yourself.
If you only want the leaf to go up and down, you can just animate imageLeaf's frame.origin.y (no need to keep track of origin.x, width, or height).
func upDownLeaf() {
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame.origin.y -= 100 /// go up
}) { _ in
UIView.animate(withDuration: 5.0, animations: {
self.imageLeaf.frame.origin.y += 100 /// go down
})
}
}
I have two shortcuts.
Each of them has initial properties, such as font size and opacity.
Before animation, the values are
labelOne.font = labelOne.font.withSize(109)
labelOne.alpha = 1.0
labelTwo.font = labelTwo.font.withSize(40)
labelTwo.alpha = 0.7
After the animation, they should have these properties:
labelOne.font = labelOne.font.withSize(40)
labelOne.alpha = 0.7
labelTwo.font = labelTwo.font.withSize(109)
labelTwo.alpha = 1.0
For transformation, I use CGAffineTransform()
example:
labelOne.transform = CGAffineTransform(scaleX: //some value? , y: //someValue?)
But I'm new to programming and don't quite understand how it works. Tell me how to write this animation with font size resizing?
To achieve an animation on both labels, as you wished by controlling font size and alpha, you could achieve it like this:
// Before animation
labelOne.font = labelOne.font.withSize(109)
labelOne.alpha = 1.0
labelTwo.font = labelTwo.font.withSize(40)
labelTwo.alpha = 0.7
UIView.animate(withDuration: 1, animations: {
// This will be values set during the animation of 1 second
self.labelOne.font = labelOne.font.withSize(40)
self.labelOne.alpha = 0.7
self.labelTwo.font = labelTwo.font.withSize(109)
self.labelTwo.alpha = 1.0
})
Meanwhile, CGAffineTransform(scaleX:, y:) lets you scale the X,Y coordinates value of your view (labelOne for example), but to make that happen with an animation you have to put that inside the animate block:
// normal state
labelOne.transform = CGAffineTransform(scaleX: 1, y: 1)
labelTwo.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
UIView.animate(withDuration: 1, animations: {
// State reached through the animation
labelOne.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)
labelTwo.transform = CGAffineTransform(scaleX: 1, y: 1)
})
To achieve a smoother transition play with:
UIView.transition(with: labelOne, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.labelOne.font = UIFont.systemFont(ofSize: 40)
}) { _ in }
Let's say I scale a UILabel using a CGAffineTransformScale like so:
let scale = 0.5
text = UILabel(frame: CGRectMake(100, 100, 100, 100))
text.text = "Test"
UIView.animateWithDuration(2.0, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.text.transform = CGAffineTransformScale(self.text.transform, scale, scale)
}, completion: {(value : Bool) in
print("Animation finished")
})
This works great when I want to scale the UILabel by half. But if I were to call this same code again, it would end up with a scale of 0.25, as it scales again by half.
Would it be possible to use the CGAffineTransformScale to always scale to a size of half the original UILabel frame, instead of a scaling it cumulatively?
Swift 3:
text.transform = CGAffineTransform.identity
UIView.animate(withDuration: 0.25, animations: {
self.text.transform = CGAffineTransform(scaleX: scale, y: scale)
})
You are scaling the existing transform. Just create a new transform:
self.text.transform = CGAffineTransformMakeScale(scale, scale)
Swift version:
self.text.transform = CGAffineTransform.init(scaleX: scaleFactorX, y: scaleFactorY)
I'm trying to create a custom segue, where the first view controller shrinks a little and the second one slides over top of it. Here's what I have for segue implementation:
override func perform() {
let firstView = self.sourceViewController.view as UIView!
let secondView = self.destinationViewController.view as UIView!
let screenSize = UIScreen.mainScreen().bounds.size
let window = UIApplication.sharedApplication().keyWindow
secondView.frame = CGRectMake(0.0, screenSize.height, screenSize.width, screenSize.height)
window?.insertSubview(secondView, aboveSubview: firstView)
UIView.animateWithDuration(2, delay: 0, options: .CurveEaseIn, animations: {
// BEGIN RELEVANT PORTION
let offset: CGFloat = 20
firstView.frame = CGRectMake(offset, offset, screenSize.width - (offset * 2), screenSize.height - (offset * 2))
firstView.autoresizesSubviews = true
firstView.layoutIfNeeded()
// END RELEVANT PORTION
}, completion: nil)
UIView.animateWithDuration(5, delay: 1, options: .CurveEaseOut, animations: {
secondView.frame = CGRectMake(0.0, 0.0, screenSize.width, screenSize.height)
}, completion: nil)
}
This produces a result like this:
This is already really great, but not what I intended. I need the subviews ("Hi, I'm Matt." label and button) to resize proportionally to the superview, as if the entire view were falling down on the z axis.
I am using Auto Layout.
Thank you so much in advance!
I solved the problem using CGAffineTransform.
override func perform() {
let firstView = self.sourceViewController.view as UIView!
let secondView = self.destinationViewController.view as UIView!
let screenSize = UIScreen.mainScreen().bounds.size
let window = UIApplication.sharedApplication().keyWindow
secondView.frame = CGRectMake(0.0, screenSize.height, screenSize.width, screenSize.height)
window?.insertSubview(secondView, aboveSubview: firstView)
firstView.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
firstView.layer.position = CGPoint(x: 0.0, y: 0.0)
UIView.animateWithDuration(0.2, delay: 0, options: .CurveEaseInOut, animations: {
print(firstView.frame)
let offset: CGFloat = 25
let offsetX = 2 * (offset / firstView.frame.width)
let offsetY = 2 * (offset * (firstView.frame.height / firstView.frame.width) / firstView.frame.height)
let transform = CGAffineTransformScale(CGAffineTransformIdentity, 1 - offsetX, 1 - offsetY)
firstView.transform = transform
firstView.layer.position = CGPoint(x: offset, y: offset)
}, completion: nil)
UIView.animateWithDuration(0.5, delay: 0.05, options: .CurveEaseOut, animations: {
secondView.frame = CGRectMake(0.0, 0.0, screenSize.width, screenSize.height)
}) { finished -> Void in
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: nil)
}
}
Thanks for your help anyway, everyone!
Applying transform is a good view to achieve it.
There's another way with which you can achieve it with no risk of modifying the first viewcontroller's view's frame. Take a snapshot of the first viewcontroller's view and use it instead of the view itself. You can use the following method in UIView to take a snapshot:
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates;
I tried it using your Scenario in my Xcode now you have to follow these steps
1- Give UIButton & UILabel Leading and Trailing Space to their SuperView
2- Make UIButton central Vertically
3- Give Vertical Space from UIButton to UILabel
Important:
Hope it will work because then trick is that you have to give leading & trailing space to super View to make the InnerViews Shrink or expand w.r.t its superView
One way around can be:
Give both of them(Label and button) width constraints.
Make outlets of both width constraints. constraintWidthLabel, constraintWidthButton
You can make outlet of a constraint like this
When you perform segue you have to make these width constraints to zero with animation.
UIView.animateWithDuration(2, delay: 0, options: .CurveEaseIn, animations: {
// BEGIN RELEVANT PORTION
let offset: CGFloat = 20
constraintWidthLabel.constant = 0
constraintWidthButton.constant = 0;
firstView.frame = CGRectMake(offset, offset, screenSize.width - (offset * 2), screenSize.height - (offset * 2))
firstView.autoresizesSubviews = true
firstView.layoutIfNeeded()
// END RELEVANT PORTION
}, completion: nil)