UITabBarController is not fitted to the screen in iOS Swift? - ios

I have been implemented UITabBarController programmatically. Functionality works fine but the UITabBarController is not fitting inside the screen.
here is my code:
class ViewController: UIViewController {
let tabBarCnt = UITabBarController()
override func viewDidLoad() {
super.viewDidLoad()
tabBarCnt.tabBar.tintColor = UIColor.black
createTabBarController()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
addHeightConstraintToTabbar()
}
func addHeightConstraintToTabbar() -> Void {
let heightConstant:CGFloat = self.view.safeAreaInsets.bottom + 49.0
tabBarCnt.tabBar.heightAnchor.constraint(equalToConstant: heightConstant).isActive = true
}
func createTabBarController() {
let firstVc = UIViewController()
firstVc.title = "First"
firstVc.view.backgroundColor = UIColor.red
firstVc.tabBarItem = UITabBarItem.init(title: "Home", image: UIImage(named: "HomeTab"), tag: 0)
let secondVc = UIViewController()
secondVc.title = "Second"
secondVc.view.backgroundColor = UIColor.green
secondVc.tabBarItem = UITabBarItem.init(title: "Location", image: UIImage(named: "Location"), tag: 1)
let controllerArray = [firstVc, secondVc]
tabBarCnt.viewControllers = controllerArray.map{ UINavigationController.init(rootViewController: $0)}
self.view.addSubview(tabBarCnt.view)
}
}
Result screenshot

Add this line:
tabBarCnt.additionalSafeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

Related

UINavigationBar Large Title doesn't appear when scroll view up

I have implemented a feature, when you press on a UITabBar icon and viewController1 scrolls up using its UIScrollView. It works perfectly, but if I scroll view down and stop somewhere, then switch to another viewController2, then get back to viewController1 and press tabBar icon - the viewController1 will scroll up, but Large Title will never be showed, and I should press tabBar icon one more time to show it:
The code I use for scroll up the VC1:
private var biggestTopSafeAreaInset: CGFloat = 0
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
self.biggestTopSafeAreaInset = max(view.safeAreaInsets.top, biggestTopSafeAreaInset)
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 0 {
let navigationVC = viewController as? UINavigationController
let firstVC = navigationVC?.viewControllers.first as? CurrencyViewController
guard let scrollView = firstVC?.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView else { return }
if traitCollection.verticalSizeClass == .compact {
scrollView.setContentOffset(CGPoint(x: 0, y: -view.safeAreaInsets.top, animated: true)
} else {
scrollView.setContentOffset(CGPoint(x: 0, y: -biggestTopSafeAreaInset, animated: true)
}
}
}
I tried to track biggestTopSafeAreaInset in different stages of VC1 life, but it always has the same number - 196.0. But then why it doesn't scroll till the Large Title after viewControllers switch?
in your tableView set contentInsetAdjustmentBehavior to never
tableView.contentInsetAdjustmentBehavior = .never
in controller update the ui of navigation bar again
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async { [weak self] in
self?.navigationController?.navigationBar.sizeToFit()
}
}
here is the navigation controller
class BaseNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 15.0, *) {
let scrollAppearance = UINavigationBarAppearance()
scrollAppearance.shadowColor = .white
scrollAppearance.backgroundColor = .white
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithDefaultBackground()
navigationBarAppearance.backgroundColor = .white
navigationBarAppearance.largeTitleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 26),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBarAppearance.titleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17),
NSAttributedString.Key.foregroundColor: UIColor.black
]
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "back-arrow")
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = scrollAppearance
navigationBar.tintColor = .black
navigationBar.prefersLargeTitles = true
navigationBar.isTranslucent = false
navigationItem.largeTitleDisplayMode = .automatic
} else {
navigationBar.largeTitleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 26),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBar.titleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17),
NSAttributedString.Key.foregroundColor: UIColor.black
]
navigationBar.tintColor = .black
navigationBar.prefersLargeTitles = true
navigationBar.isTranslucent = false
navigationItem.largeTitleDisplayMode = .automatic
navigationBar.barTintColor = .white
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .darkContent
}
}
here is the Tabbar Controller
class TabbarController:UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let c1 = C1()
let c2 = C2()
let c3 = C3()
c1.tabBarItem = UITabBarItem(title: "Home", image: UIImage(named: "home786"), tag: 0)
c1.tabBarItem.tag = 0
let nav1 = BaseNavigationController(rootViewController: c1)
c2.tabBarItem = UITabBarItem(title: "Setting", image: UIImage(named: "home786"), tag: 0)
c2.tabBarItem.tag = 1
let nav2 = BaseNavigationController(rootViewController: c2)
c2.tabBarItem = UITabBarItem(title: "User", image: UIImage(named: "home786"), tag: 0)
c2.tabBarItem.tag = 2
let nav3 = BaseNavigationController(rootViewController: c3)
viewControllers = [nav1,nav2,nav3]
selectedViewController = nav1
tabBarController?.viewControllers?.first?.view.backgroundColor = .red
}
}
Try to add this in viewDidLoad:
view.addSubview(UIView())
this single line block large title navigation Bar... I don't Know why, but this trick fix momentarily the issue...
After some research I found out what can fix my problem. If you call this method with a small delay in tabBarController didSelect then it will be possible to see a Large Title after switching viewControllers. But I still can't figure out exactly why it happened...
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
navigationVC?.navigationBar.sizeToFit()
}

