I have a tab bar controller that shows 5 tabs. It shows perfectly in iOS 12 or earlier as shown here
.
But if I run the same in iOS 13, the design is totally messed up
.
It totally loses the scroll view insets, and it is also not adjusting the bottom insets of my menus table view. some of the menus are hidden in the bottom. they are not scrolling up. and this is the code that I'm using in UITabBarController
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
guard let user = user else {
appDelegate.instituteSelectionView()
return
}
var dashBoard: UINavigationController
var menus: UINavigationController? = nil
// Creating all the view controllers for tabs.
if user.role.caseInsensitiveCompare("student") == .orderedSame {
let dashBoardVC = mainStoryBoard.instantiateViewController(withIdentifier: "DashboardVC") as! DashboardVC
dashBoardVC.menus = ContentProvider.getMenus()
dashBoardVC.school = user.school
dashBoard = UINavigationController(rootViewController: dashBoardVC)
dashBoard.navigationBar.prefersLargeTitles = true
} else {
let dashBoardVC = mainStoryBoard.instantiateViewController(withIdentifier: "DashboardWidgetsTableViewController") as! DashboardWidgetsTableViewController
dashBoardVC.user = user
dashBoardVC.title = "Dashboard"
dashBoard = UINavigationController(rootViewController: dashBoardVC)
let menusVC = mainStoryBoard.instantiateViewController(withIdentifier: "MenusTableViewController") as! MenusTableViewController
menusVC.setMenus(ContentProvider.getMenusForStaff())
menus = UINavigationController(rootViewController: menusVC)
}
dashBoard.tabBarItem = UITabBarItem(title: "Dashboard", image: #imageLiteral(resourceName: "dashboard"), tag: 0)
if menus != nil {
menus?.navigationBar.prefersLargeTitles = true
menus?.tabBarItem = UITabBarItem(title: "Menus", image: #imageLiteral(resourceName: "menu-1"), tag: 1)
}
let notificationVC = mainStoryBoard.instantiateViewController(withIdentifier: "NotificationsTabTableViewController") as! NotificationsTabTableViewController
notificationVC.user = user
let notification = UINavigationController(rootViewController: notificationVC)
notification.tabBarItem = UITabBarItem(title: "Notifications", image: #imageLiteral(resourceName: "bell"), tag: 3)
notification.tabBarItem.badgeValue = user.badge > 0 ? String(user.badge) : nil
notification.navigationBar.prefersLargeTitles = true
let settingsVC = mainStoryBoard.instantiateViewController(withIdentifier: "SettingsTabTableViewController") as! SettingsTabTableViewController
settingsVC.user = user
let settings = UINavigationController(rootViewController: settingsVC)
settings.tabBarItem = UITabBarItem(title: "Settings", image: #imageLiteral(resourceName: "settings"), tag: 4)
settings.navigationBar.prefersLargeTitles = true
let accountsVC = mainStoryBoard.instantiateViewController(withIdentifier: "AccountsTableViewController") as! AccountsTableViewController
let accounts = UINavigationController(rootViewController: accountsVC)
accounts.tabBarItem = UITabBarItem(title: "Accounts", image: #imageLiteral(resourceName: "user_group_man_man"), tag: 5)
accounts.navigationBar.prefersLargeTitles = true
mViewControllers = [dashBoard, notification, settings, accounts]
if menus != nil {
mViewControllers.insert(menus!, at: 1)
}
// Adding logo and profile button to navigation bar of each view controller.
for vc in mViewControllers {
if let nVC = vc as? UINavigationController, let vc = nVC.topViewController {
let logo = UIImageView(image: #imageLiteral(resourceName: "educare logo"))
logo.contentMode = .scaleAspectFill
logo.clipsToBounds = true
logo.widthAnchor.constraint(equalToConstant: 24).isActive = true
logo.heightAnchor.constraint(equalToConstant: 24).isActive = true
let profileButton = UIButton(type: .custom)
profileButton.widthAnchor.constraint(equalToConstant: 24).isActive = true
profileButton.heightAnchor.constraint(equalToConstant: 24).isActive = true
profileButton.imageView?.layer.cornerRadius = 12
if #available(iOS 13.0, *) {
let image = UIImage(systemName: "person.crop.circle")
profileButton.sd_setImage(with: URL(string: user.image), for: .normal, placeholderImage: image)
} else {
profileButton.sd_setImage(with: URL(string: user.image), for: .normal, placeholderImage: #imageLiteral(resourceName: "user_circle"))
}
profileButton.addTarget(self, action: #selector(openProfile), for: .touchUpInside)
let logoButtonItem = UIBarButtonItem(customView: logo)
let profileButtonItem = UIBarButtonItem(customView: profileButton)
vc.navigationItem.leftBarButtonItem = logoButtonItem
vc.navigationItem.rightBarButtonItems = [profileButtonItem]
}
}
setViewControllers(mViewControllers, animated: true)
}
Update: Actually the first view controller in the tab bar controller renders correctly but the rest of the view controllers have that ugly navigation bar.
I Solved the problem. turns out it was the transition animation (from the initial view controller to the main tab bar controller) that was causing the problem. So i changed the code from this
if animated {
UIView.transition(from: (window?.rootViewController?.view)!, to: viewController.view, duration: duration, options: animationOptions) { (completed) in
if completed {
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
}
}
} else {
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
}
to this
guard let window = UIApplication.shared.keyWindow else { return }
window.rootViewController = viewController
window.makeKeyAndVisible()
if animated {
UIView.transition(with: window, duration: duration, options: animationOptions, animations: {})
}
Related
I'm trying to add some padding to the top of my tab bar but I can't find a clear answer on how to do so. I'm coming from a SwiftUI background and still trying to get the hang of UIKit.
Below is my code and a screenshot of my current tab bar.
class MainTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .secondaryLabel
let home = UINavigationController(rootViewController: CoinListViewController())
let nilView1 = UINavigationController(rootViewController: CoinListViewController())
let nilView2 = UINavigationController(rootViewController: CoinListViewController())
let nilView3 = UINavigationController(rootViewController: CoinListViewController())
home.tabBarItem.image = UIImage(systemName: "house.fill")
nilView1.tabBarItem.image = UIImage(systemName: "magnifyingglass")
nilView2.tabBarItem.image = UIImage(systemName: "bitcoinsign.circle.fill")
nilView3.tabBarItem.image = UIImage(systemName: "person.fill")
home.title = "Home"
nilView1.title = "Search"
nilView2.title = "Coins"
nilView3.title = "Account"
tabBar.tintColor = .label
setViewControllers([home, nilView1, nilView2, nilView3], animated: true)
}
}
CNContactViewController navigation bar colour not appearing when i click Create New Contact option. See my screens for 1st time it's ok, but when i click Create New Contact i'm not getting navigation bar colour and not visible back button.
1st screen
2nd screen
In older versions
My code is
if #available(iOS 9.0, *) {
let store = CNContactStore()
let contact = CNMutableContact()
let homePhone = CNLabeledValue(label: CNLabelHome, value: CNPhoneNumber(stringValue : self.mobile ?? ""))
contact.phoneNumbers = [homePhone]
let controller = CNContactViewController(forUnknownContact : contact)
controller.contactStore = store
controller.delegate = self
if #available(iOS 10.0, *) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: {
//Set status bar background colour
let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
statusBar?.backgroundColor = UIColor.red
//Set navigation bar subView background colour
for view in controller.navigationController?.navigationBar.subviews ?? [] {
view.tintColor = UIColor.white
view.backgroundColor = UIColor.red
}
})
}
navigationController?.pushViewController(controller, animated: true)
}
And one more is by default phone number: (913) 351-5518
I would suggest to display a CNConctactViewController within a UINavigationController in Popover Modal Presentation style, and add a few button to return to the main application. This implementation seams not trivial as reported by rumours over the net.
Let me share a few pieces of my code (swift 5).
The major class:
class MyViewController: UITableViewController, UIPopoverPresentationControllerDelegate {
var contactViewController = CNContactViewController()
...
#objc func dismissContactViewController() {
contactViewController.dismiss(animated: true, completion: nil)
}
}
The extension:
extension MyViewController: CNContactViewControllerDelegate {
func openCNContactViewController(willAppearWith: CNContact, type: ContactType) {
switch type {
case .forContact:
contactViewController = CNContactViewController(for: willAppearWith)
contactViewController.allowsEditing = false
break
case .forNewContact:
contactViewController = CNContactViewController(forNewContact: willAppearWith)
contactViewController.allowsEditing = true
break
case .forUnknowContact:
contactViewController = CNContactViewController(forUnknownContact: willAppearWith)
contactViewController.allowsEditing = true
break
}
contactViewController.allowsActions = true
contactViewController.contactStore = globalContactStore
contactViewController.hidesBottomBarWhenPushed = true
contactViewController.delegate = self
// define the button (or select a default one)
let button = UIButton(type: .custom)
button.setTitleColor(self.view.tintColor, for: .normal)
button.setTitle("My app name", for: .normal)
button.addTarget(self, action: #selector(dismissContactViewController), for: .touchUpInside)
let closeButton = UIBarButtonItem(customView: button)
closeButton.style = .plain
// add flexible space
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
// add to toolbar
contactViewController.setToolbarItems([flexibleSpace, closeButton, flexibleSpace], animated: false)
let navigationVC = UINavigationController(rootViewController: contactViewController)
// show toolbar
navigationVC.setToolbarHidden(false, animated: false)
// set navigation presentation style
navigationVC.modalPresentationStyle = .popover
// present view controller
self.present(navigationVC, animated: true, completion: nil)
}
The result
I want to create Tab bar with dynamic tab. I have created tab bar with dynamic tabs but when I navigate to details screen from my tab bar screen there is black space in that details view controller. please suggest me the right way to implement it. Here is my code.
func creatTabBarProgrammatically(bottomMenuArray:[Any]) {
DispatchQueue.main.async {
self.navigationController?.setNavigationBarHidden(true, animated: false)
let tabBarController = UITabBarController()
tabBarController.tabBar.backgroundImage = self.getImageWithColor(UIColor.white, size: CGSize(width: self.view.frame.size.width, height: 49))
tabBarController.hidesBottomBarWhenPushed = true
let firstViewController = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController") as! HomeViewController
let ordersViewController = self.storyboard?.instantiateViewController(withIdentifier: "OrdersViewController") as! OrdersViewController
let myProfileViewController = self.storyboard?.instantiateViewController(withIdentifier: "MyProfileViewController") as! MyProfileViewController
var controllers = [Any]()
controllers.append(firstViewController)
for i in 0 ..< bottomMenuArray.count{
if let dict = bottomMenuArray[i] as? [String:Any]{
if let name = dict["name"] as? String{
if name == "orders"{
controllers.append(ordersViewController)
}
else if name == "profile"{
controllers.append(myProfileViewController)
}else{
}
}
}
}
tabBarController.viewControllers = controllers as? [UIViewController]
// Setting Title selected image and unselected image to tab
for i in 0 ..< bottomMenuArray.count{
if let dict = bottomMenuArray[i] as? [String:Any]{
if let name = dict["name"] as? String{
if name == "orders"{
let tabBarItem :UITabBarItem = tabBarController.tabBar.items![i + 1]
tabBarItem.title = "Orders"
tabBarItem.selectedImage = UIImage(named: "tabordersselected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
tabBarItem.image = UIImage(named: "tabordersunselected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
}
else if name == "profile"{
let tabBarItem :UITabBarItem = tabBarController.tabBar.items![i + 1]
tabBarItem.title = "Profile"
tabBarItem.selectedImage = UIImage(named: "tabprofileselected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
tabBarItem.image = UIImage(named: "tabprofileunselected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
}else{
}
}
}
}
let tabBarItem1:UITabBarItem = tabBarController.tabBar.items![0]
tabBarItem1.title = "Home"
tabBarItem1.selectedImage = UIImage(named: "tabhomeselected")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
tabBarItem1.image = UIImage(named: "tabhomeunseleted")?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
tabBarController.viewControllers = controllers.map { UINavigationController(rootViewController: $0 as! UIViewController)}
self.navigationController!.pushViewController(tabBarController, animated: false)
}
}
Try to force layout on controller you got bottom black bar
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.view.setNeedsLayout()
}
I am creating a sample app which contains TabBarViewController and also i implement slide menu using SWRevealViewController and the problem is that the slide menu it not showing out.
Here i set rootViewController in appdelegate
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = TabBarController()
Here is my TabBarController which i implement 2 tabBar items which first tabBar item should have slide menu
let homeController = HomeController()
let homeNavigation = UINavigationController(rootViewController: homeController)
homeNavigation.tabBarItem.title = "Home"
let menuController = MenuViewController()
let swReveal = SWRevealViewController(rearViewController: homeNavigation, frontViewController: menuController)
swReveal?.toggleAnimationType = SWRevealToggleAnimationType.easeOut
swReveal?.toggleAnimationDuration = 0.30
let favController = FavoriteController()
let favNavigation = UINavigationController(rootViewController: favController)
favNavigation.tabBarItem.title = "Favorite"
viewControllers = [homeNavigation, favNavigation]
For MenuController i setup some navigation button for showing menu
let menuButton = UIBarButtonItem(title: "Menu", style: .plain, target: self.revealViewController(), action: #selector(slideMenu))
self.navigationItem.leftBarButtonItem = menuButton
#objc func slideMenu() {
if revealViewController() != nil {
revealViewController().revealToggle(animated: true)
revealViewController().rearViewRevealWidth = (view.bounds.width * 80 ) / 100
}
else {
print("no reveal view")
}
}
You need to configure SWRevealViewController in your didFinishLaunchingWithOptions method as shown below:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let frontNavigationController:UINavigationController
let rearNavigationController:UINavigationController
let revealController = SWRevealViewController()
var mainRevealController = SWRevealViewController()
frontNavigationController = UINavigationController(rootViewController: TabBarController())
rearNavigationController = UINavigationController(rootViewController: MenuViewController())
frontNavigationController.navigationBar.isHidden = true
rearNavigationController.navigationBar.isHidden = true
revealController.frontViewController = frontNavigationController
revealController.rearViewController = rearNavigationController
revealController.delegate = self
mainRevealController = revealController
window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = mainRevealController
self.window?.makeKeyAndVisible()
return true
}
And in your HomeController replace
let menuButton = UIBarButtonItem(title: "Menu", style: .plain, target: self.revealViewController(), action: #selector(slideMenu))
with
let menuButton = UIBarButtonItem(title: "Menu", style: .plain, target: self, action: #selector(slideMenu))
And remove
let menuController = MenuViewController()
let swReveal = SWRevealViewController(rearViewController: homeNavigation, frontViewController: menuController)
swReveal?.toggleAnimationType = SWRevealToggleAnimationType.easeOut
swReveal?.toggleAnimationDuration = 0.30
From your TabBarController and your final code will be:
func customTabbar (){
let homeController = HomeController()
let homeNavigation = UINavigationController(rootViewController: homeController)
homeNavigation.tabBarItem.title = "Home"
let favController = FavoriteController()
let favNavigation = UINavigationController(rootViewController: favController)
favNavigation.tabBarItem.title = "Favorite"
let servicePhoneCollectionFLowLayoutInstance = UICollectionViewFlowLayout()
let serviceTabbarFlowLayoutInit = ExploreController(collectionViewLayout: servicePhoneCollectionFLowLayoutInstance)
let exploreController = serviceTabbarFlowLayoutInit
let exploreNavigation = UINavigationController(rootViewController: exploreController)
exploreNavigation.tabBarItem.title = "Explore"
let moreController = DetailViewController()
let moreViewController = UINavigationController(rootViewController: moreController)
moreViewController.tabBarItem.title = "Tools"
viewControllers = [homeNavigation, favNavigation, exploreNavigation, moreViewController]
}
And for more info check your updated project HERE.
Here is Working Code of SWRevealViewController with UINavigationController and UITabBarController by Storyboard (Swift 4)
https://stackoverflow.com/a/51725803/10150796
I use tabBarController to create a music program and I have questions like how to do it as shown in gif
Questions:
How to do so that when you click on tabBarItem, "presentViewController" worked
How to make it so that the photo does not change color and make it round, only in the third tabBarItem
Preferably without libraries
it should be
My TabBarController
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// меняет цвет фона tabBar
self.tabBar.barTintColor = .white
// меняет цвет UITabBarItem and Title
UITabBar.appearance().tintColor = UIColor(hex: 0x0077fe, alpha: 1)
//меняет цвет background UITabBar
UITabBar.appearance().barTintColor = UIColor.white
// делает фон серым
for item in self.tabBar.items! {
if let image = item.image {
item.image = image.withRenderingMode(.alwaysOriginal)
}
}
//показывает и переходит в контроллеры
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller3,controller4,controller5], animated: true)
// создает навигационный контроллер для контроллеров
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(item.tag)
if item.tag == 0{
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
Player
override func viewDidLoad() {
super.viewDidLoad()
let im = Extension.resizeImage(image:GlobalModals[thisSong].ImageView! , targetSize: CGSize.init(width:20, height: 20))
self.tabBarController?.tabBar.items![2].image = im
}
}
The TabBarController doesn't have those option, you need to implement it by subclassing.
You can use this library Animated Tab Bar to achieve the same result with animation.
I created a view and a button on it in tabBarController and created a func that sets the parameters and call it after I set all the view controllers also created a notificationCenter that could change the button image from another controller
class TabBarController: UITabBarController,UITabBarControllerDelegate {
open var playerBtn = UIButton()
//func for NotificationCenter
#objc func imageChange() {
if GlobalModals.count != 0 {
playerBtn.setImage(GlobalModals[thisSong].ImageView, for: .normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(imageChange), name: NSNotification.Name(rawValue: "imageChange"), object: nil)
self.delegate = self
//shows and goes to controllers
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller4,controller5], animated: true)
// creates navigation controller
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
self.setupMiddleButton()
}
// TabBarButton – Setup Middle Button and View
func setupMiddleButton() {
playerBtn.frame = CGRect(x: 0, y: 0, width: 45, height: 45)
var playerBtnFrame = playerBtn.frame
playerBtnFrame.origin.y = self.view.bounds.height - playerBtnFrame.height - 2
playerBtnFrame.origin.x = self.view.bounds.width / 2 - playerBtnFrame.size.width / 2
playerBtn.frame = playerBtnFrame
playerBtn.layer.cornerRadius = playerBtnFrame.height/2
playerBtn.layer.masksToBounds = true
playerBtn.contentMode = .scaleAspectFill
let view = UIView()
let width = self.view.frame.width/5
let xWidth = width*2
view.frame = CGRect(x: xWidth , y: playerBtnFrame.origin.y - 2, width: self.view.frame.width/5, height: tabBar.frame.height)
view.backgroundColor = .clear
self.view.addSubview(view)
self.view.addSubview(playerBtn)
playerBtn.setImage(UIImage(named: "home"), for: UIControlState.normal)
playerBtn.addTarget(self, action: #selector(playerButtonAction), for: UIControlEvents.touchUpInside)
self.view.layoutIfNeeded()
}
// Button Touch Action
#objc func playerButtonAction(sender: UIButton) {
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
so I change the image of the button
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageChange"), object: nil)