I have a Nav View which when it pops out by tapping the burger menu then opens to half the page. It still has the other views (the root view controller and its children) in the background.
When a user taps the greyed out background area where the other views are they can interact with those views and the page navigates leaving the nav view over the top.
The Nav View is used in multiple places so I need the code for disabling interactions with other views to be in the Nav View Controller. Code below.
import UIKit
class MenuNavViewController: ENSideMenuNavigationController, ENSideMenuDelegate {
var tabBar: ManagerTabViewController!
override func viewDidLoad() {
super.viewDidLoad()
let sb = UIStoryboard(name: "iPhoneStoryboard", bundle: nil)
let menu = sb.instantiateViewController(withIdentifier: "MenuTableViewController") as! MenuTableViewController
menu.tabBar = self.tabBar
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menu, menuPosition: .left)
sideMenu?.bouncingEnabled = false
view.bringSubview(toFront: navigationBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - ENSideMenu Delegate
func sideMenuWillOpen() {
}
func sideMenuWillClose() {
}
func sideMenuDidClose() {
}
func sideMenuDidOpen() {
}
}
How can I disable/enable interaction with other views (or at least the view it's launched from) in the NavViewController above?
I found the answer shown in the code below.
Looking further into the ENSideMenuNavigationController class I found that the "init" method sets the relevant view controllers on "viewControllers" and implemented the code below which gave the desired effect.
import UIKit
class MenuNavViewController: ENSideMenuNavigationController, ENSideMenuDelegate {
var tabBar: ManagerTabViewController!
override func viewDidLoad() {
super.viewDidLoad()
let sb = UIStoryboard(name: "iPhoneStoryboard", bundle: nil)
let menu = sb.instantiateViewController(withIdentifier: "MenuTableViewController") as! MenuTableViewController
menu.tabBar = self.tabBar
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menu, menuPosition: .left)
sideMenu?.bouncingEnabled = false
sideMenu?.delegate = self
view.bringSubview(toFront: navigationBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - ENSideMenu Delegate
func sideMenuWillOpen() {
}
func sideMenuWillClose() {
}
func sideMenuDidClose() {
// Enable interaction with other views again
for viewController in self.viewControllers {
viewController.view.isUserInteractionEnabled = true
}
}
func sideMenuDidOpen() {
// Disable interaction with other views
for viewController in self.viewControllers {
viewController.view.isUserInteractionEnabled = false
}
}
}
Any other suggestions for better practice/code would still be appreciated.
Related
I navigate to LoadUserDataViewController after a login view controller, load some user data, then automatically go to HomeViewController. Right now it only works if I use a button.
class LoadUserDataViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//this doesn't transition to the Home View Controller
transitionToHome()
}
//this does transition to the home view controller
#IBAction func gotoNextVCButton(_ sender: Any) {
transitionToHome()
}
func transitionToHome() {
let homeViewController = storyboard?.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
}
Try performing the transitionToHome in viewDidAppear instead.
class LoadUserDataViewController: UIViewController {
//...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
transitionToHome()
}
}
Alternatively you can provide a delay:
class LoadUserDataViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
perform(#selector(transitionToHome), with: self, afterDelay: 0.5)
}
#objc func transitionToHome() {
//...
}
}
I have a tab bar controller with 2 views attached, each embed in navigation controller. I've made a segue kind show from tab bar controller to another view controller with identifier "toNew". In one of the tab bar views I have a button which should trigger this segue with identifier "toNew".
I tried DataDelegate but it doesn't work here.
It's this part of storyboard
This is view controller file for view controller attached in tab bar
import UIKit
protocol DataDelegate {
func sendData(data : String)
}
class NavContToNew: UIViewController , UITabBarDelegate {
var delegate : DataDelegate?
var data : String = "ToNew"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func dismiss(_ sender: Any) {
//self.dismiss(animated: true, completion: nil)
self.delegate?.sendData(data:self.data)
print("Perform segue delegate")
}
}
And this is tab bar controller.swift file
import UIKit
class TabBarCont: UITabBarController , DataDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func sendData(data: String) {
if data == "ToNew" {
print("Segue perform")
self.performSegue(withIdentifier: "toNew", sender: self)
}
}
}
In you NavContToNew , you haven't assigned a delegate.
#IBAction func dismiss(_ sender: Any) {
self.delegate = self.tabBarController as! TabBarCont
//self.dismiss(animated: true, completion: nil)
self.delegate?.sendData(data:self.data)
print("Perform segue delegate")
}
By the way , you don't need to use delegate. You can call your taBarController anywhere in its viewControllers. Here, you can call it in
NavContToNew without using delegate.
self.tabBarController?.performSegue(withIdentifier: "toNew", sender: self.tabBarController)
Is it possible to set the navigation bar color for just a single View Controller in the navigation hierarchy? Let the default navigation bar color be red and just the last view controller in the line should have a blue one. I've used these two lines to color the navigation bar of said view controller:
navigationController?.navigationBar.barTintColor = .blue
navigationController?.navigationBar.tintColor = .white
But when going back (e.g. by pressing the back button) the navigation bar stays blue. Setting the color back to red using above code doesn't do anything.
The navigationBar is shared across all the view controllers that are in the same UINavigationController stack.
If you want to change it's look for a specific view controller, you'll have to set the new style when the view controller is shown and remove it when the view controller is dismissed. This could be done in the viewWillAppear/viewWillDisappear of your view controller for example.
I could get the navigation bar to change colors coming from a ViewControllerB to ViewControllerA perfectly fine with your code. I am not sure what your initial problem was. Here is my code which worked:
ViewController A:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .red
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonAction(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "Second")
//self.present(controller, animated: true, completion: nil)
self.navigationController?.pushViewController(controller, animated: true)
}
}
ViewController B:
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .blue
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
It worked without an issue.
I got a TabBarController with two Views in my project. Now I want to set a FirstViewController with two buttons as Initial View Controller when the app launches. The first button should show the FirstView in the TabBarController and the second button the second one. When one of the two buttons is pressed the FirstViewController should disappear and it should only be possible to navigate between the two Views with the Tabs in TabBarViewController.
I did some minor edit, and tested the code I wrote and it works. Control drag from firstButton over to the TabBarController and select Kind as "Show". Then do the same with secondButton.
In your view with the two buttons, I call it First:
import Foundation
import UIKit
class First: UIViewController {
var firstWasClicked = false
#IBAction func firstButtonAction(sender: UIButton) {
firstWasClicked = true
}
#IBAction func secondButtonAction(sender: UIButton) {
firstWasClicked = false
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let controller = segue.destinationViewController as! TabBarController
controller.firstSelected = firstWasClicked
}
}
then in your TabBarController:
import Foundation
import UIKit
class TabBarController: UITabBarController {
var firstSelected = true
override func viewDidLoad() {
if(firstSelected) {
self.selectedIndex = 0
}
else {
self.selectedIndex = 1
}
}
}
This is probably what you want.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func didTapFirst(button: UIButton) {
showViewControllerAt(index: 0)
}
#IBAction func didTapSecond(button: UIButton) {
showViewControllerAt(index: 1)
}
func showViewControllerAt(index: NSInteger) {
let tabBarController = self.storyboard?.instantiateViewController(withIdentifier: "TabBarController") as! UITabBarController
tabBarController.selectedIndex = index
UIApplication.shared.keyWindow?.rootViewController = tabBarController
}
}
Don't forget to set the Storyboard ID of your UITabBarController.
Here is my demo project.
I have two view controllers. The main one has the status bar hidden while the second one hasn't.
I created a custom driven transition animation to go from controller one to controller two.
When I'm on the child view controller (the orange one), I start the driven transition by panning from top to bottom. You can see that the status bar is coming back when dragging. And the UIButton "Hello" is moving as well.
I cancel the transition. Then I start it again and you can see the status bar is coming back as well but this time, my button isn't moving, it stays at the same location, as if the status bar was still hidden.
Any idea why it would behave like this once the transition has been cancelled at least once?
(I'm not even talking about the weird thing with the animation that is kind of doubled when cancelling (maybe a bug with the simulator as it doesn't do it on my iphone 6 9.1 and my iphone 5 8.4.)
Add: import Foundation
Then add an outlet:
class ViewController: UIViewController {
#IBOutlet weak var topConstraint: NSLayoutConstraint!
...
}
Then change the value to 0 when the view disappears and then to 20 when it will appear:
override func viewWillAppear(animated: Bool) {
topConstraint.constant = 20.0
}
override func viewWillDisappear(animated: Bool) {
topConstraint.constant = 0.0
}
Full code (make sure to remember to connect the constraint to the outlet):
import UIKit
import Foundation
class ViewController: UIViewController {
#IBOutlet weak var topConstraint: NSLayoutConstraint!
let controllerTransition = InteractiveControllerTransition(gestureType: .Pan)
let controllerTransitionDelegate = ViewController2Transition()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
controllerTransition.delegate = controllerTransitionDelegate
controllerTransition.edge = .Bottom
}
override func viewWillAppear(animated: Bool) {
topConstraint.constant = 20.0
}
override func viewWillDisappear(animated: Bool) {
topConstraint.constant = 0.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func unwindToViewController(sender: UIStoryboardSegue) { }
override func prefersStatusBarHidden() -> Bool {
return false
}
#IBAction func helloButtonAction(sender: UIButton) {
// let storyBoard = UIStoryboard(name: "Main", bundle: nil)
// let vc = storyBoard.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
//
// vc.transitioningDelegate = controllerTransition
// controllerTransition.toViewController = vc
//
// self.presentViewController(vc, animated: true, completion: nil)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
// let nvc = storyBoard.instantiateViewControllerWithIdentifier("NavigationViewController2") as! UINavigationController
// let vc = nvc.topViewController as! ViewController2
let vc = storyBoard.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
// nvc.transitioningDelegate = controllerTransition
vc.transitioningDelegate = controllerTransition
controllerTransition.toViewController = vc
// self.presentViewController(nvc, animated: true, completion: nil)
self.presentViewController(vc, animated: true, completion: nil)
}
}