How to add parameters to UIViewController extension? - ios

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")

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)
}
}

Why my UILabel doesn't conforms to the inital frame?

I'm trying to create a popup label over the imageview. But in my application it doesn't work. I've made a test application with the same screen: UIImageView and below a UIView with a UIButton on the view.
So, where are two questions.
What could be difference in a code for such a different behaviour?
Why in my application the UILabel doesn't conforms to the initial frame?
The code of the function inside my viewController is the same:
private func showBanner(startY: CGFloat, targetView: UIView) {
let height: CGFloat = 42
let finishY = startY - height
let bannerLabel = UILabel(frame: CGRect(x: 0, y: startY, width: self.view.frame.width, height: height))
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
bannerLabel.font = UIFont.systemFont(ofSize: 13, weight: .regular)
bannerLabel.textColor = .lightGray
bannerLabel.backgroundColor = .black
bannerLabel.textAlignment = .center
bannerLabel.numberOfLines = 1
bannerLabel.text = "You've added the item to the favorites"
targetView.addSubview(bannerLabel)
UIView.animate(withDuration: 0.5, animations: {
bannerLabel.frame = CGRect(x: 0,
y: finishY,
width: self.view.frame.width,
height: height
)
}) {
_ in
UIView.animate(withDuration: 0.5, delay: 1.0, options: .curveLinear, animations: {
bannerLabel.frame = CGRect(x: 0,
y: startY,
width: self.view.frame.width,
height: height
)
}, completion: {
_ in
bannerLabel.removeFromSuperview()
})
}
}
The function is being called so:
showBanner(startY: itemsImageView.frame.maxY, targetView: itemsImageView)
The problem was in the line:
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
As soon as this line was removed, the problem has dissapeared.

Slide in Menu for 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)

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.:)

UIView.animateWithDuration swift loop animation

In ViewController.Swift I managed to make a box animate from one point to another. I thought it would be easy to loop this so the box will animate to one point and then animate back to its original position and then loop again. I have managed to move the object to a position and in "complete" move it back again, but that doesn't make i loop. How can this be achieved?
I thought maybe this could work but i honestly don't know:
let boxmoves = [CGRect(x: 120, y: 220, width: 100, height: 100), CGRect(x: 120, y: 120, width: 100, height: 100)]
for boxmove in boxmoves {
coloredSquare.frame = boxmove
}
How could I center it based on the device-width (I assume there are some math involved?)?
My code:
let coloredSquare = UIView()
coloredSquare.backgroundColor = UIColor.blueColor()
coloredSquare.frame = CGRect(x: 120, y: 120, width: 100, height: 100)
self.view.addSubview(coloredSquare)
// found repeate but this will not animate as I want.
//UIView.animateWithDuration(2.0, delay: 0.2, options: UIViewAnimationOptions.Repeat, animations: {
UIView.animateWithDuration(2.0, animations: {
coloredSquare.frame = CGRect(x: 120, y: 220, width: 100, height: 100)
}, completion: { finished in
UIView.animateWithDuration(2.0, animations: {
coloredSquare.frame = CGRect(x: 120, y: 120, width: 100, height: 100)
})
})
No need to do the completion block approach, just use the animation options argument:
updated for Swift 3.0
UIView.animate(withDuration: 2.0, delay: 0, options: [.repeat, .autoreverse], animations: {
coloredSquare.frame = CGRect(x: 120, y: 220, width: 100, height: 100)
}, completion: nil)
If for any reason you want to stop the animation later, just use:
coloredSquare.layer.removeAllAnimations()
UIView.animate(withDuration: 3.0,
delay: 0.0,
options: [.curveLinear, .repeat],
animations: { () -> Void in
coloredSquare.frame = CGRect(x: 120, y: 220, width: 100, height: 100)
}, completion: { (finished: Bool) -> Void in
})

Resources