Getting 'Unexpectedly found nil' value while creating TabBar Controller items Programatically [duplicate] - ios

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 3 years ago.
I am new to swift. I am Working with TabBar controller. I have set the Tabbar controller and view controllers using a storyboard. But for some designing purpose, I need to add Tab bar items Programmatically. when I ran this code getting crashed stated 'Unexpectedly found nil' values for my outlets. Am I missing anything?
Thanks in Advance..
Here is my Code,
class TabbarController: UITabBarController,UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.tintColor = UIColor.white
tabBarItem.title = ""
setTabBarItems()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
{
// print("hi", terminator: "")
}
func setTabBarItems(){
let v1 = MyOrderViewController()
v1.tabBarItem.image = UIImage(named: "footer_1")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v1.tabBarItem.selectedImage = UIImage(named: "footer_1")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v1.tabBarItem.title = "Orders"
v1.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let v2 = FavouritesViewController()
v2.tabBarItem.image = UIImage(named: "footer_2")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v2.tabBarItem.selectedImage = UIImage(named: "footer_2")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v2.tabBarItem.title = "Favorites"
v2.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let v3 = FavouritesViewController()
v3.tabBarItem.image = UIImage(named: "huggg")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
v3.tabBarItem.selectedImage = UIImage(named: "huggg")?.withRenderingMode(UIImage.RenderingMode.alwaysOriginal)
v3.tabBarItem.title = ""
v3.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let v4 = MoreViewController()
v4.tabBarItem.image = UIImage(named: "footer_4")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v4.tabBarItem.selectedImage = UIImage(named: "footer_4")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v4.tabBarItem.title = "Account"
v4.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let v5 = MoreViewController()
v5.tabBarItem.image = UIImage(named: "footer_5")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v5.tabBarItem.selectedImage = UIImage(named: "footer_5")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
v5.tabBarItem.title = "More"
v5.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let controllers = [v1, v2, v3, v4, v5]
self.viewControllers = controllers
}
}

Reason of you issue is that you didn't connect you ViewControllers classes with storyboards views, so compiler can not instantiate outlets.
Instead of init method of you controllers, use UIStoryboard method instantiateViewController(withIdentifier:).
First create your storyboard
UIStoryboard(name: <storyboard_name>, bundle: nil)
than instantiate appropriate UIViewController using it identifier
let controller = storyboard.instantiateViewController(withIdentifier: <your_controller_identifier>)

Related

iOS Swift Code - Just working when i have a breakpoint on it

