Navigation controller isn't working as expected - ios

I am using navigation controller for connecting 8 form page to each other. According to users choice it is navigating next screen using following code :
if (GlobalVariable.page[0] == true || GlobalVariable.page[1] == true || GlobalVariable.page[2] == true || GlobalVariable.page[3] == true) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationVC:UIViewController!
destinationVC = storyboard.instantiateViewController(withIdentifier: "firstInfo") as! FirstInfoVC
navigationController?.show(destinationVC, sender: self)
}
When I use above code user can go previous screen using swipe left gesture. It is okay until now. However, every page has menu button and after filled 8 page or whenever they wants user can click that menu button and according to choice it needs to navigate the another view controller independent from that 8 page. For navigating that independent view controller I am also trying to use above code in my menu table view didselectrowat function but it is not working (actually, it is doing nothing). What could be the reason of it and how can I fix it?
Edit :
In my side navigation controller class
override func viewDidLoad() {
super.viewDidLoad()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let menu = storyboard.instantiateViewController(withIdentifier: "MenuTableViewController") as! MenuTableViewController
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menu, menuPosition: .left)
sideMenu?.menuWidth = UIScreen.main.bounds.width * 0.8
sideMenu?.bouncingEnabled = false
//Navigation Bar Özelleştirmesi İçin Renk Mavi Üstüne Beyaz
UINavigationBar.appearance().barTintColor = UIColor(red:0.01, green:0.47, blue:0.74, alpha:1.0)
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
view.bringSubview(toFront: navigationBar)
}
and in my menu table view code is following:
class MenuTableViewController: UITableViewController {
override func viewDidLoad()
{
super.viewDidLoad()
tableView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationVC:UIViewController!
if (indexPath.row == 2) {
destinationVC = storyboard.instantiateViewController(withIdentifier: "independentPage") as? IndependentVC
navigationController?.show(destinationVC, sender: self)
}
}
}

Related

Add UIViewController made via storyboard to TabBarController.setViewControllers

I am failing to understand the fundamentals of what is needed to add my HomeViewController (UIViewController) as one of the tabs in my homeTabBarController (UITabBarController) using the setViewControllers method.
I have tried initializing this and simply adding it as a param in the method. There seems to be a difference between a view controller created via storyboard and one created programmatically because when I tried adding a viewcontroller:UIViewController programmatically to the setViewControllers method, it worked fine.
My code below compiles however I get a runtime exception Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7b8491598) at the line when homeTabBarController.setViewControllers is called
`
func loadTabBar() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = storyboard.instantiateViewController(identifier: Constants.Storyboard.homeViewController) as? HomeViewController
homeViewController!.title = "Home"
homeTabBarController.setViewControllers([homeViewController!], animated: false)
homeTabBarController.modalPresentationStyle = .fullScreen
present(homeTabBarController, animated: true)
}
`
//MARK: - Create the instances of ViewControllers
let grayViewController = HomeViewController()
let blueViewController = FirstViewController()
let brownViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
//set title of the viewcontrollers
grayViewController.title = "home"
blueViewController.title = "first"
brownViewController.title = "second"
//Assigne the viewcontrollers to the TabBarViewController
self.setViewControllers([ grayViewController, blueViewController, brownViewController], animated: false)
//set system images to each tabBars
guard let items = self.tabBar.items else {
return
}
let images = ["house.fill", "star.fill", "bell.fill"]
for i in 0...2 {
items[i].image = UIImage(systemName: images[i])
}
self.tabBar.tintColor = .black
}
// You can download the project from the below github link
https://github.com/ahmetbostanciklioglu/AddingTabBarControllerProgrammatically.git

SWIFT: If I implement the UITAB bar programmatically, is it possible to edit the different tabItems inside the storyBoard

