I have a UITabBar containing 5 tabs. I disabled one of the tabs like this:
tabBar.items?[3].isEnabled = false
To enable it again, I am using the following code:
tabBar.items?[3].isEnabled = true
The problem is that it doesn't actually get enabled again. I also tried to place the above code inside viewWillAppear and viewDidAppear, but the tab stays disabled.
Here's the full code:
import UIKit
class MainTabViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.items?[0].title = NSLocalizedString("tab1", comment: "-")
tabBar.items?[1].title = NSLocalizedString("tab2", comment: "-")
tabBar.items?[2].title = NSLocalizedString("tab3", comment: "-")
tabBar.items?[3].title = NSLocalizedString("tab4", comment: "-")
tabBar.items?[4].title = NSLocalizedString("tab5", comment: "-")
self.tabBar.items?[3].isEnabled = true
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
It should be the tabBar property of the UITabBarController.
self.tabBarController?.tabBar.items?[3].isEnabled = false
If you call it from inside the custom UITabBarController subclass:
self.tabBar.items?[3].isEnabled = false
Make sure viewWillAppear and viewDidAppear of the custom UITabBarController subclass are called only once, unlike the methods in every tab, since they are called every time the tab is selected.
The below code seems to work fine:
self.tabBar.items?[3].isEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.tabBar.items?[3].isEnabled = true
}
Related
I am running instruction framework at this link.
I am checking the application launched for the first time by this code:
func isFirstTimeOpening() -> Bool {
let defaults = UserDefaults.standard
if(defaults.integer(forKey: "hasRun") == 0) {
defaults.set(1, forKey: "hasRun")
return true
}
return false
}
I am calling it in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isFirstTimeOpening(){
self.startInstructions() //1
self.coachMarksController.dataSource = self //2
}
}
Here is my viewDidAppear function
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//if isFirstTimeOpening() {
self.coachMarksController.start(in: .window(over: self))
coachMarksController.delegate = self
// }
}
Second thing that i want to check for commented 1 & 2 is First viewController is Loading collectionView is viewed for the first time. This is because function func isFirstTimeOpening() -> Bool checks only application launched for the first time not viewController is visited multiple time during the first launch. I want to restrict these commands self.startInstructions() self.coachMarksController.dataSource = self to execute for first time viewed only(viewController having collectionView) ? How can do that?
hey guys I've used large UINavigationBar in a child ViewController and i want to resize my navBar to default size when popping back to rootViewController smoothly.
vc's gif:https://giphy.com/gifs/1P0HwqlIqqMnzibxbH
EDIT
I don't want to remove largeNavBar from parent vc, i only want to disappear it gradually and with animation like app store:https://giphy.com/gifs/YXsTA6I5r0lGik1gC8
here is the child vc code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.initUI()
super.enableLargeNavigationTitle(title: (self.favorty?.sellerProduct?.product?.name)!)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
super.removeTitleImage()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
}
here is the enableLargeNavigationBar function:
func enableLargeNavigationTitle(title: String) {
self.navigationController?.view.backgroundColor = VVUtility.splashBackGroundColor()
self.navigationItem.title = "\(title)".localized()
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white, NSAttributedStringKey.font : VVUtility.normalFontWithPlusSize(increaseSize: -2.0)]
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.backgroundColor = VVUtility.splashBackGroundColor()
self.navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white, NSAttributedStringKey.font : VVUtility.normalFontWithPlusSize(increaseSize: 0.0)]
} else {
// Fallback on earlier versions
}
}
disableLargeNavigation function:
func disableLargeNavigationTitle() {
if #available(iOS 11.0, *) {
self.navigationController?.navigationItem.largeTitleDisplayMode = .never
self.navigationController?.navigationBar.prefersLargeTitles = false
} else {
// Fallback on earlier versions
}
}
here is parent vc code:
override func viewDidLoad() {
super.viewDidLoad()
self.initUI()
self.getData()
super.disableLargeNavigationTitle()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.addSubview(searchBarBoxView)
self.timerDelegate?.startTimer()
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.searchBarBoxView.removeFromSuperview()
self.timerDelegate?.stopTimer()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tabBarController?.delegate = self
super.disableLargeNavigationTitle()
}
This worked for me. Try putting this code in awakeFromNib() for each view controller, with the settings changed as you need.
override func awakeFromNib() {
// Large titles
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = false // This could be true for other view controller
navigationItem.largeTitleDisplayMode = .never // This could be .always for other view controller
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black] // Or whatever you want
} else {
// Handle iOS 10 and below (no large titles)
}
}
Alternatively, I think you can do this just in Storyboard, but that didn’t work for me.
I am trying to execute a function after the view has appeared.
I have tried series of steps I tried wait()
override func viewDidLoad()
{
super.viewDidLoad()
let w = UnsafeMutablePointer<Int32>.allocate(capacity: 20)
self.title = "Quiz Section"
wait(w)
Function()
}
I tried sleep()
override func viewDidLoad()
{
super.viewDidLoad()
sleep(10)
Function()
}
I even tried viewWillAppear but none of them seemed to work. I want that once the view is in front then the function executes. Any help?
You can try viewDidAppear
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
function() // don't start function name with capital
}
OR dispatch the function in after queue if you need more duration
override func viewDidLoad()
{
super.viewDidLoad()
let w = UnsafeMutablePointer<Int32>.allocate(capacity: 20)
self.title = "Quiz Section"
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0 ) {
function()
}
}
Try this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
function()
}
I need to add Tap Gesture on Navigation Bar or View.
I got the below solution which works perfectly fine.
But removeGestureRecognizer is not removing the gesture and it's breaking the functionality of other back buttons in other view controllers.
How to fix the issue?
var taskTodoOnBar : UITapGestureRecognizer!
override func viewWillAppear(animated: Bool)
{
navigationController?.view.addGestureRecognizer(taskTodoOnBar)
}
override func viewWillDisappear(animated: Bool)
{
navigationController?.view.removeGestureRecognizer(taskTodoOnBar)
}
Or
override func viewWillAppear(animated: Bool)
{
navigationController?.navigationBar.addGestureRecognizer(taskTodoOnBar)
}
override func viewWillDisappear(animated: Bool)
{
navigationController?.navigationBar.removeGestureRecognizer(taskTodoOnBar)
}
When I try to get gestureRecognizers count, It says nil. Then where is the gesture being added ?
override func viewWillDisappear(animated: Bool)
{
print(navigationController!.view.gestureRecognizers!.count)
print(navigationController!.navigationBar.gestureRecognizers!.count)
}
Try using this
Declared gesture as
let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer()
Gesture Handler
#objc func tapHandler(handler: UITapGestureRecognizer){
print("gesture Added")
}
Added in Navigation bar as
override func viewDidLoad()
{
super.viewDidLoad()
tapGesture.numberOfTapsRequired = 1
tapGesture.addTarget(self, action: #selector(VC2.tapHandler(handler:)))
self.navigationController?.view.addGestureRecognizer(tapGesture)
}
Removed as
override func viewWillDisappear(_ animated: Bool) {
for gesture in (navigationController?.view.gestureRecognizers)! {
if gesture == tapGesture {
navigationController?.view.removeGestureRecognizer(tapGesture)
print("removed")
}
}
}
Updated Answer for - gesture count prints nil
console Output :
After help from iOS Geek, I figured out that, gestureRecognizers!.count was 2 in ViewdDidLoad but was nil inside viewWillDisappear.
Then I dug more and discovered that I had written the custom code for my back button.
So in such case, we Should removeGestureRecognizer before popToViewController
So this is for all whom I wish not to make mistake like me while using the custom back button.
func backBarBtnFnc(sender: UIBarButtonItem)
{
navigationController?.navigationBar.removeGestureRecognizer(taskTodoOnBar)
// CodTdo ...
self.navigationController!.popToViewController(VC2, animated: true)
}
I have an overlay view to segregate content, I'm checking for authentication in viewWillAppear() and I have a Notification subscribed to my Auth method. If I authenticate before any of my other views appear the overlay does not show up, however it does on the first view and will not go away even after calling removeFromSuperView().
import UIKit
import FirebaseAuth
class ProtectedViewController: UIViewController, ForceSignInBannerDelegate,
SignUpViewControllerDelegate, LoginViewControllerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
NotificationCenter.default.addObserver(self, selector: #selector(checkAuthentication), name: .myNotification, object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.checkAuthentication()
}
func checkAuthentication() {
let bannerViewController = ForceSignInBanner.instanceFromNib() as! ForceSignInBanner
bannerViewController.delegate = self
if (!AuthenticationService.sharedInstance.isAuthenticated()) {
self.setView(view: bannerViewController, hidden: false)
print("Need to login")
} else if(AuthenticationService.sharedInstance.isAuthenticated()) {
self.setView(view: bannerViewController, hidden: true)
}
}
func setView(view: UIView, hidden: Bool) {
UIView.transition(with: view, duration: 0.5, options: .transitionCrossDissolve, animations: { _ in
view.isHidden = hidden
if hidden {
view.removeFromSuperview()
} else {
self.view.addSubview(view)
}
}, completion: nil)
}
It's because you're trying to remove a new ForceSignInBanner each time. Ideally you should create it once and keep a reference to the ForceSignInBanner created (as an optional property of ProtectedViewController).
Then remove the ForceSignInBanner that you've stored in the property.
class ProtectedViewController: UIViewController, ForceSignInBannerDelegate {
// This lazily loads the view when the property is first used and sets the delegate.
// Ideally you wouldn't force-case the `as` but I've left it for simplicity here.
private lazy var forceSignInBannerView: ForceSignInBanner = {
let forceSignInBannerView = ForceSignInBanner.instanceFromNib() as! ForceSignInBanner
forceSignInBannerView.delegate = self
return forceSignInBannerView
}()
// ... your other code ... //
fun toggleForceSignInBannerViewVisibility(isVisible: Bool) {
if isVisible {
view.addSubview(forceSignInBannerView)
} else {
forceSignInBannerView.removeFromSuperview()
}
}
}