How add tabs programmatically in UITabBarController with swift? - swift

How to create programmatically tabs from any class extended by UIViewController:
class DashboardTabBarController: UITabBarController {
override func viewDidLoad() {
//here
}
...
}

UPDATE SWIFT 5
One example of how to create an UITabBarController programmatically could be like this:
First we create the UIViewControllers that will be the content for each tab of the tab bar interface. For this example we only create one very simple.
class Item1ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
self.title = "item1"
print("item 1 loaded")
}
}
Now, the UITabBarController:
We create the new instances of the UIViewControllers that we want to display in the tab bar. Then we create an icon for each instance we have created and then we create an array that contains all UIViewControllers that specify the content for each tab of the tab bar interface. The order of the view controllers in the array corresponds to the display order in the tab bar.
class DashboardTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let item1 = Item1ViewController()
let icon1 = UITabBarItem(title: "Title", image: UIImage(named: "someImage.png"), selectedImage: UIImage(named: "otherImage.png"))
item1.tabBarItem = icon1
let controllers = [item1] //array of the root view controllers displayed by the tab bar interface
self.viewControllers = controllers
}
//Delegate methods
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
print("Should select viewController: \(viewController.title ?? "") ?")
return true;
}
}

If you are using storyboard for the viewcontrollers then you have to write like this in your tabbarcontroller class.
class CustomTabbarController : UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let firstViewController = FirstViewController()
let navigationController = UINavigationController(rootViewController: firstViewController)
navigationController.title = "First"
navigationController.tabBarItem.image = UIImage.init(named: "map-icon-1")
viewControllers = [navigationController]
if let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
let navgitaionController1 = UINavigationController(rootViewController: secondViewController)
navgitaionController1.title = "Second"
navgitaionController1.tabBarItem.image = UIImage.init(named: "second-icon-1")
var array = self.viewControllers
array?.append(navgitaionController1)
self.viewControllers = array
}
}
}

private lazy var tabbarViewController: UITabBarController = {
let tabbarViewController = UITabBarController()
tabbarViewController.setViewControllers([startVC,
offerVC,
benefitsVC,
shopVC,
recipesVC], animated: true)
return tabbarViewController
}()
window?.rootViewController = tabbarViewController

Related

screen freezes if using modalPresentationStyle = .overCurrentContext swift 5

I am presenting a navigation controller with modalPresentationStyle as overCurrentContext. After dismissing controller screen freezes.
I am presenting a FirstViewController with NAvigationController.
let firstVC = FirstViewController.controller()
let nvc = UINavigationController(rootViewController: firstVC)
nvc.modalPresentationStyle = .overCurrentContext
present(nvc, animated: true)
Then inside FirstViewController, I am passing navigationController to push SecondViewController
override func viewDidLoad() {
super.viewDidLoad()
guard let nav = navigationController else { return }
showSecondViewController(parentController: nav)
}
func showSecondViewController(parentController: UINavigationController) {
let secondVC = SecondViewController.controller()
parentController.pushViewController(secondVC, animated: true)
}
Now first I am popping SecondViewcontroller on click action from SecondViewController
navigationController?.popViewController(animated: animated)
Then with some call back I am dismissing FirstViewController and NavigationControoler (nvc)
self.controller?.dismiss(animated: true)
self.nvc?.dismiss(animated: true)
Now after dismissing as above I am facing screen freeze issue.
I need help to resolve this issue. Please help. Why screen is freezing.
Please let me know if I am missing anything here?
Thanks
Did you see any errors in the console? I'm not clear about "some call back" as you mentioned above. Can you elaborate?
I created a small project to replicate your issue. The approach below works fine in my case
ViewController is the root view controller
FirstViewController is the first controller presented on top of ViewController
SecondViewController is pushed from the first view controller after the event "ViewDidLoad" happens in FirstViewController
I also created 1 onViewControllerDissmied callback in each ViewControllers (especially FirstViewController and SecondViewController)
In ViewController - I created a touch up inside event as below
#objc func onButtonClicked() {
let firstVC = FirstViewController()
nvc = UINavigationController(rootViewController: firstVC)
guard let nvc = nvc else { return }
nvc.modalPresentationStyle = .overCurrentContext
present(nvc, animated: true)
mycontroller = firstVC
firstVC.onViewControllerDimissed = { [weak self] in
self?.mycontroller?.dismiss(animated: true)
self?.nvc?.dismiss(animated: true)
}
}
FirstViewController
class FirstViewController: UIViewController {
var label: UILabel = {
let button = UILabel()
button.translatesAutoresizingMaskIntoConstraints = false
button.text = "First View Controller"
button.textColor = .white
return button
}()
var onViewControllerDimissed: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .purple
guard let nav = navigationController else { return }
showSecondViewController(parentController: nav)
}
func showSecondViewController(parentController: UINavigationController) {
let secondVC = SecondViewController()
parentController.pushViewController(secondVC, animated: true)
secondVC.onViewControllerDimissed = { [weak self] in
self?.onViewControllerDimissed?()
}
}
}
SecondViewController
class SecondViewController: UIViewController {
var label: UILabel = {
let button = UILabel()
button.translatesAutoresizingMaskIntoConstraints = false
button.text = "Second View Controller"
return button
}()
var onViewControllerDimissed: (() -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: view)
let pnt: CGPoint = CGPoint(x: position.x, y: position.y)
if (view.bounds.contains(pnt)) {
onScreenTouch()
}
}
}
func onScreenTouch() {
navigationController?.popViewController(animated: true)
onViewControllerDimissed?()
}
}

