Animate video UIView to go full screen like YouTube App - swift

I have a view controller that has a UIView that I call playerView. In playerView, I use an AVLayer and AVPlayerLayer to show the user a video. Image the Youtube app when you first click on any video.
How do I manipulate the frame of this playerView so that it can take up the entire screen. (Go full screen)
This is the current frame:
//16 x 9 is the aspect ratio of all HD videos
let height = view.frame.width * 9 / 16
let playerFrame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
playerView.frame = videoPlayerFrame
I was testing around in the YouTube app and their app only supports Portrait orientation (just like mine) due to how the animation of a video going full screen looks. How can I do this at the the tap of a button and even further when the user holds the device horizontally?
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.playerView.frame = ?
}, completion: nil)
Edit: Tried this but it doesn't really work how I want it... it doesn't take up the whole screen
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.playerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.height, height: self.view.frame.width)
self.playerView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
}, completion: nil)

I figured it out.
I create a global variable called isExpanded that will be set to true when we're in full screen mode. Also, I create a CGPoint variable called videoPlayerViewCenter so I can save the center before going full screen so I can set it again when going back to small screen.
This is the code that gets called when the user wants to go full screen
func fullScreenTapped(sender: vVideoPlayer) {
if !isExpanded {
//Expand the video
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.videoPlayerViewCenter = self.videoPlayerView.center
self.videoPlayerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.height, height: self.view.frame.width)
self.videoPlayerView.center = self.view.center
self.videoPlayerView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi / 2))
self.videoPlayerView.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.videoPlayerView.transform = CGAffineTransform.identity
self.videoPlayerView.center = self.videoPlayerViewCenter
let height = self.view.frame.width * 9 / 16
let videoPlayerFrame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: height)
self.videoPlayerView.frame = videoPlayerFrame
self.videoPlayerView.layoutSubviews()
}, completion: nil)
}
isExpanded = !isExpanded
}
To expand the video, I save the center in my global CGPoint variable and then set the frame. Then, I set the center and do a 90 degree turn using CGAffineTransform.
To shrink the video again, I basically set the settings to how they were before. (The order in which things are done is very important)
Another thing that's also very important is to override your layoutSubviews method in your videoPlayerView to be like this:
override func layoutSubviews() {
super.layoutSubviews()
self.playerLayer?.frame = self.bounds
}
This will make it so your playerLayer adjusts its frame as the view is getting bigger.

I know is too late but maybe someone else can find this code helpful.
I use to use Auto layout constraints in my views so animating must have a different approach.
func setMapFullScreen(_ fullscreen: Bool) {
if fullscreen {
// Set the full screen constraints
self.setFullScreenLayoutConstraints()
// Hide the navigation bar
self.navigationController?.setNavigationBarHidden(true, animated: true)
} else {
// Set small screen constraints
self.setSmallScreenLayoutConstraints()
// Show the navigation bar
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
// Animate to full screen
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
}) { (finished) in
// ...
}
In my case a have I MKMapView and I want to tap on it in order to see the map fullscreen (and back).
This is the function that toogle the state.
As you can see, in the UIView.animate call, I animate only the self.view.layoutIfNeeded()
I also hide the navigation bar cause I have one ☺
Here my setSmallScreenLayoutConstraints and setFullScreenLayoutConstraints functions using SnapKit framework
func setSmallScreenLayoutConstraints() {
self.mapView.snp.remakeConstraints { (make) -> Void in
make.top.equalTo(self.mapLabel.snp.bottom).offset(8)
make.left.equalTo(self.view).offset(0)
make.right.equalTo(self.view).offset(0)
make.height.equalTo(150)
}
}
func setFullScreenLayoutConstraints() {
self.mapView.snp.remakeConstraints { (make) -> Void in
make.top.equalTo(self.view).offset(0)
make.left.equalTo(self.view).offset(0)
make.right.equalTo(self.view).offset(0)
make.bottom.equalTo(self.view).offset(0)
}
}
This is the result
Enjoy!!

As I said it is not an answer for your question. It is an idea and hope it helps you:
var rectangleView:UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Tap Gesture Recognizer for growing the rectangleView
let tapForward = UITapGestureRecognizer.init(target: self, action: #selector(self.tapForward(tap:)))
tapForward.numberOfTapsRequired = 1
// Configure the rectangleView
self.rectangleView = UIView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
self.rectangleView.transform = CGAffineTransform.init(scaleX: 0.65, y: 0.25)
self.rectangleView.backgroundColor = UIColor.red
self.rectangleView.addGestureRecognizer(tapForward)
self.view.addSubview(rectangleView)
}
And this going to be our tapForward(tap:) function:
func tapForward(tap:UITapGestureRecognizer) {
UIView.animate(withDuration: 0.5, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.8, options: .curveEaseInOut, animations: {
self.rectangleView.transform = CGAffineTransform.identity
}) { (completed:Bool) in
// Completion block
}
}

Related

Touch Labels That Keep Animating

