Rotate UIBarButtonItem Swift - ios

In Swift, I have a hamburger bar button, so when tapped I want that hamburger Bar button to rotate 90 degrees (so that the lines are vertical) and then when you click it again I would like it to go back to it's original state (horizontal)
NOTE: Can you make sure that this works for a UIBarButtonItem, because some solution to a normal UIButton does not work.

I use a UIButton inside of UIBarButtonItem to achieve this, and a variable with state vertical or not
this is my storyboard setup
Here is the code of simple view controller
import UIKit
class ViewController: UIViewController {
var isVertical : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func rotateAction(_ sender: Any) {
if(!self.isVertical)
{
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: 90 * .pi / 180)
}, completion: { (finished) in
self.isVertical = true
})
}else{
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform.identity
}, completion: { (finished) in
self.isVertical = false
})
}
}
}
Result
Hope this helps

#IBAction func rotateAction1(_ sender: Any) {
if (!self.isVertical) {
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: 90 * .pi / 180)
}, completion: {
(finished) in
self.isVertical = true
})
revealViewController().revealToggle(true)
} else {
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform.identity
}, completion: {
(finished) in
self.isVertical = false
})
revealViewController().revealToggle(false)
}
}

Swift 4:
func rotateBarButton() {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) // Create new button & set its frame
button.setImage(#imageLiteral(resourceName: "settings"), for: UIControlState()) // Assign an image
let lef = UIBarButtonItem(customView: button)
self.navigationItem.leftBarButtonItem = lef// Set as barButton's customView
// Gets you half way there //
UIView.animate(withDuration: 0.8, delay: 0.1, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI))
}, completion: nil)
// Rotates all the way around //
UIView.animate(withDuration: 0.5, delay: 0.5, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.navigationItem.leftBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI * 2))
}, completion: nil)
}

Related

Wait until the user touches the screen. Swift

After something happens I make a view appear with a label,
let myView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
let label = UILabel(frame: CGRect(x:0,y:0,width: 100, height: 100))
myView.addSubview(lebel)
self.view.addSubview(myView)
myView.alpha = 0
UIView.animate(withDuration: 0.5, animations: {
myView.alpha = 1
})
Now I want you to wait until a tap is made on the screen, and then
UIView.animate(withDuration: 0.5, animations: {
muView.alpha = 0
}, completion: { (bool) in
myView.removeFromSuperview()
})
How can I wait until I touch the screen?
Just add tapGesture to Your View
At viewDidload
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
self.view.addGestureRecognizer(tapGesture)
handleTapGesture
#IBAction func handleTapGesture(_ recognizer: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.5, animations: {
muView.alpha = 0
}, completion: { (bool) in
myView.removeFromSuperview()
})
}
In addtion to Abdelahad's answer, you should declare let myView as a property to use it within #IBAction func handleTapGesture(_ recognizer: UITapGestureRecognizer) {}. So, the new code is the following:
#IBAction func handleTapGesture(_ recognizer: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.5, animations: {
self.myView.alpha = 0
}, completion: { (bool) in
self.myView.removeFromSuperview()
})
}
Finally adding, I found your slight mistake in your code. muView is wrong and myView is correct.

Animate (Rotate) UIBarButtonItem custom buttons