I have a tab coordinator that extends from a parent coordinator; i want to assign a delegate to the UITabViewController; but when i do it, it doesn't trigger anything; but if i have a breakpoint in the init function of my coordinator; it will work as expected. My brain is going to explode because i don't know where to search the bug, as it is working as expected when XCODE does have a breakpoint.
import RxSwift
enum HomeRoutes: Route{
case explore
case swaps
case post
case notifications
case profile
}
class HomeCoordinator: ViewCoordinator<HomeRoutes> {
typealias Dependencies = HasUserManager & HasItemService
// MARK: - Stored properties
private let viewDelegate = CrowdswapTabDelegate()
private let disposeBag = DisposeBag()
convenience init(dependencies: Dependencies) {
//Create Tabs
let exploreTab = ExploreCoordinator(dependencies: dependencies)
exploreTab.rootViewController.navigationBar.isHidden = true
exploreTab.rootViewController.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "explore"), selectedImage: nil)
exploreTab.rootViewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let swapsTab = SwapsCoordinator(dependencies:dependencies)
swapsTab.rootViewController.navigationBar.isHidden = true
swapsTab.rootViewController.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "swaps"), selectedImage: nil)
swapsTab.rootViewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let postTab = PostCoordinator(dependencies: dependencies)
postTab.rootViewController.navigationBar.isHidden = true
postTab.rootViewController.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "upload"), selectedImage: nil)
postTab.rootViewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let notificationTab = NotificationCoordinator()
notificationTab.rootViewController.navigationBar.isHidden = true
notificationTab.rootViewController.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "notifications"), selectedImage: nil)
notificationTab.rootViewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let profileTab = ProfileCoordinator(dependencies: dependencies)
profileTab.rootViewController.navigationBar.isHidden = true
profileTab.rootViewController.tabBarItem = UITabBarItem(title: nil, image: #imageLiteral(resourceName: "profile"), selectedImage: nil)
profileTab.rootViewController.tabBarItem.imageInsets = UIEdgeInsets(top: 6, left: 0, bottom: -6, right: 0)
let tabBarController = UITabBarController()
tabBarController.tabBar.tintColor = UIColor(red:0.21, green:0.17, blue:0.46, alpha:1.0)
tabBarController.tabBar.backgroundColor = UIColor.white
tabBarController.tabBar.isTranslucent = false
tabBarController.tabBar.backgroundImage = UIImage()
tabBarController.tabBar.layer.borderWidth = 0.0
tabBarController.tabBar.clipsToBounds = true
tabBarController.viewControllers = [exploreTab.rootViewController, swapsTab.rootViewController, postTab.rootViewController, notificationTab.rootViewController, profileTab.rootViewController]
self.init(controller: tabBarController)
}
// MARK: - Init
init(controller: UITabBarController) {
controller.delegate = viewDelegate
super.init(root: controller)
}
}
And here is my viewDelegate:
class CrowdswapTabDelegate: NSObject, UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
/// Prevent selection of the same tab twice (which would reset its navigation controller)
if viewController.tabBarItem.image == #imageLiteral(resourceName: "upload") {
return false
}
if let viewController = viewController.children[0] as? ExploreViewController{
if tabBarController.selectedIndex == 0 {
viewController.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
}
return true
}
return true
}
}
OK. I solved it; first thought was that it was a problem about threads or a race-condition; but it seemed it was that as the delegate is a weak reference; it wasnt retained BUT when i have a breakpoint, the IDE retained the delegate, so it worked.
solution was to have the delegate parent as a stored property, to have the delegate retained by the parent.

Tabbar controller issue

I am using tab bar controller using xib but when I include one vc there is space remaining in that vc at the end. Here is video of my problem
Here is my viewcontroller xib and code
Please help me with it
here is my tabbar controller code
import UIKit
class TabbarControllerVC: UITabBarController, UITabBarControllerDelegate{
let homeVC = HomeVC(nibName: "HomeVC", bundle: nil)
let listVC = HomeVC(nibName: "HomeVC", bundle: nil)
let notificationVC = HomeVC(nibName: "HomeVC", bundle: nil)
let settingVC = MyProfileVC(nibName: "MyProfileVC", bundle: nil)
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
tabBar.tintColor = ColorConstants.ThemeColor
var tabbarControllers = [UIViewController]()
tabbarControllers.append(homeVC)
tabbarControllers.append(listVC)
tabbarControllers.append(notificationVC)
tabbarControllers.append(settingVC)
self.setViewControllers(tabbarControllers, animated: true)
homeVC.tabBarItem = UITabBarItem(title: "", image: UIImage(named : "Home"), selectedImage: UIImage(named : "Home"))
listVC.tabBarItem = UITabBarItem(title: "", image: UIImage(named : "List"), selectedImage: UIImage(named : "List"))
notificationVC.tabBarItem = UITabBarItem(title: "", image: UIImage(named : "Notification"), selectedImage: UIImage(named : "Notification"))
settingVC.tabBarItem = UITabBarItem(title: "", image: UIImage(named : "Setting"), selectedImage: UIImage(named : "Setting"))
UITabBar.appearance().tintColor = ColorConstants.ThemeColor
homeVC.tabBarItem.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
listVC.tabBarItem.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
notificationVC.tabBarItem.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
settingVC.tabBarItem.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
if #available(iOS 10.0, *) {
UITabBar.appearance().unselectedItemTintColor = ColorConstants.BlackColor
} else {
UITabBarItem.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: ColorConstants.BlackColor], for: .normal)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupTabBarSeparators() {
let itemWidth = floor(self.tabBar.frame.size.width / CGFloat(self.tabBar.items!.count))
// this is the separator width. 0.5px matches the line at the top of the tab bar
let separatorWidth: CGFloat = 0.5
// iterate through the items in the Tab Bar, except the last one
for i in 0...(self.tabBar.items!.count - 1) {
// make a new separator at the end of each tab bar item
let separator = UIView(frame: CGRect(x: itemWidth * CGFloat(i + 1) - CGFloat(separatorWidth / 2) , y: 15, width: CGFloat(separatorWidth), height: self.tabBar.frame.size.height - 30))
// set the color to light gray (default line color for tab bar)
separator.backgroundColor = UIColor.lightGray
self.tabBar.addSubview(separator)
}
}
override func viewWillAppear(_ animated : Bool){
super.viewWillAppear(true)
setupTabBarSeparators()
}
}