How to present viewController from tabBar, like instagram post item in tabBar [Swift]

In my iOS app I've a tabBarController and I want to use one of this buttons to PRESENT a viewController, like for instagram "+" item (when you want to add new posts).
Now I've tried with the following code, but when I dismiss viewController, it go to + page, and I don't want this.
I don't know if I explained myself well, but it works like the button on the instagram tabBar.
class TabBarController: UITabBarController, UITabBarControllerDelegate {
var window: UIWindow!
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = .white
window = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
}
func presentView() {
let storyBoard = UIStoryboard(name: "CreateComment", bundle: nil)
let viewController = storyBoard.instantiateViewController(identifier: "NAV_CONTROLLER")
viewController.modalPresentationStyle = .fullScreen
// I tried to use window because I want to present this viewController over all other viewControllers.
window?.rootViewController = self // tabBarController
window?.makeKeyAndVisible()
present(viewController, animated: true, completion: nil)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard let items = tabBar.items else { return }
switch item {
case items.first: break
case items.second: presentView()
case items.last: break
default: break
}
}
}

iOS 13 - Right Bar Button Item goes offset in Modal Sheet presentation

I have this strange problem with iOS 13 and its new sheet cards style modal presentation.
From ViewController1 I modal present ViewController2 embedded in a NavigationController, and everything works fine.
From ViewController2, I then modal present ViewController3 embedded in a NavigationController, and I get the Right Bar Button offset.
Here is a video of the problem: does anybody have a fix?
Main View Controller
import UIKit
let vc1identifier = "vc1identifier"
let vc2identifier = "vc2identifier"
class ViewController: UIViewController {
#IBAction func tap1(_ sender: UIButton) {
if let navigation = self.storyboard?.instantiateViewController(withIdentifier: vc1identifier) as? UINavigationController,
let nextVC = navigation.contentViewController as? UnoViewController {
//self.navigationController?.pushViewController(nextVC, animated: true)
self.present(navigation, animated: true, completion: nil)
}
}
}
extension UIViewController {
var contentViewController: UIViewController {
if let navcon = self as? UINavigationController {
return navcon.visibleViewController!
} else {
return self
}
}
}
Second View Controller
import UIKit
class UnoViewController: UIViewController {
#IBOutlet weak var barButton: UIBarButtonItem!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
barButton.title = "GotoVC2"
}
#IBAction func pressThis(_ sender: UIBarButtonItem) {
if let navigation = self.storyboard?.instantiateViewController(withIdentifier: vc2identifier) as? UINavigationController,
let nextVC = navigation.contentViewController as? DueViewController {
self.present(navigation, animated: true, completion: nil)
}
}
}
I came across the same issue.
Solution is easy, you just need to tell navigationbar it needs layout like this
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if #available(iOS 13.0, *) {
navigationController?.navigationBar.setNeedsLayout()
}
}

Changing NavigationItem title

I'm trying to change the title in Product but somehow the navigationItem is different. How come the navigationItem in Container is different compared to the one in Product?
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let controller = Container()
let navigation = UINavigationController(rootViewController: controller)
navigationController?.pushViewController(navigation, animated: true)
}
}
class Container: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(navigationItem)
navigationItem.title = "test"
let controller = Product()
controller.didMove(toParentViewController: self)
self.addChildViewController(controller)
view.addSubview(controller.view)
}
}
class Product: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(navigationItem)
navigationItem.title = "" // Doesn't remove the title
}
}
I'm just reading the documentation for navigationItem, and it says this:
This is a unique instance of UINavigationItem created to represent the view controller when it is pushed onto a navigation controller.
However, in your case, the embedded VC is not a direct child of a navigation controller.
So, I tried the following code and it worked. The key part is I overrode navigationItem to return the parent's navigation item if there is a parent view controller.
override var navigationItem: UINavigationItem {
if let parentItem = parent?.navigationItem {
return parentItem
} else {
return super.navigationItem
}
}

Set BarButtonItem directly from navigation controller

I have navigation controller with one rootViewController called "LoginController". When I try to add rightBarButtonItem from init of LoginController so it works, but directly in navigation controller same operation does not works.
This works:
class LoginController: UIViewController {
init(){
super.init(nibName: nil, bundle: nil)
let button = UIBarButtonItem()
button.title = "Test2"
navigationItem.rightBarButtonItem = button
}
......
}
This doesn't work:
class MainNavigationController : UINavigationController{
private var _distributionProvider : DistributionProvider!
init(rootViewController: UIViewController, distributionProvider : DistributionProvider) {
_distributionProvider = distributionProvider
super.init(rootViewController: rootViewController)
navigationBar.barTintColor = UIColor(red: 90/255, green: 177/255, blue: 225/255, alpha: 1)
let button = UIBarButtonItem()
button.title = "Test"
navigationItem.rightBarButtonItem = button
}
....
}
Where is problem? Thanks
You've to do this:
class MainNavigationController: UITabBarController, UITabBarControllerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
override func viewWillAppear(animated: Bool) {
let item1 = Item1ViewController()
let icon1 = UITabBarItem(title: "Title", image: UIImage(named: "someImage.png"), selectedImage: UIImage(named: "otherImage.png"))
item1.tabBarItem = icon1
let controllers = [item1] //array of the root view controllers displayed by the tab bar interface
self.viewControllers = controllers
}
//Delegate methods
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
print("Should select viewController: \(viewController.title) ?")
return true;
}
}