Override var canBecomeFirstResponder not called in a nested collectionViewController

I am struggling to get inputAccessoryView to show up in my UICollectionViewController. Ok here is a simple explanation of what I have.
A UIPageViewController with 3 ViewControllers as pages - so that I can scroll horizontally between them
PageViewController also has segmented view embedded in navigation bar. Have programmed it in a way where when I press a segment, the PageViewController scrolls to the relevant Viewcontroller
One of the ViewControllers in the PageViewController is my ChatViewController
ChatViewController is a UICollectionViewController
Now ignoring this PageViewController, if I simply present the ChatViewController modally, the following code gets called and everything works as expected. I can see the keyboard, type into the input accessory textview and dismiss it.
Code present in ChatViewController
override var inputAccessoryView: UIView? { //IN ChatViewController
get {
if self.typeReply == true{
return viewForReplyInputAccessory
} else {
return viewForInputAccessory
}
}
}
override var canBecomeFirstResponder: Bool {
return true
}
lazy var viewForInputAccessory: KeyboardView = {
let civ = KeyboardView(frame: .init(x: 0, y: 0, width: view.frame.width, height: 50))
civ.sendButton.isUserInteractionEnabled = true
let gcSend = UITapGestureRecognizer(target: self, action: #selector(handleSend))
civ.addGestureRecognizer(gcSend)
return civ
}()
lazy var viewForReplyInputAccessory: KeyboardReplyView = {
let civ = KeyboardReplyView(frame: .init(x: 0, y: 0, width: view.frame.width, height: 114))
civ.sendButton.isUserInteractionEnabled = true
let gcSend = UITapGestureRecognizer(target: self, action: #selector(handleSend))
civ.addGestureRecognizer(gcSend)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleReplyMessageClose))
civ.closeImageView.addGestureRecognizer(gestureRecognizer)
return civ
}()
The Problem
When this ChatViewController comes nested in this UIPageViewController, for some reason, inputAccessoryView is not shown. override var canBecomeFirstResponder: Bool is not called. Again it works if I simply present the ChatViewController modally without nesting it anywhere. Am I doing something wrong?
For more clarity. Here is my Hierarchy:
UINavigationController (has embedded segmented view) -> UIPageViewController -> [VC1, ChatVC, VC3]
Here is the code for my UIPageViewController
class JobsContainerScreenController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var controllers = [UIViewController]()
var segmentedControl: UISegmentedControl!
var currentSegment: Int = 0
let jobsDetailScreenController = JobsDetailScreenController()
let jobsChatScreenController = ChatController(collectionViewLayout: UICollectionViewFlowLayout())
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
let index = controllers.firstIndex(where: {$0 == viewController}) ?? 0
if index != 1 {
self.currentSegment = index
}
if index == 0{
self.segmentedControl.selectedSegmentIndex = index
return nil
}
self.segmentedControl.selectedSegmentIndex = index
return controllers[index - 1]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
let index = controllers.firstIndex(where: {$0 == viewController}) ?? 0
if index != 1 {
self.currentSegment = index
}
if index == controllers.count - 1{
self.segmentedControl.selectedSegmentIndex = index
return nil
}
self.segmentedControl.selectedSegmentIndex = index
return controllers[index + 1]
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
handleSegment()
}
fileprivate func setupViews() {
view.backgroundColor = .white
self.overrideUserInterfaceStyle = .light
dataSource = self
delegate = self
view.isUserInteractionEnabled = true
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.backgroundColor = .white
navBarAppearance.shadowColor = .clear
let backImage = UIImage(systemName: ImageBackArrow)?.withRenderingMode(.alwaysTemplate)
let leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(handleBack))
navigationItem.leftBarButtonItem = leftBarButtonItem
navigationItem.leftBarButtonItem?.tintColor = ColorBlackAlpha
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
self.segmentedControl = UISegmentedControl(items: ["Activity", "Chat", "Contract"])
self.segmentedControl.sizeToFit()
self.segmentedControl.backgroundColor = UIColor.black.withAlphaComponent(0.01)
self.segmentedControl.selectedSegmentTintColor = UIColor.white
self.segmentedControl.selectedSegmentIndex = 0
self.segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font : UIFont(name: FontPromptBold, size: 14)!, NSAttributedString.Key.foregroundColor: ColorBlackLow], for: .normal)
self.segmentedControl.setTitleTextAttributes([NSAttributedString.Key.font : UIFont(name: FontPromptBold, size: 14)!, NSAttributedString.Key.foregroundColor: ColorDarkGreen], for: .selected)
self.segmentedControl.addTarget(self, action: #selector(handleSegment), for: .valueChanged)
self.navigationItem.titleView = segmentedControl
jobsDetailScreenController.job = self.job
jobsChatScreenController.job = self.job
let jobsContractScreenController = JobsContractScreenController()
controllers = [jobsDetailScreenController, jobsChatScreenController, jobsContractScreenController]
setViewControllers([controllers.first!], direction: .forward, animated: false, completion: nil)
}
#objc func handleSegment() {
if self.segmentedControl.selectedSegmentIndex == 0 {
currentSegment = 0
setViewControllers([controllers.first!], direction: .reverse, animated: true, completion: nil)
} else if segmentedControl.selectedSegmentIndex == 1 {
if currentSegment == 0 {
setViewControllers([controllers[1]], direction: .forward, animated: true, completion: nil)
} else {
setViewControllers([controllers[1]], direction: .reverse, animated: true, completion: nil)
}
} else {
currentSegment = 2
setViewControllers([controllers[2]], direction: .forward, animated: true, completion: nil)
}
}
}