I have implemented a tabBar programmatically:
class ViewController: UIViewController {
let tabBarCnt = UITabBarController()
override func viewDidLoad() {
super.viewDidLoad()
tabBarCnt.tabBar.tintColor = UIColor.black
createTabBarController()
}
func createTabBarController() {
let firstVc = UIViewController()
let downloadViewController = DownloadsViewController()
downloadViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 0)
let bookmarkViewController = BookmarksViewController()
bookmarkViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 1)
let favoritesViewControllers = FavoritesViewController()
favoritesViewControllers.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 2)
// Adding navigationControllers
let controllerArray = [downloadViewController, bookmarkViewController, favoritesViewControllers]
// For somereason this made the word Home appear in first tab
tabBarCnt.viewControllers = controllerArray.map{ UINavigationController.init(rootViewController: $0)}
self.view.addSubview(tabBarCnt.view)
}
}
DownloadViewController.swift
class DownloadsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
self.title = "Downloads"
}
}
This is the result:
MY QUESTION IS:
Do I have to implement all the layouts of the different viewControllers downloadViewController, bookmarkViewController, favoritesViewControllers programmatically?
Can't I use the storyboard and associate a viewController and do all the UI design and implementation there like this:
The problem here is that if I will implement all the layout programmatically, it won't be an obvious or a practical thing to do.
And assigning DownloadsViewController to that storyboard ViewController, doesn't make it get display it when I navigate to DownloadsTab in the UITabBar.
Yes, you can do that currently your creating view controller instance programmatically, instead of that you have to load/get viewController instance from storyBoard like this.
UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(identifier: "downloadsVC") as! DownloadsViewController
before calling this method set viewController StoryboardID

Loading a new viewController on the same tab bar item not working

So I am having a problem loading a new view controller on the same tab bar item. I have a tab bar controller as my root controller and 4 tabs on the tab bar. One of which is the account tab, when the user is logged in he would need to see an overview of his account details but when there is no user there needs to be a sign-in / login page on that tab bar item. For the moment I have a custom tab bar class for my tabbarcontroller with a function that I thought was the solution but nothing happens. I have put a breakpoint in the view controller that needs to loaded (the user detail page) and it comes in the ViewDidLoad so it loads but it doesn't appear on the screen.
Hope I can finally solve this problem!
Kind regards
B.
this is my custom tab bar controller class:
import UIKit
class TabBarViewController: UITabBarController, UITabBarControllerDelegate {
var window: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool{
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
let index = tabBarController.viewControllers?.firstIndex(of: viewController)
if index == 3{ // Index of Account tab
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = mainStoryBoard.instantiateViewController(withIdentifier: "MessagesViewController") as! MessagesViewController
window?.rootViewController = loginVC
window?.makeKeyAndVisible()
}
return true// you decide
}
}
here is the code that presents full screen loginVc
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool{
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
let index = tabBarController.viewControllers?.firstIndex(of: viewController)
if index == 1{ // Index of Account tab
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = mainStoryBoard.instantiateViewController(withIdentifier: "MessagesViewController") as! MessagesViewController
loginVC.modalPresentationStyle = .fullScreen
self.present(loginVC, animated: false, completion: nil)
}
return true// you decide
}
If you also want to have bottom bar ... your that viewController should be Navigation Controller and you can push your desired ViewController then like
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool{
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
let index = tabBarController.viewControllers?.firstIndex(of: viewController)
if index == 1{ // Index of Account tab
let mainStoryBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = mainStoryBoard.instantiateViewController(withIdentifier: "MessagesViewController") as! MessagesViewController
// loginVC.modalPresentationStyle = .fullScreen
if let navigation = viewController as? UINavigationController {
navigation.pushViewController(loginVC, animated: false)
}
}
return true// you decide
}
It will show back button and navigationBar on top
if you dont want NavigationBar then in your MessagesViewController did load function call
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = true
// Do any additional setup after loading the view.
}
And if you just want to hide back button then call
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.setHidesBackButton(true, animated: false);
// Do any additional setup after loading the view.
}
Changing your rootViewController is bad practice in Swift.
The correct way to handle tasks like this is to override your accountViewController's viewWillAppear function to determine if the user isn't logged in and present your loginViewController. Try something like:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// authManager would be a custom class that manages authorization with a property for loggedIn
if authManager.loggedIn == false {
guard let loginVC = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "login") as? LoginViewController else { return }
self.present(loginVC, animated: true, completion: nil)
}
}

Side bar menu icon is invisible in Tab bar based view controller