Class has no initializers when using POP on a variable

I want to apply protocol oriented programming in one of my applications. I created a protocol called "CustomAnchor" to get rid of the huge amount of commands, needed for Autolayout. But when I assign this protocol to a constant e.g profileImage, the Controller gets the following error:
Class 'ProfileController' has no initializers
On the constant I get this gray warning:
Stored property 'profileImageView' without initial value prevents synthesized initializers
This is how the code looks like: (Use of POP at the bottom)
class ProfileController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.navigationItem.title = "Profil"
self.navigationItem.largeTitleDisplayMode = .never
fillData()
setupView()
confBounds()
}
func fillData() {
profileImageView.image = UIImage(named: "test")
}
func setupView() {
view.addSubview(profileImageView)
}
func confBounds() {
profileImageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
profileImageView.anchor(top: self.view.topAnchor, left: nil, bottom: nil, right: nil, paddingTop: 60, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 140, height: 140)
}
let profileImageView: UIImageView, CustomAnchor = { () -> UIImageView in
let pView = UIImageView()
pView.contentMode = .scaleAspectFill
pView.clipsToBounds = true
pView.image = UIImage(named: "test")
return pView
}()
}
Because no one answered I made a simple workaround and gave the Property to the Controller. Not really what I wanted (smooth POP) but it works..

Accessing functions/variables inside a UITabBarController with or without global variables?

currently I have a layout like this with my app. I have a UITabBarcontroller as my root view controller inside app delegate, this is fine and works great.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FIRApp.configure()
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = Tabs()
UITabBar.appearance().tintColor = UIColor.init(r: 198, g: 214, b: 91)
UITabBar.appearance().barTintColor = UIColor.init(r: 47, g: 47, b: 47)
UINavigationBar.appearance().barTintColor = UIColor.init(r: 53, g: 57, b: 77)
UINavigationBar.appearance().tintColor = UIColor.init(r: 198, g: 214, b: 91)
However, in my layout with the tab controller, I have laid it out in a way where i have UITabBar at the top, followed by an override didload function that lays out all the views like this.
class Tabs: UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
tabBar.isTranslucent = true
let feed = feedController()
let feedArray = UINavigationController(rootViewController: feed)
let feedButton = UITabBarItem(title: nil, image: UIImage(named: "feed.png"), selectedImage: UIImage(named: "selectedimage.png"))
feedButton.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
feedArray.tabBarItem = feedButton
let messages = messagesController()
let messagesArray = UINavigationController(rootViewController: messages)
let messagesButton = UITabBarItem(title: nil, image: UIImage(named: "messages.png"), selectedImage: UIImage(named: "selectedimage.png"))
messagesButton.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
messagesArray.tabBarItem = messagesButton
let post = postController()
let postButton = UITabBarItem(title: nil, image: UIImage(named: "post.png"), selectedImage: UIImage(named: "selectedimage.png"))
postButton.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
post.tabBarItem = postButton
let profile = profileController()
let profileArray = UINavigationController(rootViewController: profile)
let profileButton = UITabBarItem(title: nil, image: UIImage(named: "profile.png"), selectedImage: UIImage(named: "selectedimage.png"))
profileButton.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
profile.tabBarItem = profileButton
let settings = settingsController()
let settingsArray = UINavigationController(rootViewController: settings)
let settingsButton = UITabBarItem(title: nil, image: UIImage(named: "settings.png"), selectedImage: UIImage(named: "selectedimage.png"))
settingsButton.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
settingsArray.tabBarItem = settingsButton
if FIRAuth.auth()?.currentUser?.uid == nil {
DispatchQueue.main.async {
self.handleLogout()}
}
/* all views BOII */ viewControllers = [feedArray, messagesArray, post, profileArray, settingsArray]
However, when I try to access a function or whatever in a viewcontroller inside this tab controller from another viewcontroller inside. like this
class settingsController: UIViewController{
messagesController().removeMessages()
}
.......
class messagesController: UIViewController{
removeMessages(){
messages.removeAll()
messagesDictionary.removeAll()
tableView.reloadData()
print("working")
}
}
The functions get called (im putting it in a function on logout), I get the print message but anything I wanted affected on that viewcontroller does not seem to work. Currently, I have so far tried these methods (on settings controller).
I know this has to do with something with a tabbarcontroller in swift, something about how the views inside run parallel to each other.
What I did do and it worked, was put the declarations of the variables before the class in my tabs.swift file. So I took the let statements that set up the views and put them before the class was declared and under the import statements. This worked, but is this bad practice and is there a better way of doing it? As this let statements are now global.
Really would appreciate the help guys and Im so sorry for the long post, I just wanted you to try and understand what Im saying here, I'm new.
If your UIKit objects manipulates from current stack it won't work. Since object should be called from main thread.
DispatchQueue.main.async {
self.handleLogout()
}
I can only suggest you fast solution use next block for UI elements:
dispatch_async(dispatch_get_main_queue()) {
messages.removeAll()
messagesDictionary.removeAll()
tableView.reloadData()
}