Messagekit Navigation bar is not showing up

How can i show the default or custom navigation bar ? tried everything but nothing works , It seems like nothing is on the top , its a messageViewcontroller of messagekit and couldn't find any delegate method for navigation bar , it would be nice if someone educate me about this ..
My Code
override func viewDidLoad() {
messagesCollectionView = MessagesCollectionView(frame: .zero, collectionViewLayout: CustomMessagesFlowLayout())
messagesCollectionView.register(CustomCell.self)
super.viewDidLoad()
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
messagesCollectionView.messageCellDelegate = self
messageInputBar.delegate = self
configureMessageInputBar()
configureInputBarItems()
updateTitleView(title: "Hanzala", subtitle: "Online")
}
import UIKit
extension UIViewController {
func updateTitleView(title: String, subtitle: String?, baseColor: UIColor = .white) {
let titleLabel = UILabel(frame: CGRect(x: 0, y: -2, width: 0, height: 0))
titleLabel.backgroundColor = UIColor.clear
titleLabel.textColor = baseColor
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.text = title
titleLabel.textAlignment = .center
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.sizeToFit()
let subtitleLabel = UILabel(frame: CGRect(x: 0, y: 18, width: 0, height: 0))
subtitleLabel.textColor = baseColor.withAlphaComponent(0.95)
subtitleLabel.font = UIFont.systemFont(ofSize: 12)
subtitleLabel.text = subtitle
subtitleLabel.textAlignment = .center
subtitleLabel.adjustsFontSizeToFitWidth = true
subtitleLabel.sizeToFit()
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), height: 30))
titleView.addSubview(titleLabel)
if subtitle != nil {
titleView.addSubview(subtitleLabel)
} else {
titleLabel.frame = titleView.frame
}
let widthDiff = subtitleLabel.frame.size.width - titleLabel.frame.size.width
if widthDiff < 0 {
let newX = widthDiff / 2
subtitleLabel.frame.origin.x = abs(newX)
} else {
let newX = widthDiff / 2
titleLabel.frame.origin.x = newX
}
navigationItem.titleView = titleView
}
}
I want the navigation bar like this
Pushing ChatViewController in NavigationController
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let model = Api.Params.chatUser[indexPath.row]
openConversation(model)
}
func openConversation(_ model: ChatUser) {
Api.Params.inputRecieverId = model.userId
let id = String(requestManager.instance.userID)
let vc = ChatViewController(recieverId: model.userId, senderId: id, conversationId: "Eman-Conversation-\(id)-\(model.userId)")
vc.title = model.username
vc.navigationItem.largeTitleDisplayMode = .never
navigationController?.pushViewController(vc, animated: true)
}
You've to embed the MessageViewcontroller inside a UINavigationController and use it.
let navigationController = UINavigationController(rootViewController: MessageViewcontroller())
If the controller that you're pushing MessageViewcontroller onto already has a navigationController then push the MessageViewcontroller into the navigational stack instead of presenting.
let messageViewcontroller = MessageViewcontroller()
navigationController?.pushViewController(messageViewcontroller, animated: true)

