Slide in Menu for iOS - ios

I'm trying to create a slide in menu that comes in from the left but I've done something and its coming in onto the right instead. I've programmatically done this not used storyboards as most of the time the constraints messes up.
for example heres a screenshot (probably something I've done):
Slide Menu - Gone Wrong
import UIKit
class SideInMenu: NSObject {
let blackView = UIView()
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
return cv
}()
#objc func showSlideMenu() {
//Show slide in menu Here
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target:
self, action: #selector(handleDismiss)))
window.addSubview(blackView)
window.addSubview(collectionView)
let width: CGFloat = 194
let x = window.frame.width - width
collectionView.frame = CGRectMake(x, 0, width, window.frame.height)
//collectionView.frame = CGRectMake(x, 0, width, window.frame.height)
blackView.frame = window.frame
blackView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)
}
}
#objc func handleDismiss() {
UIView.animate(withDuration: 0.5) {
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.collectionView.frame = self.CGRectMake(window.frame.width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}
}
}
override init() {
super.init()
}
func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {return CGRect(x: x, y: y, width: width, height: height)
}
}

Always remember that 0, 0 for x and y is always the top left of the screen and if the width and height is 20, 20, it will go from the top left, right 20px across and down 20px. The first thing you have to do inside you viewDidLoad is to set the collectionViews initial frame, which is off screen like this. DON'T FORGET MINUS WIDTH:
self.collectionView.frame = self.CGRectMake(-width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
Animate it in from the left like so:
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(0, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)
Dismiss with:
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping:
1, initialSpringVelocity: 1, options: .curveEaseOut,
animations: {
self.blackView.alpha = 1
self.collectionView.frame = self.CGRectMake(-width, 0, self.collectionView.frame.width, self.collectionView.frame.height)
}, completion: nil)

Related

Flip UIView without dim effect

I am trying to create a flip animation in one of my views. I managed to get the animation working but it looks horrible because the card (which has a white background color) dims its color to be darker during the animation.
Do you know if there is a way to get rid of this dim effect to maintain the original color of the cards during the whole animation?
For further reference, please find below a simple view controller reproducing my problem.
import UIKit
class ViewController: UIViewController {
private lazy var cardView: UIView = {
let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
backgroundView.backgroundColor = .white
return backgroundView
}()
private lazy var flipButton: UIButton = {
let flipButton = UIButton(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
flipButton.setTitle("Flip", for: .normal)
flipButton.setTitleColor(.red, for: .normal)
flipButton.addTarget(self, action: #selector(flipCard), for: .touchUpInside)
return flipButton
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
view.addSubview(cardView)
cardView.addSubview(flipButton)
cardView.center = view.center
}
#objc
func flipCard() {
UIView.transition(with: cardView, duration: 10, options: .transitionFlipFromRight, animations: nil, completion: nil)
}
}
u can use
UIView.animateKeyframes(withDuration: 6, delay: 0, options: []) {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
self.cardView.transform3D = CATransform3DRotate(self.cardView.layer.transform, CGFloat.pi, 0, 1, 0)
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 1) {
self.cardView.transform3D = CATransform3DRotate(self.cardView.layer.transform, CGFloat.pi, 0, 1, 0)
}
}
instead of
UIView.transition(with: cardView, duration: 10, options: .transitionFlipFromRight, animations: nil, completion: nil)
You can go through UIView.animate() also in Swift 5,
UIView.animate(withDuration: duration/2, delay: 0, options: .curveLinear) {
targetView.transform = CGAffineTransform.identity.rotated(by: .pi )
} completion: { (_) in
UIView.animate(withDuration: duration/2, delay: 0, options: .curveLinear) {
targetView.transform = CGAffineTransform.identity.rotated(by: .pi * 2)
}
}

How to add parameters to UIViewController extension?

I've successfully created an extension on UIViewController that allows me to call a function that triggers a UIView to animate down. However instead of just a plain UIView, I want to improve on the extension so that it is it takes two parameters (background color, and label text) that are a subview on the UIView 'popupAlert?
extension UIViewController {
func showAlert() {
let popupAlert = UIView(frame: CGRect(x: 0, y: -60, width: view.frame.width, height: 60))
popupAlert.backgroundColor = UIColor.brandSuccess()
let window = (UIApplication.shared.delegate as! AppDelegate).window!
window.addSubview(popupAlert)
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseIn, animations: {
popupAlert.transform = .init(translationX: 0, y: 60)
}) { (_) in
UIView.animate(withDuration: 0.6, delay: 4, options: .curveEaseIn, animations: {
popupAlert.transform = .init(translationX: 0, y: -60)
}, completion: { (_) in
popupAlert.removeFromSuperview()
})
}
}
}
extension UIViewController {
func showAlert(bgColor: UIColor, lblText: String) {
let popupAlert = UIView(frame: CGRect(x: 0, y: -60, width: view.frame.width, height: 60))
let popupLbl = UILabel(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 60))
popupAlert.backgroundColor = bgColor
popupLbl.text = lblText
popupLbl.textColor = .green
popupLbl.textAlignment = .center
let window = (UIApplication.shared.delegate as! AppDelegate).window!
popupAlert.addSubview(popupLbl)
window.addSubview(popupAlert)
UIView.animate(withDuration: 0.6, delay: 0, options: .curveEaseIn, animations: {
popupAlert.transform = .init(translationX: 0, y: 60)
}) { (_) in
UIView.animate(withDuration: 0.6, delay: 4, options: .curveEaseIn, animations: {
popupAlert.transform = .init(translationX: 0, y: -60)
}, completion: { (_) in
popupAlert.removeFromSuperview()
})
}
}
}
call
showAlert(bgColor: .red, lblText: "testtttt")