I've implemented side bar using SWRevealViewController. But when i do same using Tab bar based view controller, menu icon is invisible only in tab based screens.
Icon is visible in interface builder but invisible in simulator/device.
Functionality is working in every screen, even in tab based view controller.
When i drag from left to right and vice versa, functionality is working fine.
Only the issue is menu icon is invisible in simulator.
Can anyone help me?
Thanks in advance.
below is the swift file for tab
import UIKit
class ItemOneViewController: UIViewController {
#IBOutlet weak var menuBar: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
setMenuBarBtn(menuBar: menuBar)
navigationController!.navigationBar.barTintColor = UIColor.color(.blue)
}
func setMenuBarBtn(menuBar: UIBarButtonItem) {
menuBar.target = revealViewController()
menuBar.action = #selector(SWRevealViewController.revealToggle(_:))
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
}
Code is similar for three tabs.
I've done below tasks before asking question here
Deleted and recreated screens two times.
Cross checked everything and compared tab bar based scenes source code with other scenes.
googled it.
Below are the images for your reference
Tab based screen with side bar functionality (working good)
Tab based screen without menu icon (Problem persist here , i want menu icon to be displayed)
HomeScreen with menu icon (Working good)
Navigation Controller attribute inspector
my storyboard
Item One View Controller view hierarchy
Item One view Controller related navigation controller scene
You gave to add normal button on HomeViewController
Then directly perform segue through storyboard(from burger menu button).
#IBAction func sideMenuBtnAction(_ sender: UIButton) {
performSegue(withIdentifier: "sideMenu", sender: self)
}
Then on SideMenuController :
import UIKit
import SideMenu
class SideMenuViewController: UIViewController {
#IBOutlet var sideMenuTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
SideMenuManager.default.menuPresentMode = .menuSlideIn
SideMenuManager.default.menuFadeStatusBar = false
SideMenuManager.default.menuWidth = self.view.frame.width / 1.4
SideMenuManager.default.menuShadowOpacity = 0.3
sideMenuTableView.tableFooterView = UIView(frame: .zero)
}
}
You need to create a tabbarController class like below, and assign this ViewController class to tabbar on storyboard ...
I have not tested this code but something like this should work for your case
class ViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
self.viewControllers = [firstSw,secondsw, thirdSw]
// Do any additional setup after loading the view.
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
OR Following by doing this there is no need to create a tabbarController
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// create UIWindow with the same size as main screen
window = UIWindow(frame: UIScreen.mainScreen().bounds)
// create story board. Default story board will be named as Main.storyboard in your project.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// create view controllers from storyboard
// Make sure you set Storyboard ID for both the viewcontrollers in
// Interface Builder -> Identitiy Inspector -> Storyboard ID
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
// Set up the Tab Bar Controller to have two tabs
let tabBarController = UITabBarController()
tabBarController.viewControllers = [firstSw,secondsw, thirdSw]
// Make the Tab Bar Controller the root view controller
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
return true
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}

Popup view controller shows dark background when user navigates to another view controller without closing the current pop up view