I'm having an issue trying to touch a label that keeps animating because during the animation it becomes an image (set the animation on repeat). Is there a way to touch it during animation (I cant't use UITapGestureRecognizer) ?
func animatePro() {
UIView.animate(withDuration: 2, delay: 0, options: [.autoreverse, .repeat], animations: {
//1.5 times it's normal size
self.proLabel.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}){ (finished) in
self.proLabel
.transform = CGAffineTransform.identity
}
}
func animateRev() {
UIView.animate(withDuration: 2, delay: 0, options: [.repeat], animations: {
//1.5 times it's normal size
self.revLabel.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
}){ (finished) in
UIView.animate(withDuration: 2, delay: 0, animations: {
self.revLabel.transform = CGAffineTransform.identity
})
}
}
Add allowUserInteraction
options: [.allowUserInteraction,.autoreverse, .repeat]

How to create twitter tab bar push animation

I have the following code:
private var bounceAnimation: CAKeyframeAnimation = {
let bounceAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
bounceAnimation.values = [1.0, 1.4, 0.9, 1.02, 1.0]
bounceAnimation.duration = TimeInterval(0.3)
bounceAnimation.calculationMode = CAAnimationCalculationMode.cubic
return bounceAnimation
}()
This creates the animation where the icon gets bigger and then smaller. I am trying to create the animation where the icon gets smaller and then back to normal like it's being pushed similar to twitter, Spotify, etc. I assume it's just changing around the bounce values all though I'm not sure how would I do this.
I'd use a normal UIView.animate function like this:
UIView.animate(withDuration: 0.05, delay: 0, options: .curveLinear, animations: {
view.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
}, completion: nil)
UIView.animate(withDuration: 0.3, delay: 0.05, usingSpringWithDamping: 0.2, initialSpringVelocity: 7, options: .curveEaseOut, animations: {
view.transform = .identity
}, completion: nil)
Just change view to be whatever view you're trying to animate. Then mess around with the initial scale, the duration and the spring dampening to get the animation you want!

Swift UIView Animate doesn't work properly

Slide up animation will be triggered when a button is clicked.
func animation_onClick(action: String) {
let up = CGAffineTransform(translationX: 0, y: -30)
myView.transform = CGAffineTransform(translationX: 0, y: 0)
UIView.animate(withDuration: 0.5, delay: 0.0, options: [], animations: {
self.myView.transform = up
}, completion: nil)
}
My code means when the function runs, myView’s position will be set to (0, 0) referring to its original position every time before it was set to (0, -30).
But when I run the app, I repeatedly click the button, somehow myView doesn't go back to its origin, it goes below where it should be. What I mean is it's not supposed to go below its origin.
See the gif below.
Try the following
func animation_onClick(action: String) {
myView.layer.removeAllAnimations()
myView.transform = .identity
UIView.animate(withDuration: 0.5, delay: 0.0, options: [], animations: {
self.myView.transform = CGAffineTransform(translationX: 0, y: -30)
}, completion: nil)
}

Resize image to its initial size

I am using 2 animations.
when the screen is launched, the first animation starts in viewDidLoad.
this animation is applied to 2 images called layer2 and layer3.
func firstAnimation(){
UIView.animate(withDuration: 1, delay: 0, options:
UIViewAnimationOptions.repeat , animations: {
self.layer2.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
self.layer3.transform = CGAffineTransform(scaleX: 2, y: 2)
}, completion: { finished in
})
}
when an image called layer0 is long pressed, then firstAnimation() is being stopped by this code:
layer2.layer.removeAllAnimations()
layer3.layer.removeAllAnimations()
and different animation is being applied to layer2 and layer3.
the second animation is
func secondAnimation() {
UIView.animate(withDuration: 20, delay: 0, options:
UIViewAnimationOptions.curveEaseOut , animations: {
self.layer2.transform = CGAffineTransform(scaleX: 10, y: 10)
self.layer3.transform = CGAffineTransform(scaleX: 10, y: 10)
}, completion: { finished in
})
}
when I want to delete the second animation and launch the first one again, the 2 images layer2 and layer3 are starting from the size that they gained because of the second animation. how to relaunch the first animation with the initial sizes of the images?
You can reset views that were transformed to their original scale by using CGAffineTransform.identity.
For example:
layer2.transform = .identity

Prevent View from Snapping Back after Keyframe Animation Finished

I have a view which I am animating in such a way that it expands and moves to a new set of coordinates in the View Controller. I am doing this using Keyframes:
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: .CalculationModeCubic, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
//Moving main View
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
}
}, completion: nil)
The problem is that once the animation is completed, the view returns to its original position and size.
Is there a way to prevent this from happening in Swift?
This is an issue if one is using auto layout. In which case, the final coordinates must be put in the completion handler for it to work.
Swift 2
UIView.animateKeyframesWithDuration(0.5, delay: 0, options: .CalculationModeCubic, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5) {
// Moving main View
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
}
}, completion: {(finished: Bool) -> Void in
tappedView.frame = CGRectMake((self.screenSize.width/2)-250, (self.screenSize.height/2)-250, 500, 500)
})
Swift 3, 4, 5
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.5) {
// Moving main View
tappedView.frame = CGRect(x: (self.screenSize.width/2)-250, y: (self.screenSize.height/2)-250, width: 500, height: 500)
}
}, completion: {(finished: Bool) -> Void in
tappedView.frame = CGRect(x: (self.screenSize.width/2)-250, y: (self.screenSize.height/2)-250, width: 500, height: 500)
})