All I want to do is rotating the UIBarButtonItem when it is clicked.
I followed this post Rotate UIBarButtonItem Swift, but it is not working.
The difference is:
1- I set the image on runtime when view is loaded:
showHideBtn.image = showHeaderimage
2- I have two buttons in my right Bar button items:
Here is my code:
#IBAction func rotateAction(_ sender: Any) {
if(!self.isVertical)
{
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.rightBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: 90 * .pi / 180)
}, completion: { (finished) in
self.isVertical = true
})
}else{
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.rightBarButtonItem?.customView?.transform = CGAffineTransform.identity
}, completion: { (finished) in
self.isVertical = false
})
}
}
What am I doing wrong?
Updated code:
DispatchQueue.main.async {
if(!self.isVertical) {
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.rightBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: 90 * .pi / 180)
}, completion: { (finished) in
self.isVertical = true
})
}else {
UIView.animate(withDuration: 0.2, animations: {
self.navigationItem.rightBarButtonItem?.customView?.transform = CGAffineTransform(rotationAngle: 90 * .pi / 180)
}, completion: { (finished) in
self.isVertical = false
})
} }
Show button property:
It could be how you've set the custom view in the bar item. Maybe this self.navigationItem.rightBarButtonItem?.customView?. is returning nil. Anyway, here is a working version:
Creating the custom UIBarButtonItem:
let button1: UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 60, height: 30))
let button2: UIButton = UIButton(frame: CGRect(x: 70, y: 0, width: 60, height: 30))
override func viewDidLoad(_ animated: Bool) {
super.viewDidLoad(animated)
button1.backgroundColor = .gray
button1.setTitle("CustomButton", for: .normal)
button1.addTarget(self, action: #selector(rotateAction(_:)), for: .touchUpInside)
let barItem1: UIBarButtonItem = UIBarButtonItem(customView: button1)
button2.backgroundColor = .gray
button2.setTitle("CustomButton", for: .normal)
button2.addTarget(self, action: #selector(rotateAction(_:)), for: .touchUpInside)
let barItem2: UIBarButtonItem = UIBarButtonItem(customView: button1)
navigationItem.setRightBarButtonItems([barItem1, barItem2], animated: true)
}
Animate the tapped button:
#objc func rotateAction(_ sender: UIButton) {
let customView = sender
let transform: CGAffineTransform = isVertical ? .identity : CGAffineTransform(rotationAngle: 90 * .pi / 180)
UIView.animate(withDuration: 0.2, animations: {
customView.transform = transform
}, completion: { (finished) in
self.isVertical = !self.isVertical
})
}
To avoid replacing the bar button item/items set from storyboard, get those button items and create an array of bar button item including those with the ones you created in code:
let existingBarItems: [UIBarButtonItem] = navigationItem.rightBarButtonItems ?? []
let rightBarItems = existingBarItems = [yourCustomButtonItem]
navigationItem.setRightBarButtonItems(rightBarItems, animated: true)
And make sure your custom bar button item's frame doesn't intersect with existing buttons.

How to make table view to display like flip animation in all devices in swift 3?

In all devices the table view needs to be display only 70% of the screen irrespective of all devices and it should need to display only from bottom to top with animation and if I select anywhere on screen it should go to below and hide with animation I tried but unable to implement it can any one help me how to implement this ?
Here is my code
#IBAction func addtoCartButtonAction(_ sender: Any) {
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.addToCartTableView.isHidden = false
self.addToCartTableView.delegate = self
self.addToCartTableView.dataSource = self
self.addToCartTableView.reloadData()
self.addToCartTableView.frame.origin.y = 200
}) { (Bool) in
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
UIView.animate(withDuration: 1, delay: 0.3, options: .autoreverse, animations: {
}) { (Bool) in
self.addToCartTableView.isHidden = true
}
}
#IBAction func AddtoCartTableViewCloseButtonAction(_ sender: Any) {
self.addToCartTableView.isHidden = true
}
For this you can use CGAffineTransform.
For example :
in viewWillAppear() do this :
addToCartTableView.alpha = 0.0
addToCartTableView.transform = CGAffineTransform(translationX: addToCartTableView.bounds.origin.x, y: self.view.bounds.height)
And in you button action addtoCartButtonAction do this :
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.addToCartTableView.delegate = self
self.addToCartTableView.dataSource = self
self.addToCartTableView.reloadData()
self.addToCartTableView.transform = .identity
self.addToCartTableView.alpha = 1.0
}) { (Bool) in
}
And a tap gesture to your view and whenever you want to hide it again do this :
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.addToCartTableView.transform = CGAffineTransform(translationX: addToCartTableView.bounds.origin.x, y: self.view.bounds.height)
self.addToCartTableView.alpha = 0.0
}) { (Bool) in
}
Also you should not assign self.addToCartTableView.delegate = self self.addToCartTableView.dataSource = self in button action. Do that in viewDidLoad() or in storyboard only.