UIView bottom to top animation issue

This is a simple function I am using for animating a view from top to bottom and vice versa (if is top to bottom animation and else is bottom to top animation) :
#objc func openMenu(sender: UIButton) {
if sender.tag == 1 {
self.buttonView.tag = 2
self.moduleView = ModulesCollectionView(frame: CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0), collectionViewLayout: UICollectionViewLayout())
self.window?.addSubview(self.moduleView)
UIView.animate(withDuration: 0.7, animations: {
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - self.frame.size.height - 22)
}, completion: { _ in
})
} else {
self.buttonView.tag = 1
UIView.animate(withDuration: 3, animations: {
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0)
}, completion: { _ in
self.moduleView.removeFromSuperview()
})
}
}
Top animation works fine and the view is animated from top to bottom pleasantly in 0.7 seconds. However, bottom to top animation does not happen. The view is removed instantly. This is the result I am getting :
But I want the animation to be clean while going from bottom to top as well. Just like here.
Secondary : What I finally plan to achieve is PullUpController with the exact reverse animation. So if anyone knows a similar library (pull down drag) can share there inputs.
Update : The issue is coming only with UICollectionView. I replaced collectionView with a simple UIView and it worked perfect.
You should try to use layoutSubViews() method after at the end of animation block. Change the animation block like this.
For if block:
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - self.frame.size.height - 22)
self.moduleView.layoutSubviews()
For else block:
self.moduleView.frame = CGRect(x: 0, y: self.frame.origin.y + self.frame.size.height + 20, width: UIScreen.main.bounds.size.width, height: 0)
self.moduleView.layoutSubviews()
Here is example code, hope it will help.
Show view:
self.containerView = ModulesCollectionView(frame: UIScreen.main.bounds)
self.containerView.center = CGPoint(x: self.view.center.x,
y: self.view.center.y + self.view.frame.size.height)
self.window?.addSubview(self.moduleView)
self.window?.bringSubview(toFront: self.moduleView)
self.window?.endEditing(true)
UIView.animate(withDuration: 0.3, delay: 0.0,
usingSpringWithDamping: 0.7, initialSpringVelocity: 3.0,
options: .allowAnimatedContent, animations: {
self.moduleView.center = self.view.center
}) { (isFinished) in
self.view.layoutIfNeeded()
}
hide view:
UIView.animate(withDuration: 0.7, delay: 0.0,
usingSpringWithDamping: 1, initialSpringVelocity: 1.0,
options: .allowAnimatedContent, animations: {
self.moduleView.center = CGPoint(x: self.view.center.x,
y: self.view.center.y + self.view.frame.size.height)
}) { (isFinished) in
self.moduleView.removeFromSuperview
}

create a custom view programmatically then animate it

