How can I completely override the navbar's back button action? - ios

Basically my issue is that I'm trying to create a drawer since iOS/Swift doesn't seem to have a native object for this. For reasons I can't recall, I decided to go with a navigation controller for this, thinking I could just override the back button(s) and their actions to turn this nav bar into a drawer. While I've changed the image/text of the back button successfully to look like a "burger" (drawer icon), I can't figure out how to successfully stop the button from taking us back, and instead have it just open/close my drawer.
Any advice appreciated, including suggestions to take an entirely different approach to creating this.
The following is the controller for the page which has the back button i'm trying to override. You can see in viewDidLoad() I call prepareDrawerPage(), which is a helper I have in another file. This definition can also be seen below.
class CreateListingController: UIViewController {
let DRAWER_OPEN = CGFloat(0)
var DRAWER_CLOSED: CGFloat = 0 // -1 * width of drawer
#IBOutlet var navItem: UINavigationItem!
#IBOutlet var drawerView: UIView!
#IBOutlet var drawerViewLead: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
DRAWER_CLOSED = UIScreen.main.bounds.width * -1 * drawerViewLead.multiplier
drawerViewLead.constant = DRAWER_CLOSED
prepareDrawerPage(controller: self, title: "PBX - Create a Listing")
}
#IBAction func flipMenu(_ sender: UIBarButtonItem) {
if (drawerViewLead.constant == DRAWER_OPEN){
drawerViewLead.constant = DRAWER_CLOSED
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseOut, animations: {
self.view.layoutIfNeeded()
})
} else {
drawerViewLead.constant = DRAWER_OPEN
UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
})
}
}
}
prepareDrawerPage():
func prepareDrawerPage(controller: UIViewController, title: String) {
let blank = UIImage()
controller.navigationController?.navigationBar.backIndicatorImage = blank
controller.navigationController?.navigationBar.backIndicatorTransitionMaskImage = blank
controller.title = title
}
The above approach works fine on the home page, where we haven't used the navigation bar yet. However, once we click to the create listing page, the button still takes us back home when clicked despite having linked an action between the BarButtonItem (back button) and CreateListingController (flipMenu function).

You can add a custom button on the navigation bar.
override func viewDidLoad() {
...
self.navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(image: YourImage, style: .plain, target: self, action: #selector(back(sender:)))
self.navigationItem.leftBarButtonItem = newBackButton
...
}
#objc func back(sender: UIBarButtonItem) {
// Perform your custom actions
// ...
// Go back to the previous ViewController
// self.navigationController?.popViewController(animated: true)
}

Related

UIBarButtonItem created programmatically not Tapping and linked

I created a view controller for a UIBarButtonItem, a toggle on the main controller. but the UIAction button is inactive
import UIKit
class SideMenuContentViewController: UIViewController {
//Manipulation for Dashboard Side Menu Hamburger
let profileButton = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
profileButton.setImage(UIImage(named: "hamburger") , for: .normal)
profileButton.contentMode = .scaleAspectFit
profileButton.translatesAutoresizingMaskIntoConstraints = false
// function performed when the button is tapped
profileButton.addTarget(self, action: #selector(profileButtonTapped(_:)), for: .touchUpInside)
// Add the profile button as the left bar button of the navigation bar
let barbutton = UIBarButtonItem(customView: profileButton)
self.navigationItem.leftBarButtonItem = barbutton
// Set the width and height for the profile button
NSLayoutConstraint.activate([
profileButton.widthAnchor.constraint(equalToConstant: 35.0),
profileButton.heightAnchor.constraint(equalToConstant: 35.0)
])
// Make the profile button become circular
profileButton.layer.cornerRadius = 35.0 / 2
profileButton.clipsToBounds = true
}
#IBAction func profileButtonTapped(_ sender: Any){
}
}
import UIKit
class MainSideViewController: SideMenuContentViewController {
#IBOutlet weak var sideMenuContainer: UIView!
#IBOutlet weak var sideMenuViewLeadingConstraint: NSLayoutConstraint!
#IBOutlet weak var dashBoardViewLeadingConstraint: NSLayoutConstraint!
var sideMenuVisible = false
override func viewDidLoad() {
super.viewDidLoad()
sideMenuViewLeadingConstraint.constant = 0 - self.sideMenuContainer.frame.size.width
}
#objc func toggleSideMenu(fromViewController: SideMenuContentViewController) {
if(sideMenuVisible){
UIView.animate(withDuration: 0.5, animations: {
// hide the side menu to the left
self.sideMenuViewLeadingConstraint.constant = 0 - self.sideMenuContainer.frame.size.width
// move the content view (tab bar controller) to original position
self.dashBoardViewLeadingConstraint.constant = 0
self.view.layoutIfNeeded()
})
} else {
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.5, animations: {
// move the side menu to the right to show it
self.sideMenuViewLeadingConstraint.constant = 0
// move the content view (tab bar controller) to the right
self.dashBoardViewLeadingConstraint.constant = self.sideMenuContainer.frame.size.width
self.view.layoutIfNeeded()
})
}
sideMenuVisible = !sideMenuVisible
}
}
// THIS IS THE ACTION NOT WORKING
#IBAction override func profileButtonTapped(_ sender: Any){
if let mainVC = self.navigationController?.tabBarController?.parent as? MainSideViewController {
mainVC.toggleSideMenu(fromViewController: self)
}
}
}
I want the action to google in the side menu.