Swift UITabBarController hide with animation

I'm trying to add animation to my tabBarController when hidden. Im able to accomplish this effect with the navigationBarController by using self.navigationController?.isNavigationBarHidden = true. I'm able to hide the tabBar by using self.tabBarController?.tabBar.isHidden = true but i do not get the animation how can I do this thank you in advance.
You could change the tab bar's frame inside an animation, so something like:
func hideTabBar() {
var frame = self.tabBarController?.tabBar.frame
frame?.origin.y = self.view.frame.size.height + (frame?.size.height)!
UIView.animate(withDuration: 0.5, animations: {
self.tabBarController?.tabBar.frame = frame!
})
}
func showTabBar() {
var frame = self.tabBarController?.tabBar.frame
frame?.origin.y = self.view.frame.size.height - (frame?.size.height)!
UIView.animate(withDuration: 0.5, animations: {
self.tabBarController?.tabBar.frame = frame!
})
}
Which sets the tab bar just below the visible screen, so that it slides up/down from the bottom.
I've developed a util extension for UIViewController
Swift 4 compatible:
extension UIViewController {
func setTabBarHidden(_ hidden: Bool, animated: Bool = true, duration: TimeInterval = 0.3) {
if animated {
if let frame = self.tabBarController?.tabBar.frame {
let factor: CGFloat = hidden ? 1 : -1
let y = frame.origin.y + (frame.size.height * factor)
UIView.animate(withDuration: duration, animations: {
self.tabBarController?.tabBar.frame = CGRect(x: frame.origin.x, y: y, width: frame.width, height: frame.height)
})
return
}
}
self.tabBarController?.tabBar.isHidden = hidden
}
}
Improvement of the response of #Luca Davanzo. If the bar is already hidden, it will continue hiding it and moving it lower. Also get rid of the return, so the state of the tabbar.hidden changes when the animation happens.
So I added a check:
extension UIViewController {
func setTabBarHidden(_ hidden: Bool, animated: Bool = true, duration: TimeInterval = 0.5) {
if self.tabBarController?.tabBar.isHidden != hidden{
if animated {
//Show the tabbar before the animation in case it has to appear
if (self.tabBarController?.tabBar.isHidden)!{
self.tabBarController?.tabBar.isHidden = hidden
}
if let frame = self.tabBarController?.tabBar.frame {
let factor: CGFloat = hidden ? 1 : -1
let y = frame.origin.y + (frame.size.height * factor)
UIView.animate(withDuration: duration, animations: {
self.tabBarController?.tabBar.frame = CGRect(x: frame.origin.x, y: y, width: frame.width, height: frame.height)
}) { (bool) in
//hide the tabbar after the animation in case ti has to be hidden
if (!(self.tabBarController?.tabBar.isHidden)!){
self.tabBarController?.tabBar.isHidden = hidden
}
}
}
}
}
}
}
In case if you need to toggle it from hide to visible and vice versa:
func toggleTabbar() {
guard var frame = tabBarController?.tabBar.frame else { return }
let hidden = frame.origin.y == view.frame.size.height
frame.origin.y = hidden ? view.frame.size.height - frame.size.height : view.frame.size.height
UIView.animate(withDuration: 0.3) {
self.tabBarController?.tabBar.frame = frame
}
}
Swift 4 solution:
tabBarController?.tabBar.isHidden = true
UIView.transition(with: tabBarController!.view, duration: 0.35, options: .transitionCrossDissolve, animations: nil)
Here is a simple extension :
func setTabBar(hidden:Bool) {
guard let frame = self.tabBarController?.tabBar.frame else {return }
if hidden {
UIView.animate(withDuration: 0.3, animations: {
self.tabBarController?.tabBar.frame = CGRect(x: frame.origin.x, y: frame.origin.y + frame.height, width: frame.width, height: frame.height)
})
}else {
UIView.animate(withDuration: 0.3, animations: {
self.tabBarController?.tabBar.frame = UITabBarController().tabBar.frame
})
}
}
So I've been playing around for 3 days with this now, finding out that the one that worked for me in my code was Adriana's post from 14th Sept 2018. But I was not sure how to use the coding once copied into my Project. So, after much experimenting I found that the way I could use this func was to put the following into into the respective swipe actions.
setTabBarHidden(false)
setTabBarHidden(true)
My next step is to try to get the swipe actions working while using UIScrollView in the same UIView at the same time.
You have to add UIView transitionWithView class func
Swift 2
func hideTabBarWithAnimation() -> () {
UIView.transitionWithView(tableView, duration: 1.0, options: .TransitionCrossDissolve, animations: { () -> Void in
self.tabBarController?.tabBar.hidden = true
}, completion: nil)
}
Swift 3, 4, 5
func hideTabBarWithAnimation() -> () {
UIView.transition(with: tableView, duration: 1.0, options: .transitionCrossDissolve, animations: { () -> Void in
self.tabBarController?.tabBar.isHidden = true
}, completion: nil)
}