I'm creating a custom view programmatically, which is working fine, then I'm trying to animate the view. For some strange reason the view is animating at all, I'm not sure why. Here is the code:
override func viewDidLoad() {
super.viewDidLoad()
let customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
UIView.animate(withDuration: 4, animations: {
customView.transform.translatedBy(x: 40, y: 60)
customView.transform.rotated(by: CGFloat.pi/2)
customView.transform.scaledBy(x: 0, y: 0.5)
})
}
you better call this animation block in viewDidAppear, with some delay.
import UIKit
class ViewController: UIViewController {
var customView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 5, delay: 1, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.customView.transform = CGAffineTransform(scaleX: 1, y: 0.5)
self.customView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
self.customView.transform = CGAffineTransform(translationX: 40, y: 60)
}, completion: nil)
}
func handleTap() {
UIView.animate(withDuration: 5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.customView.transform = CGAffineTransform(scaleX: 1, y: 0.5)
self.customView.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
self.customView.transform = CGAffineTransform(translationX: 40, y: 60)
}, completion: nil)
}
}
Your question is not correct. your view is not animating.
Why?
You are using a return function and your customView needs to catch the modified version like this.
customView.transform = customView.transform.translatedBy(x: 40, y: 60)
So your code would be like this.
let customView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
customView.backgroundColor = UIColor.blue
customView.layer.cornerRadius = 25
customView.layer.borderWidth = 8
customView.layer.borderColor = UIColor.red.cgColor
self.view.addSubview(customView)
UIView.animate(withDuration: 4, animations: {
customView.transform = customView.transform.translatedBy(x: 40, y: 60)
//customView.transform = customView.transform.rotated(by: CGFloat.pi/2)
//customView.transform = customView.transform.scaledBy(x: 0, y: 0.5)
})
There are lots of online resources how to group animation.Just google it.:)

Combine translation, alpha and scale animations in swift3

I'm totally newbies with iOS Swift developement and i try to combine three parameters in a single animations but i don't succeed.
I think the solution is here -Apple Dev Core Animation Programming Guide by grouping the animations but being a beginner and after a lot of Internet research i can't find what i'm looking for.
What do you think of my code and what is for you the best solution to combine performance and stability.
I want to point out that the purpose of this animation is to create an animated Splashscreen. There are other elements (UIImage) that will be to animates.
Here is my code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
logoImg.alpha = 0
logoImg.transform = CGAffineTransform(translationX: 0, y: -200)
logoImg.transform = CGAffineTransform(scaleX: 0, y: 0)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
self.logoImg.transform = CGAffineTransform(translationX: 0, y: 0)
self.logoImg.transform = CGAffineTransform(scaleX: 1, y: 1)
self.logoImg.alpha = 1
}, completion: nil)
}
Based on what I am seeing you are wanting to preset the animation and translate it back. In that case I would do this.
self.logoImg.transform = CGAffineTransform(translationX: 0, y: -200).concatenating(CGAffineTransform(scaleX: 0, y: 0))
self.logoImg.alpha = 0
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
self.logoImg.transform = .identity
self.logoImg.alpha = 1
}, completion: nil)
I think you may not be seeing all the animation so try to start the scale at 0.5
self.logoImg.transform = CGAffineTransform(translationX: 0, y: -200).concatenating(CGAffineTransform(scaleX: 0.5, y: 0.5))
self.logoImg.alpha = 0
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
self.logoImg.transform = .identity
self.logoImg.alpha = 1
}, completion: nil)
The key here is that the animation is animating back the original identity. Hope this helps
You can use concatenating method to combining two existing affine transforms.
UIView.animate(withDuration: 1.0, delay: 1.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10, options: [.curveEaseOut], animations: {
let translation = CGAffineTransform(translationX: 0, y: 0)
let scale = CGAffineTransform(scaleX: 1, y: 1)
self.logoImg.transform = translation.concatenating(scale)
self.logoImg.alpha = 1
}, completion: nil)
Look at Apple Document for more info. Hope it help. :)
Rotate and Translate and make it like parallax effect in tableview header:
func setupTableHeaderView() {
self.customHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 200))
self.customHeaderView?.backgroundColor = .white
self.customImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 200))
self.customImageView?.image = ImageNamed(name: "camera")
self.customImageView?.contentMode = .center
self.customHeaderView?.addSubview(self.customImageView!)
self.tableHeaderView = self.customHeaderView
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let yPos = scrollView.contentOffset.y
if yPos < 0 {
let scaleX = ((yPos * -1) / 200) + 1
let translateY = yPos / 2
var commonTransform = CGAffineTransform.identity
commonTransform = commonTransform.translatedBy(x: 0, y: translateY)
commonTransform = commonTransform.scaledBy(x: scaleX, y: scaleX)
self.customImageView?.transform = commonTransform
}else{
self.customImageView?.transform = CGAffineTransform.identity
}
}
NOTE : Make sure the View you apply transform to is SUBVIEW of the header view, not header view itself. In above example customImageView is subview of main headerview.
You can use this also with swift 3.0.1:
UIView.transition(with: self.imageView,
duration:0.5,
options: .transitionCrossDissolve,
animations: { self.imageView.image = newImage },
completion: nil)
Reference: https://gist.github.com/licvido/bc22343cacfa3a8ccf88

Resources