Gesture recogniser not working when other view is on top of the google maps

I am trying to add swipe gesture recognizer for a sidebar. I have created a side bar which is working fine and I can open/close it using a bar button. View is sliding out of left side and is placed on top of the google maps view.
It would be only logical if user would be able to sfipe to the left to close side bar, but unfortunately it is not working.
#IBOutlet weak var sideMenuView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.isMyLocationEnabled = true
mapView.delegate = self
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
setUpSideMenuWidth()
let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction))
swipeLeft.direction = .left
self.sideMenuView.addGestureRecognizer(swipeLeft)
}
func setUpSideMenuWidth() {
self.mapView.bringSubviewToFront(self.sideMenuView)
sideMenuWidth = screenSize.width * 0.7
self.widthSideMenuConstraint.constant = sideMenuWidth
self.leadingConstraint.constant = -sideMenuWidth
self.sideMenuView.layoutIfNeeded()
}
#objc func swipeAction(swipe: UISwipeGestureRecognizer) {
menuViewAction()
}
func menuViewAction() {
if (menuShowing) {
self.leadingConstraint.constant = -sideMenuWidth
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
} else {
self.leadingConstraint.constant = 0
UIView.animate(withDuration: 0.3, animations: {
self.view.layoutIfNeeded()
})
}
menuShowing = !menuShowing
}
I somehow feel that it is related that my side menu is a subview of mapsView. Have everyone faced such problem because when I use simply a bar button it opens and closes perfectly but swipe is not working by some kind of reason. I have also tried to add gesture view storyboard but without luck.

how to make a sliding door animation in Xcode

I am very new to Xcode, so this could be something very basic that i'm just not finding, but i'm trying to create an app that moves 2 images away from each other (at least one width away) when the user taps the screen, and then have the images return to their original positions when the user taps the screen again, however, i have been unable to find out how to move an image in a specific direction, a specific distance.
i'm also new to Stack Overflow, so i'm sorry if i'm doing something wrong
I am breaking the rules by giving up the answer when it appears you have not googled enough. The general rule is to show the code you have attempted and indicate what part you are having issues with.
The code below will achieve what you are attempting to do.
import UIKit
class slidingIamgesViewController: UIViewController {
#IBOutlet weak var topImage: UIImageView!
#IBOutlet weak var bottomImage: UIImageView!
var doubleTap: Bool! = false
//MARK: View LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
let singleFingerTap = UITapGestureRecognizer(target: self, action: #selector(slidingIamgesViewController.handleSingleTap(_:)))
self.view.addGestureRecognizer(singleFingerTap)
}
// MARK: gestutre recognizer
func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
if (doubleTap) {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y += basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y -= basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images Moved back!")
})
doubleTap = false
} else {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y -= basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y += basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images sperated!")
})
doubleTap = true
}
}
}
Make sure in your storyboard to added a Tap Gesture Recognizer.

how to animate an existing button in swift