presenting ViewController Label from UITabBarController

I'm new in programming IOS Apps with swift.
I've programed TabBar in AppDelegate.swift with the code, and with 2 ViewControllers program in MainPageViewController.swift and DetailViewController.swift.
let tabBarController = UITabBarController()
let mainPageVC = MainPageViewController()
let detailVC = DetailViewController()
tabBarController.viewControllers = [mainPageVC,detailVC]
let mainTabBar = UITabBarItem(title: nil, image: UIImage(named: "03")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal), selectedImage: UIImage(named: "06")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal))
let detailTabBar = UITabBarItem(title: nil, image: UIImage(named: "02")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal), selectedImage: UIImage(named: "05")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal))
mainPageVC.tabBarItem = mainTabBar
deviceVC.tabBarItem = deviceTabBar
mainPageVC.tabBarItem.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
deviceVC.tabBarItem.imageInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
UITabBar.appearance().barTintColor = UIColor(red: 211/255, green: 1, blue: 246/255, alpha: 1)
window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = tabBarController
self.window?.makeKeyAndVisible()
And then I added a Label in the MainPageViewController.swift
class MainPageViewController: UIViewController {
#IBOutlet weak var testLabel: UILabel!
#IBOutlet weak var mainTabBarItem: UITabBarItem!
override func viewDidLoad() {
super.viewDidLoad()
let backgraondLayer = color.gl
backgraondLayer.frame = view.frame
self.view.layer.insertSublayer(backgraondLayer, atIndex: 0)
testLabel.backgroundColor = UIColor.blackColor()
}
I keep getting the "fatal error: unexpectedly found nil while unwrapping an Optional value" error.
And if I remove the "Label.backgroundColor" code and setup the background color in the Attribute Inspector, the Build App will not shown the Label.
What code did i miss?
Thanks in advance!
If it gives you "nil while unwrapping an optional value" in testLabel.backgroundColor = UIColor.blackColor(), the chances are that your label is not connected to the control in Interface Builder. In the editor the IBOutlets have little circle next to the declaration. Empty one for unconnected and circle with dot inside for connected.
If you are still at the beginning of your app dev, consider doing the UITabBarController plumbing in Interface Builder/Storyboard. It will save you from some boring work every time you add a new tab.

Resources