I have a table view which is inside a tab bar. When I click the table view, it shows a popup view controller like this:
But, whenever I navigate to another tab bar, leaving the pop up view as it is without closing it, and when I come back to the previous pop up view, it shows a dark background as shown is the picture below:
How do I solve this problem?
*Note = I am new to IOS
code for adding my sub views:
import UIKit
class NotificationViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var dataALL = [NotificationDataModel]()
#IBOutlet weak var tableViewAll: UITableView!
#IBOutlet var mainSegmentedControl: UISegmentedControl!
lazy var iNotificationViewController : INotificaitonViewController = {
let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
var viewController = storyBoard.instantiateViewController(withIdentifier: "INotificaitonViewController") as! INotificaitonViewController
self.addViewContollerAsChildViewcontroller(childViewContoller: viewController)
return viewController
}()
var activityIndicator : UIActivityIndicatorView = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
tableViewAll.delegate = self
tableViewAll.dataSource = self
let nib = UINib(nibName: "TableViewCell", bundle: nil)
tableViewAll.register(nib, forCellReuseIdentifier: "customCell")
setupView()
tableViewAll.separatorStyle = .none
self.startShowingIndication()
downloadAllJSONData {
self.tableViewAll.reloadData()
self.stopShowingIndication()
print("sucess")
}
}
func downloadAllJSONData(completed : #escaping ()->()){
guard let url = URL(string: "http://www.swipecrafts.com/notice/get")else {return}
var request = URLRequest.init(url: url)
request.httpMethod = "POST"
request.addValue("cf7ab8c9d4efae82b575eabd6bec76cbb86c6108391e036387f3d5356a582171519367747000", forHTTPHeaderField: "api_ky")
let postdata = "school_id=\(SCHOOL_ID)"
request.httpBody = postdata.data(using: .utf8)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if error == nil{
do{
self.dataALL = try JSONDecoder().decode([NotificationDataModel].self, from: data!)
DispatchQueue.main.async {
completed()
print(self.dataALL.count)
}
}catch{
print(error)
}
}
}.resume()
}
private func setupView(){
setupSegmentedControl()
updateView()
}
private func updateView(){
iNotificationViewController.view.isHidden = !(mainSegmentedControl.selectedSegmentIndex == 0)
}
private func setupSegmentedControl(){
mainSegmentedControl.removeAllSegments()
mainSegmentedControl.insertSegment(withTitle: "General Notification", at: 0, animated: false)
mainSegmentedControl.insertSegment(withTitle: "All Notificatoin", at: 1, animated: false)
mainSegmentedControl.addTarget(self, action: #selector(selectionDidChange(sender:)), for: .valueChanged)
mainSegmentedControl.selectedSegmentIndex = 0
}
#objc func selectionDidChange(sender: UISegmentedControl) {
updateView()
}
private func addViewContollerAsChildViewcontroller (childViewContoller : UIViewController){
addChildViewController(childViewContoller)
view.addSubview(childViewContoller.view)
childViewContoller.view.frame = view.bounds
childViewContoller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
childViewContoller.didMove(toParentViewController: self)
}
private func removeViewControllerAsChildViewController(childViewController : UIViewController){
childViewController.willMove(toParentViewController: nil)
childViewController.view.removeFromSuperview()
childViewController.removeFromParentViewController()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataALL.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableViewAll.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! TableViewCell
cell.customInit(noticeTitle: dataALL[indexPath.row].notice_title!,noticeDiscripetion: dataALL[indexPath.row].notice_description!)
let downloadedimage = dataALL[indexPath.row].image_name
if downloadedimage != nil{
cell.lblImageNotificaiton.downloadedFrom(link: downloadedimage!)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToPopUpViewww", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? PopUpViewController{
destination.notificationfianlData = dataALL[(tableViewAll.indexPathForSelectedRow?.row)!]
}
}
func startShowingIndication(){
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
}
func stopShowingIndication(){
activityIndicator.stopAnimating()
}
}
Currently you are presenting popupviewcontroller over the context of NotificationViewController. Using Perform Segue "goToPopUpViewww".
Instead of that please try to present popupviewcontroller over the context of UITabBarController. This makes more sense. This change situation as - when a popup is presented user can't switch the tab.
So you are presenting a Seague Modally, You can try to change the presentation style (e.g over current context) of the seague programmatically or through story board, it might help in this scenario.
Please see the screen shot
This is an example project from Apple documentation that was converted to Swift 3. It uses a custom UIPresentationController to modally present ViewControllers and also controls how they are animated on and off screen. Follow the same approach and you shouldn't have any issues with dark backgrounds.
https://github.com/ooper-shlab/CustomTransitions-Swift
I would look into UIPopoverPresentationController.
Normally when you present something in the manner you've describe, a user should not be able to navigate away from the parent view controller without first dismissing the "popup" or "popover" view. So even if you don't go with UIPopoverPresentationController you should consider dismissing the popover view via any taps outside of it.
Update 4/3/18
I would definitely recommend researching and reading some documentation or following some tutorials so you can understand exactly what code does what and why. If you haven't already the documentation link for UIPopoverPresentationController that I provided above points you to this, which is pretty much exactly what you need.
Here's an example with some of your code.
lazy var iNotificationViewController : INotificaitonViewController = {
let storyBoard = UIStoryboard(name: "Main", bundle: Bundle.main)
let viewController = storyBoard.instantiateViewController(withIdentifier: "INotificaitonViewController")
// may want to set the viewController.preferredContentSize here => which will be the size of your popover
viewController.modalPresentationStyle = .popover
if let popoverPresentationController = viewController.popoverPresentationController {
popoverPresentationController.delegate = self
// set the .sourceView and .sourceRect so that the popover can position itself and arrow accordingly
}
present(viewController, animated: true) {
// closure for when the popover is visible
}
return viewController
}
As mentioned by #Britto Thomas you really shouldn't be adding your popover as a child but presenting it.
Update 4/9/18
Apologizes, I forgot to add this tid bit, which is very important. Since popovers are intended for iPads you have to implement this delegate method as follows:
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
Otherwise it will simply present it as any other modal, despite the fact that we told the 'modalPresentationStyle' to be '.popover'

Resources