I'd like to animate an existing UIButton in swift, the goal is to let get the attention of the user to this button when he taps the view controller. The animation is supposed to start from the top in the view controller and go to the initial position of the button.
I did the following scenario but it's not working:
import UIKit
class ViewController: UIViewController {
#IBOutlet var dismissButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
dismissButton = UIButton()
var tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleTapGesture:"))
view.addGestureRecognizer(tapGesture)
}
func handleTapGesture(tapGesture: UITapGestureRecognizer) {
println("tap")
UIView.animateWithDuration(2.0, delay: 0.5, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: nil, animations: {
self.dismissButton.center = CGPoint(x: 200, y: 90 + 200)
}, completion: nil)
}
}
Any suggestion?
Thank you!
Step 1: Take IBOutlet of your Top Constraint of button
Step 2: Update your Constraint's constant by using constraintName.constant property
Step 3: In animation block type self.view.layoutIfNeeded()
Here is an simple example: https://stackoverflow.com/a/26040569/3411787

UIBarButtonItems wont show in navigationBar

I've created a navigationBar in my view which is suppose to hold 2 buttons left and right. The problem is whatever i do it wont show the buttons/views. How am i suppose to do this? here is my code so far.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "Aerovias Brasil NF", size: 30)!, NSForegroundColorAttributeName: UIColor.whiteColor()]
navTitleView = UIView(frame: CGRectMake(0, 0, 100, 44))
titleLabel.text = "SHOPOP"
navTitleView?.addSubview(titleLabel)
self.navigationItem.titleView?.addSubview(navTitleView!)
navigationHeight = UIApplication.sharedApplication().statusBarFrame.height+self.navigationController!.navigationBar.bounds.height
searchBar = UISearchBar(frame: CGRectZero)
searchBarWrapper = UINavigationBar(frame: CGRectMake(0, 100, self.navigationController!.navigationBar.bounds.size.width, self.navigationHeight!))
var buttonSearchBar:UIBarButtonItem = UIBarButtonItem(customView: searchBar!)
var cancelButton:UIBarButtonItem = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
searchBarWrapper?.topItem?.leftBarButtonItem = buttonSearchBar
searchBarWrapper?.topItem?.rightBarButtonItem = cancelButton
self.navigationController?.view.addSubview(searchBarWrapper!)
}
#IBAction func showSearchBar(sender: UIBarButtonItem) {
searchBar?.becomeFirstResponder()
UIView.animateWithDuration(0.25, animations: {
// Optional chaining may return nil
self.searchBarWrapper!.center = CGPointMake(self.navigationController!.view.center.x, self.navigationHeight!/2)
// return
}, completion: {
finished in
println("Basket doors opened!")
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func hideSearchBar(sender: UIBarButtonItem) {
UIView.animateWithDuration(0.25, animations: {
// Optional chaining may return nil
self.searchBarWrapper!.center = CGPointMake(self.navigationController!.view.center.x, -self.navigationHeight!/2)
self.searchBar?.resignFirstResponder()
// return
}, completion: {
finished in
println("Basket doors opened!")
})
Based on the changes to your question you should look into the UISearchController class. A cursory glance at the documentation leads me to believe that after you configure it properly you can push it onto the view controller stack and it will behave like you want.
Old answer:
While I'm not sure about wrapping a UISearchBar inside of a UIBarButtonItem I can say with certainty that this is the wrong way to go about adding a UINavigationBar to a UINavigationController:
self.navigationController?.view.addSubview(searchBarWrapper!)
The right way to go about providing items for the navigation bar is to override UIViewController's navigationItem property and return a customized UINavigationItem instance. This way when you push and pop view controllers off of the UINavigationController's stack the navigation bar will be updated automatically.
This is an example from a UIViewController subclass in one of my projects.
private var customNavigationItem = UINavigationItem(title:nil);
override var navigationItem:UINavigationItem
{
get
{
customNavigationItem.title = self.title;
customNavigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem:.Cancel,
target:self, action:"dismissForm:");
customNavigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem:.Save,
target:self, action:"saveForm:");
return customNavigationItem;
}
}
Doing this will ensure that all of the "free" functionality that UINavigationController provides works as intended. Messing with UINavigationController's view is generally a bad idea.

Resources