Black background when presenting modally view controller

I have one problem when I see my view being presented with black BG color. Here is my code:
class Loader: UIViewController, NVActivityIndicatorViewable {
override func viewDidLoad() {
super.viewDidLoad()
self.modalPresentationStyle = .currentContext
self.view.backgroundColor = .clear
self.view.isOpaque = false
let width = UIScreen.main.bounds.width
let height = UIScreen.main.bounds.height
let frame = CGRect(x: width / 2, y: height / 2, width: 100, height: 100)
let activityIndicatorView = NVActivityIndicatorView(frame: frame, type: NVActivityIndicatorType.lineScale, color: GlobalVariables.stacksBlue, padding: 20)
self.view.addSubview(activityIndicatorView)
activityIndicatorView.startAnimating()
activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
And the extension where is call function:
extension UIViewController {
func showLoader() {
let loader = Loader()
self.modalPresentationStyle = .currentContext
self.present(loader, animated: false, completion: nil)
}
func hideLoader() {
self.dismiss(animated: false, completion: nil)
}
}
Can you try
let loader = Loader()
loader.providesPresentationContextTransitionStyle = true;
loader.definesPresentationContext = true;
loader.modalPresentationStyle = .overCurrentContext
self.present(loader, animated: false, completion: nil)
You are saying:
self.view.backgroundColor = .clear
self.view.isOpaque = false
So the view itself is clear, and you are seeing the blackness of the window behind it.

Make custom button on Tab Bar rounded

Here is what I am trying to do:
Note: The screenshot is taken from an earlier version of iOS
What I have been able to achieve:
Code:
override func viewWillAppear(animated: Bool) {
// Creates image of the Button
let imageCameraButton: UIImage! = UIImage(named: "cameraIcon")
// Creates a Button
let cameraButton = UIButton(type: .Custom)
// Sets width and height to the Button
cameraButton.frame = CGRectMake(0.0, 0.0, imageCameraButton.size.width, imageCameraButton.size.height);
// Sets image to the Button
cameraButton.setBackgroundImage(imageCameraButton, forState: .Normal)
// Sets the center of the Button to the center of the TabBar
cameraButton.center = self.tabBar.center
// Sets an action to the Button
cameraButton.addTarget(self, action: "doSomething", forControlEvents: .TouchUpInside)
// Adds the Button to the view
self.view.addSubview(cameraButton)
}
I did try to create a rounded button in the normal way, but this was the result:
Code Snippet for rounded button:
//Creation of Ronded Button
cameraButton.layer.cornerRadius = cameraButton.frame.size.width/2
cameraButton.clipsToBounds = true
Solution
You need to subclass UITabBarController and then add the button above TabBar's view. A button action should trigger UITabBarController tab change by setting selectedIndex.
Code
The code below only is a simple approach, however for a full supporting iPhone (including X-Series)/iPad version you can check the full repository here: EBRoundedTabBarController
class CustomTabBarController: UITabBarController {
// MARK: - View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
let controller1 = UIViewController()
controller1.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 1)
let nav1 = UINavigationController(rootViewController: controller1)
let controller2 = UIViewController()
controller2.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 2)
let nav2 = UINavigationController(rootViewController: controller2)
let controller3 = UIViewController()
let nav3 = UINavigationController(rootViewController: controller3)
nav3.title = ""
let controller4 = UIViewController()
controller4.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 4)
let nav4 = UINavigationController(rootViewController: controller4)
let controller5 = UIViewController()
controller5.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 5)
let nav5 = UINavigationController(rootViewController: controller5)
viewControllers = [nav1, nav2, nav3, nav4, nav5]
setupMiddleButton()
}
// MARK: - Setups
func setupMiddleButton() {
let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
var menuButtonFrame = menuButton.frame
menuButtonFrame.origin.y = view.bounds.height - menuButtonFrame.height
menuButtonFrame.origin.x = view.bounds.width/2 - menuButtonFrame.size.width/2
menuButton.frame = menuButtonFrame
menuButton.backgroundColor = UIColor.red
menuButton.layer.cornerRadius = menuButtonFrame.height/2
view.addSubview(menuButton)
menuButton.setImage(UIImage(named: "example"), for: .normal)
menuButton.addTarget(self, action: #selector(menuButtonAction(sender:)), for: .touchUpInside)
view.layoutIfNeeded()
}
// MARK: - Actions
#objc private func menuButtonAction(sender: UIButton) {
selectedIndex = 2
}
}
Output
Swift 3 Solution
With a slight adjustment to EricB's solution to have this work for Swift 3, the menuButton.addTarget() method needs to have it's selector syntax changed a bit.
Here is the new menuButton.addTarget() function:
menuButton.addTarget(self, action: #selector(MyTabBarController.menuButtonAction), for: UIControlEvents.touchUpInside)
When defining my TabBarController class, I also add a UITabBarControllerDelegate and placed all of the that in the
override func viewDidAppear(_ animated: Bool) { ... }
For extra clarity, the full code is:
Full Code Solution
import UIKit
class MyTabBarController: UITabBarController, UITabBarControllerDelegate {
// View Did Load
override func viewDidLoad() {
super.viewDidLoad()
}
// Tab Bar Specific Code
override func viewDidAppear(_ animated: Bool) {
let controller1 = UIViewController(self.view.backgroundColor = UIColor.white)
controller1.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 1)
let nav1 = UINavigationController(rootViewController: controller1)
let controller2 = UIViewController()
controller2.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 2)
let nav2 = UINavigationController(rootViewController: controller2)
let controller3 = UIViewController()
let nav3 = UINavigationController(rootViewController: controller3)
nav3.title = ""
let controller4 = UIViewController()
controller4.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 4)
let nav4 = UINavigationController(rootViewController: controller4)
let controller5 = UIViewController()
controller5.tabBarItem = UITabBarItem(tabBarSystemItem: UITabBarSystemItem.contacts, tag: 5)
let nav5 = UINavigationController(rootViewController: controller5)
self.viewControllers = [nav1, nav2, nav3, nav4, nav5]
self.setupMiddleButton()
}
// TabBarButton – Setup Middle Button
func setupMiddleButton() {
let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
var menuButtonFrame = menuButton.frame
menuButtonFrame.origin.y = self.view.bounds.height - menuButtonFrame.height
menuButtonFrame.origin.x = self.view.bounds.width / 2 - menuButtonFrame.size.width / 2
menuButton.frame = menuButtonFrame
menuButton.backgroundColor = UIColor.red
menuButton.layer.cornerRadius = menuButtonFrame.height/2
self.view.addSubview(menuButton)
menuButton.setImage(UIImage(named: "example"), for: UIControlState.normal)
menuButton.addTarget(self, action: #selector(MyTabBarController.menuButtonAction), for: UIControlEvents.touchUpInside)
self.view.layoutIfNeeded()
}
// Menu Button Touch Action
func menuButtonAction(sender: UIButton) {
self.selectedIndex = 2
// console print to verify the button works
print("Middle Button was just pressed!")
}
}
This is the customTabbarcontroller class which is the subclass of UITabbarcontroller. It's the same idea as given by #EridB. But in his code #Raymond26's issue wasn't solved. So, posting a complete solution written in Swift 3.0
protocol CustomTabBarControllerDelegate
{
func customTabBarControllerDelegate_CenterButtonTapped(tabBarController:CustomTabBarController, button:UIButton, buttonState:Bool);
}
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate
{
var customTabBarControllerDelegate:CustomTabBarControllerDelegate?;
var centerButton:UIButton!;
private var centerButtonTappedOnce:Bool = false;
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews();
self.bringcenterButtonToFront();
}
override func viewDidLoad()
{
super.viewDidLoad()
self.delegate = self;
self.tabBar.barTintColor = UIColor.red;
let dashboardVC = DashboardViewController()
dashboardVC.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
let nav1 = UINavigationController(rootViewController: dashboardVC)
let myFriendsVC = MyFriendsViewController()
myFriendsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .featured, tag: 2)
let nav2 = UINavigationController(rootViewController: myFriendsVC)
let controller3 = UIViewController()
let nav3 = UINavigationController(rootViewController: controller3)
nav3.title = ""
let locatorsVC = LocatorsViewController()
locatorsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 4)
let nav4 = UINavigationController(rootViewController: locatorsVC)
let getDirectionsVC = GetDirectionsViewController()
getDirectionsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .history, tag: 5)
let nav5 = UINavigationController(rootViewController: getDirectionsVC)
viewControllers = [nav1, nav2, nav3, nav4, nav5]
self.setupMiddleButton()
}
// MARK: - TabbarDelegate Methods
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
{
switch viewController
{
case is DashboardViewController:
self.showCenterButton()
case is MyFriendsViewController:
self.showCenterButton()
case is GetDirectionsViewController:
self.showCenterButton()
case is LocatorsViewController:
self.showCenterButton()
default:
self.showCenterButton()
}
}
// MARK: - Internal Methods
#objc private func centerButtonAction(sender: UIButton)
{
// selectedIndex = 2
if(!centerButtonTappedOnce)
{
centerButtonTappedOnce=true;
centerButton.setImage(UIImage(named: "ic_bullseye_white"), for: .normal)
}
else
{
centerButtonTappedOnce=false;
centerButton.setImage(UIImage(named: "ic_bullseye_red"), for: .normal)
}
customTabBarControllerDelegate?.customTabBarControllerDelegate_CenterButtonTapped(tabBarController: self,
button: centerButton,
buttonState: centerButtonTappedOnce);
}
func hideCenterButton()
{
centerButton.isHidden = true;
}
func showCenterButton()
{
centerButton.isHidden = false;
self.bringcenterButtonToFront();
}
// MARK: - Private methods
private func setupMiddleButton()
{
centerButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
var centerButtonFrame = centerButton.frame
centerButtonFrame.origin.y = view.bounds.height - centerButtonFrame.height
centerButtonFrame.origin.x = view.bounds.width/2 - centerButtonFrame.size.width/2
centerButton.frame = centerButtonFrame
centerButton.backgroundColor = UIColor.red
centerButton.layer.cornerRadius = centerButtonFrame.height/2
view.addSubview(centerButton)
centerButton.setImage(UIImage(named: "ic_bullseye_red"), for: .normal)
centerButton.setImage(UIImage(named: "ic_bullseye_white"), for: .highlighted)
centerButton.addTarget(self, action: #selector(centerButtonAction(sender:)), for: .touchUpInside)
view.layoutIfNeeded()
}
private func bringcenterButtonToFront()
{
print("bringcenterButtonToFront called...")
self.view.bringSubview(toFront: self.centerButton);
}
}
This is the DashboardViewController for complete reference:
class DashboardViewController: BaseViewController, CustomTabBarControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
(self.tabBarController as! CustomTabBarController).customTabBarControllerDelegate = self;
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated);
(self.tabBarController as! CustomTabBarController).showCenterButton();
}
override func viewWillDisappear(_ animated: Bool)
{
super.viewWillDisappear(animated);
self.hidesBottomBarWhenPushed = false;
(self.tabBarController as! CustomTabBarController).hideCenterButton();
}
override func viewWillLayoutSubviews()
{
super.viewWillLayoutSubviews();
if(!isUISetUpDone)
{
self.view.backgroundColor = UIColor.lightGray
self.title = "DASHBOARD"
self.prepareAndAddViews();
self.isUISetUpDone = true;
}
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
//MARK: CustomTabBarControllerDelegate Methods
func customTabBarControllerDelegate_CenterButtonTapped(tabBarController: CustomTabBarController, button: UIButton, buttonState: Bool)
{
print("isDrive ON : \(buttonState)");
}
//MARK: Internal Methods
func menuButtonTapped()
{
let myFriendsVC = MyFriendsViewController()
myFriendsVC.hidesBottomBarWhenPushed = true;
self.navigationController!.pushViewController(myFriendsVC, animated: true);
}
//MARK: Private Methods
private func prepareAndAddViews()
{
let menuButton = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
menuButton.titleLabel?.text = "Push"
menuButton.titleLabel?.textColor = UIColor.white
menuButton.backgroundColor = UIColor.red;
menuButton.addTarget(self, action: #selector(DashboardViewController.menuButtonTapped), for: .touchUpInside)
self.view.addSubview(menuButton);
}
}
with StoryBoard:
Click the tab bar button within the view controller of the particular tab bar item you want to make prominent,
Remove the text, just set the image inset top to -25 of the tab bar button.
Check Like Below image

Resources