I am trying to get a pod to work.
It is from here:
https://github.com/Ahmadalsofi/SOTabBar
I get no errors until runtime, where I get a long list of what seems like a thread loop of sorts.
Images:
1.Tread Stack
2.Line where error happens
3.Error Message
Where I can trace the error to:
My scene delegate entry to application: (I am not using storyboards)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = MainTabBarVC()
window?.makeKeyAndVisible()
}
}
My MainTabBarVC:
import UIKit
import SOTabBar
class MainTabBarVC: SOTabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstVC = SearchVC(nibName: "Main", bundle: nil)
let secondVC = TestVC(nibName: "Main", bundle: nil)
firstVC.tabBarItem = UITabBarItem(title: "Home", image: UIImage(systemName: "mappin.and.ellipse"), selectedImage: UIImage(systemName: "star"))
secondVC.tabBarItem = UITabBarItem(title: "Chat", image: UIImage(named: "secondImage"), selectedImage: UIImage(systemName: "mappin.and.ellipse"))
viewControllers = [firstVC, secondVC]
}
override func loadView() {
super.loadView()
SOTabBarSetting.tabBarHeight = 60.0
SOTabBarSetting.tabBarTintColor = UIColor.red
SOTabBarSetting.tabBarBackground = UIColor.purple
SOTabBarSetting.tabBarCircleSize = CGSize(width: 50.0, height: 50.0)
SOTabBarSetting.tabBarSizeImage = CGFloat(20)
SOTabBarSetting.tabBarSizeSelectedImage = CGFloat(40)
SOTabBarSetting.tabBarAnimationDurationTime = 2
}
}
extension MainTabBarVC: SOTabBarControllerDelegate {
func tabBarController(_ tabBarController: SOTabBarController, didSelect viewController: UIViewController) {
print("Hello")
}
}
My SearchVC and TestVC are just simple VCs with a different background color like:
import UIKit
class SearchVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
The view controllers themselves are fine, the issue seems to be the pod. I tried looking at their github source but I don't know what could be causing it. So the issue is either the pod or my implementation of it.
Related
I have a view controller on the storyboard that I'm trying to send the user to (TitleInfoVC), but the view controller I'm trying to send the user from was created in code, in TabBarVC, which is being set as the rootViewController in the SceneDelegate.
When I try either of the attempts in the DiscoverVC I'm getting the following error:
Attempt to present <x.TitleInfoVC: 0x12b417630> on <x.DiscoverVC: 0x12b411310> (from <xDiscoverVC: 0x12b411310>) whose view is not in the window hierarchy.
How do I send the user to the TitleInfoVC when the DiscoverVC isn't in the storyboard?
SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = TabBarVC()
window?.makeKeyAndVisible()
}
TabBarVC:
override func viewDidLoad() {
super.viewDidLoad()
let vc1 = UINavigationController(rootViewController: DiscoverVC())
let vc2 = UINavigationController(rootViewController: SearchVC())
vc1.tabBarItem.image = UIImage(systemName: "house")
vc2.tabBarItem.image = UIImage(systemName: "magnifyingglass")
vc1.title = "Discover"
vc2.title = "Search"
setViewControllers([vc1, vc2], animated: true)
}
DiscoverVC (Where I'm trying to send the user from) :
func sendToView(titleID: Int) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let titleInfoVC = storyBoard.instantiateViewController(withIdentifier: "TitleInfoVC") as! TitleInfoVC
titleInfoVC.titleID = titleID
self.present(titleInfoVC, animated: true, completion: nil)
let titleInfoVC = TitleInfoVC()
titleInfoVC.titleID = titleID
self.navigationController?.pushViewController(titleInfoVC, animated: true)
self.present(titleInfoVC, animated: true, completion: nil)
}
TitleInfoVC (Where I'm trying to send the user to) :
#IBOutlet weak var titleIDLabel: UILabel!
var titleID = 0
override func viewDidLoad() {
super.viewDidLoad()
titleIDLabel.text = "Title ID \(titleID)"
}
//EDIT:
Where sendToView is being called from (completely different view controller)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedTitleID = titles[indexPath.row].id
let discoverVC = DiscoverVC()
discoverVC.sendToView(titleID: selectedTitleID)
}
From what you show you need to work with a shared DiscoverVC.
in TabBarVC :
// use the share discoverVC
let vc1 = UINavigationController(rootViewController: DiscoverVC.shared)
in DiscoverVC declare the shared instance :
class DiscoverVC: UIViewController {
static var shared = DiscoverVC()
in TableCellVC, use the shared instance
let discoverVC = DiscoverVC.shared
discoverVC.sendToView(titleID: selectedTitleID)
i created two projects to learn how the delegate method is working..
one project created WITHOUT storyboard, just via code and my delegate is working just fine.
i built the other Project WITH storyboard, which means all ViewControllers are visible in the Interfacebuilder..
i am sure the issue lays in the definition of the ViewControllers in the code file:
let homeVC = HomeViewController()
Can someone please tell what is wrong here?
import UIKit
protocol HomeViewControllerDelegate: AnyObject {
func showMenu()
}
class HomeViewController: UIViewController {
var delegate: HomeViewControllerDelegate?
override func viewDidLoad() {
title = "App"
super.viewDidLoad()
configureNaviBar()
}
func configureNaviBar() {
// Left Bar Button Item
let burgerButton = UIImage(systemName: "line.horizontal.3")
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: burgerButton, style: .plain, target: self, action: #selector(showMenu))
}
#objc func showMenu(sender: AnyObject) {
print("show Menu (home)")
// homeDelegate is nil?
delegate!.showMenu() // throws an error!
}
}
import UIKit
class MainViewController: UIViewController {
let naviVC:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NaviVC") as! NaviVC
let menuVC:UIViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SideMenuID") as! SideMenuViewController
let homeVC = HomeViewController()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
setupContainerView()
}
func setupContainerView() {
// menu
addChild(menuVC)
self.view.addSubview(menuVC.view)
menuVC.view.frame = CGRect(x: 0, y: 0, width: 200, height: 896)
menuVC.didMove(toParent: self)
// Home
homeVC.delegate = self
addChild(naviVC)
self.view.addSubview(naviVC.view)
naviVC.view.frame = self.view.bounds
naviVC.didMove(toParent: self)
}
}
extension MainViewController: HomeViewControllerDelegate {
func showMenu() {
// does not get called
print("did tap menu")
}
}
Error:
Debug_project/HomeViewController.swift:49: Fatal error: Unexpectedly found nil while unwrapping an Optional value
i am already searching for days now, and just can't find the solution for this...
please help me out guys
I found the solution!
Tanks to Phillip Mills and all others for helping me find this..
the solution is:
change
let homeVC = HomeViewController()
to
override func viewDidLoad() {
super.viewDidLoad()
let homeVC = naviVC.viewControllers.first as! HomeViewController // working: this is it!
}
class MainViewController: UIViewController {
let naviVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NaviVC") as! NaviVC
let menuVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SideMenuID") as! SideMenuViewController
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
setupContainerView()
}
func setupContainerView() {
// menu
addChild(menuVC)
self.view.addSubview(menuVC.view)
menuVC.view.frame = CGRect(x: 0, y: 0, width: 200, height: 896)
menuVC.didMove(toParent: self)
// Home
let homeVC = naviVC.viewControllers.first as! HomeViewController // working: this is it!
homeVC.delegate = self
addChild(naviVC)
self.view.addSubview(naviVC.view)
naviVC.view.frame = self.view.bounds
naviVC.didMove(toParent: self)
}
}
extension MainViewController: HomeViewControllerDelegate {
func showMenu() {
// does not get called
print("did tap menu")
}
}
I am starting an app programmaticaly with no storyboards. When I try to make my UITabBarController the root view controller it looks like The background color should be red and the tab item should be a house. What could be the problem?
SceneDelegate code:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = TabBarController()
window?.makeKeyAndVisible()
}
HomeViewController code:
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
// Do any additional setup after loading the view.
}
}
TabBarController code:
import UIKit
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupTab()
}
func setupTab(){
let homeVC = HomeViewController()
homeVC.tabBarItem = UITabBarItem(title: "Home", image: UIImage(named: "House"), tag: 0)
let homeNC = UINavigationController(rootViewController: homeVC)
tabBarController?.viewControllers = [homeNC]
}
}
The work for setting up the tab view controller needs to be done while you are setting up the scene in the SceneDelegate. The HomeViewContoller's viewDidLoad was never being called.
var homeNavigationController : UINavigationController!
var secondHomeNavigationController : UINavigationController!
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let tabBarController = TabBarController()
homeNavigationController = UINavigationController.init(rootViewController: HomeViewController())
secondHomeNavigationController = UINavigationController.init(rootViewController: HomeViewController())
tabBarController.viewControllers = [homeNavigationController, secondHomeNavigationController]
// let item1 = UITabBarItem(title: "Home", image: UIImage(named: "House"), tag: 0)
let item1 = UITabBarItem(tabBarSystemItem: .featured, tag: 0)
let item2 = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 1)
homeNavigationController.tabBarItem = item1
secondHomeNavigationController.tabBarItem = item2
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
}
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupTab()
}
func setupTab(){
}
}
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
// Do any additional setup after loading the view.
}
}
I get an nearly black screen when I try to load this view. But if I add view.backgroundColor = .red it shows the color red correctly. Just the settings I did in Storyboard wont get shown.
Here is the code I used:
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
window?.rootViewController = mainStoryboard.instantiateViewController(withIdentifier: "ResultsViewController") as! ResultsViewController
window?.makeKeyAndVisible()
}
import UIKit
class ResultsViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
}
You seem to have created the ResultsViewController in the storyboard. You'll have to load it from the storyboard to set it as rootViewController.
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
window?.rootViewController = mainStoryboard.instantiateViewController(withIdentifier: "ResultsViewController") as! ResultsViewController
Note: Don't forget to set the identifier for ResultsViewController as "ResultsViewController" in the storyboard.
I do have a non-Storyboard, 100% coded UIViewController, UICollectionView and UICollectionViewCell - works perfect.
here's the code in question:
SceneDelegate
not sure if this is relevant, tho.
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let myController = MyViewController(collectionViewLayout: layout)
window?.rootViewController = myController
window?.makeKeyAndVisible()
}
.
.
ViewController
very simple and straight forward...
class MyViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let data = loadOnboardingData()
.
.
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .white
collectionView?.register(MyPageCell.self, forCellWithReuseIdentifier: "cellId")
collectionView?.isPagingEnabled = true
collectionView.showsHorizontalScrollIndicator = false
collectionView?.tag = myPageControl.currentPage
setupMyPageControl()
}
ViewControllerExtention
here's the problem: the pushViewController method just doesn't do anything but the modal present works like a charm and I'm not getting what's wrong and why:
extension MyViewController: MyPageCellDelegate {
.
.
func didTabOnActionButton(title: String) {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let homeViewController = storyboard.instantiateViewController(withIdentifier: "HomeViewController") as? HomeViewController else {
print("Coun't find controller")
return
}
navigationController?.pushViewController(homeViewController, animated: true) <- NO EFFECT
//present(homeViewController, animated: true, completion: nil) <- WORKS PERFECT!
}
MyPageCell
I set up the Delegate via protocol and it seems that's fine too
protocol MyPageCellDelegate {
func didTabOnActionButton(title: String)
}
class MyPageCell: UICollectionViewCell {
var delegate: MyPageCellDelegate?
let myActionButton: UIButton = {
let button = UIButton(type: .system)
return button
}()
myActionButton.addTarget(self, action: #selector(self.doAction), for: .touchUpInside)
.
.
#objc private func doAction(_ sende: Any) {
delegate?.didTabEndOnboardingActionButton(title: "end Onboarding")
}
so, any Idea what's wrong with:
navigationController?.pushViewController(homeViewController, animated: true)
EDIT --------------------------------------------------------------------
As pointed out by #Michcio this here: window?.rootViewController = UINavigationController(rootViewController: myController) works half way and as far as I understand it, I'm embedding myController into an UINavigationController which adds the Navigation Bar to the current and following controllers.
But that's not what I need!
What I need is a clean and simple one for the onboarding i.e. MyViewController and the HomeViewController should be one with a Tab- and Navigation Bar
Basically starting from scratch after onboarding.
I used to solve this in the previous version editing the AppDelegate first Method like this (in this example I used Storyboards):
extension AppDelegate {
func showOnboarding() {
if let window = UIApplication.shared.keyWindow, let onboardingViewController = UIStoryboard(name: "Onboarding", bundle: nil).instantiateInitialViewController() as? OnboardingViewController {
onboardingViewController.delegate = self
window.rootViewController = onboardingViewController
}
}
func hideOnboarding() {
if let window = UIApplication.shared.keyWindow, let mainViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() {
mainViewController.view.frame = window.bounds
UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
window.rootViewController = mainViewController
}, completion: nil)
}
}
}
and in the Delegate itself like this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let isFirstRun = true // logic to determine goes here
if isFirstRun {
showOnboarding()
}
return true
}
but I'm seriously not getting the new SceneDelegate or simply don't understand it
Really would appreciate if someone could past some code here for re-use.
It didn't work, because you are set MyViewController as window.rootViewController. Just change line in SceneDelegate to:
window?.rootViewController = UINavigationController(rootViewController: myController)