Stop block of animations with UITapGestureRecognizer

I have got this problem with breaking animations block in my app. I am animating an image inside UIScrollView (zooming-in, moving from left to right, zooming-out). Animation is being launched on tap with UITapGestureRecognizer.
The problem is that even though I can zoom-out the image when it is being animated, animations block continues and as a result I get the image off the iPhone screen.
I have tried to use removeAllAnimations() method on both scrollView.layer and view.layer but it seems not to work.
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
setupGestureRecognizer()
}
func setupGestureRecognizer() {
let singleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleSingleTap(recognizer:)))
singleTap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(singleTap)
}
func handleSingleTap(recognizer: UITapGestureRecognizer) {
animateRecipeImage()
}
// MARK: - Recipe Image Animation
func animateRecipeImage() {
let offset = CGPoint(x: 0, y: 0)
var middleOffset = CGPoint()
let endOffset = CGPoint(x: (imageView.bounds.width / 2), y: 0)
// To avoid animation in landscape mode we check for current device orientation
if (UIDeviceOrientationIsPortrait(UIDevice.current.orientation)) {
if (scrollView.zoomScale > scrollView.minimumZoomScale) {
scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
// Remove all animations and update image contraints to fit the screen
self.scrollView.layer.removeAllAnimations()
self.view.layer.removeAllAnimations()
updateConstraintsForSize(size: view.bounds.size)
} else {
scrollView.setZoomScale(scrollView.maximumZoomScale, animated: true)
let screenWidth = screenSize.width
// Handle big screen sizes
if (screenWidth < self.imageView.bounds.width) {
middleOffset = CGPoint(x: ((self.imageView.bounds.width - screenWidth) * self.scrollView.zoomScale), y: 0)
} else {
middleOffset = CGPoint(x: (self.imageView.frame.width - screenWidth), y: 0)
}
// Start animating the image
UIView.animate(withDuration: 2, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(offset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 4, delay: 0.5, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(middleOffset, animated: false)
}, completion: { _ in
UIView.animate(withDuration: 1.0, delay: 0.2, usingSpringWithDamping: 0.4, initialSpringVelocity: 1.1, options: [.allowUserInteraction], animations: {
self.scrollView.setContentOffset(endOffset, animated: false)
}, completion: nil)
})
})
}
}
}
Any ideas how to break that animations block? The result I would like to achieve is break animation and zoom-out image.

Resources