Framework ViewController View Not showing - ios

I am trying to create a framework (Login VC) which contains a view controller. I have successfully imported the framework and presented the VC, but the view is not showing. I have a print function in the imported viewDidLoad and it is printing. What am I missing?
Framework VC:
public class LoginVC: UIViewController {
#IBOutlet weak var button: UIButton! {
didSet {
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
}
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func viewDidLoad() {
super.viewDidLoad()
print("View Loaded") // Firing
}
#objc func buttonPressed() {
print("hello")
}
}
Framework VC Xib:
This is view debugger when I present the framework VC
-- Update: This is how I am showing the VC ---
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let homeViewController = LoginVC()
homeViewController.view.backgroundColor = UIColor.white
window!.rootViewController = homeViewController
window!.makeKeyAndVisible()
return true
}
-- Update --
Since many comments relate to the app delegate, I first present a general ViewController which then will present my login framework VC.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let homeViewController = ViewController()
homeViewController.view.backgroundColor = UIColor.white
window!.rootViewController = homeViewController
window!.makeKeyAndVisible()
return true
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton! {
didSet {
button.addTarget(self, action: #selector(presentNext), for: .touchUpInside)
}
}
#objc func presentNext() {
let loginVC = LoginVC()
present(loginVC, animated: true, completion: nil)
}
}
Now, when I present the login framework, all I get is a black screen.
-- Update --
I can change the background color of the view in viewdidLoad, but the xib views are not shown. Why is this?..

Frameworks with xibs connected to viewController require explicit loading from the bundle.
Usually when we create a cocoaTouch UIViewController with xib, this is handled for us. However, when using frameworks, it is not handled.
To solve this, I add load the xib in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// Frameworks require loading of the xib to connect xib to view controller
let bundle = Bundle(for: type(of: self))
bundle.loadNibNamed("viewControllerName", owner: self, options: nil)
}

You need to call your presentNext() after the ViewController has actually appeared -- not in viewDidLoad, and not even in viewWillAppear.
Like this:
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentNext()
}
#objc func presentNext() {
let loginVC = LoginVC()
present(loginVC, animated: true, completion: nil)
}
}
Here's the working test project:
https://github.com/drewster99/SO_LoginVC
Also, maybe double-check that your ViewController.xib has the IBOutlet actually attached for the button. It's actually got to be that. Everything else looks good.
Here's what I've got:
AppDelegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let homeViewController = ViewController()
homeViewController.view.backgroundColor = UIColor.white
window!.rootViewController = homeViewController
window!.makeKeyAndVisible()
return true
}
}
ViewController.swift:
import UIKit
import LoginFrameworkThing
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton! {
didSet {
button.addTarget(self, action: #selector(presentNext), for: .touchUpInside)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
#objc func presentNext() {
print("Presenting next...")
let loginVC = LoginVC()
present(loginVC, animated: true, completion: nil)
}
}
The button is connected in ViewController, and LoginVC (both .xib and .swift) exist in the framework.
I updated the sample project. Check the link.

Related

Changing Root view controllers gives IBOutlet as nil

I have to change my root view controller according to login status.
I had change my AppDelegate file and create a MainNavigationController class which extends UINavigationController.
**PROBLEM :- **
When the defined root class is loaded it gives all outlet variables as nil.
CODE
class PostalViewController: UIViewController {
#IBOutlet weak var btn_currentLocation: UIButton!
#IBOutlet weak var btn_viewAccount: UIButton!
#IBOutlet weak var in_postCode: UITextField!
let locManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Here it gives a nil on btn_currentLocation
btn_currentLocation.layer.cornerRadius = 15.0
btn_currentLocation.layer.masksToBounds = true
btn_currentLocation.setButtonGradient(colorOne: UIColor(named: "lightBlue")!, colorTwo: UIColor(named: "gradient2")!)
btn_viewAccount.layer.cornerRadius = 15.0
btn_viewAccount.layer.masksToBounds = true
// Add search button in post code text field
let searchButton = UIButton(type: .custom)
searchButton.setImage(UIImage(named: "iconSearch"), for: .normal)
searchButton.frame = CGRect(x: CGFloat(in_postCode.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
searchButton.addTarget(self, action: #selector(self.actionSearch), for: .touchUpInside)
in_postCode.rightView = searchButton
in_postCode.rightViewMode = .always
}
AppDelegate.swift (only that particular function)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
return true
}
MainNavigationController.swift
class MainNavigationController : UINavigationController{
var isLogin : Bool?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
isLogin = UserDefaults.standard.bool(forKey: "isLogin")
print("IsLogin --->", isLogin!)
if (isLogin != nil && isLogin!){
perform(#selector(postalController), with: nil, afterDelay: 0.01)
}else{
perform(#selector(showHomeContoller), with: nil, afterDelay: 0.01)
}
}
#objc func showHomeContoller () {
let homepageController = HomePageViewController()
present(homepageController, animated: true, completion: nil)
}
#objc func postalController () {
let postalController = PostalViewController()
viewControllers = [postalController]
}
}
EDIT
I have add a screenshot below, I have to switch my root view between HomePage and Postal
I suppose you just want to show PostalViewController if user is logged. If user isn't logged you want to show just HomePageViewController. First delete these lines from app delegate, you don't need this:
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationController()
Now delete the whole navigation controller class. You won't need it because you will do all of this in HomePageViewController. Now in HomePageViewController swift file add this to viewDidLoad()
isLogin = UserDefaults.standard.bool(forKey: "isLogin")
print("IsLogin --->", isLogin!)
if isLogin != nil {
performSegue(withIdentifier: "segueToPostal", sender: self)
}
In the end set segue from HomePageViewController to PostalViewController
and set its identifier as segueToPostal
If you don't want to let user navigate back from Postal view controller you can just embbed PostalViewController in new NavigationController. Then just set segue from HomePageViewController to this NavigationController and set identifier.
IBOulet is set when view controller is initialized with nib file.
So you need create the view controllers using the init method below
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
For example, if name of your xib file where you config your IBOultets the same as controllers name, you can just do next:
let postalController = PostalViewController(nibName: "PostalViewController", bundle: nil)
Here You will have controller with initialized IBOutlets.
For using story board you should init them from story board.
Try to ini controllers next way:
let sb = UIStoryboard(name: "MainStoryboard", bundle: nil)
let vc = sb.instantiateViewController(withIdentifier: "PostalViewController")
And you should set storyboard id in your story board for the controller, like on the screen below.
storyboard id
Also you can find more info how to init correctly view controllers from storyboard here
What is a StoryBoard ID and how can i use this?

Loading one instance of Google Admob throughout multiple ViewControllers

I am looking to load one instance of a Google Admob Banner View throughout multiple View Controllers (including, but not limited to a UITabBarController.)
My attempt is below. I'm using AppDelegate to set the adSize, adUnitID and testDevices. Then in each VC where I want a banner displayed, I set the rootViewController, frame, load request, and then addSubView.
This works, in the fact that the ads show up fine. However, the ads keep changing when I segue or dismiss VC! It appears that a new request is happening everytime VC's change. Which is precisely the result that must be avoided!
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var adBannerViewFromAppDelegate = GADBannerView()
let loadRequest = GADRequest()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
adBannerViewFromAppDelegate.adSize = kGADAdSizeSmartBannerPortrait
adBannerViewFromAppDelegate.adUnitID = "12345"
loadRequest.testDevices = [kGADSimulatorID, myiPhone]
}
}
ViewController
(This has a button to SecondViewController via Push Segue)
import UIKit
import GoogleMobileAds
class ViewController: UIViewController {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addBannerToView()
}
func addBannerToView() {
appDelegate.adBannerViewFromAppDelegate.rootViewController = self
appDelegate.adBannerViewFromAppDelegate.load(appDelegate.loadRequest)
appDelegate.adBannerViewFromAppDelegate.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: appDelegate.adBannerViewFromAppDelegate.frame.height)
view.addSubview(appDelegate.adBannerViewFromAppDelegate)
}
}
SecondViewController
import UIKit
import GoogleMobileAds
class SecondViewController: UIViewController {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addBannerToView()
}
#IBAction func closeButtonPressed(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
func addBannerToView() {
appDelegate.adBannerViewFromAppDelegate.rootViewController = self
appDelegate.adBannerViewFromAppDelegate.load(appDelegate.loadRequest)
appDelegate.adBannerViewFromAppDelegate.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: appDelegate.adBannerViewFromAppDelegate.frame.height)
view.addSubview(appDelegate.adBannerViewFromAppDelegate)
}
}
How can I get one instance of the Banner created in the AppDelegate to display on multiple ViewControllers? Thanks.

iOS10: Hide status bar when using a UITabBarController()

I have a UITabBarController() that I use and assign in AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
showTabBar()
return true
}
func showTabBar() {
let tabBarVC = TabBarVC()
if let window = self.window {
window.rootViewController = tabBarVC
}
}
I have the following key is in info.plist:
In my Target under General, I have the following setting:
I use the following code in one of my tabs to hide the Status Bar:
class ViewController: UIViewController {
var statusBarShouldBeHidden = false
override func viewDidLoad() {
super.viewDidLoad()
}
override var prefersStatusBarHidden: Bool {
return statusBarShouldBeHidden
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
#IBAction func buttonHideShowStatusBarTapped(_ sender: UIButton) {
statusBarShouldBeHidden = !statusBarShouldBeHidden
UIView.animate(withDuration: 0.25) {
self.setNeedsStatusBarAppearanceUpdate()
print("animating")
}
}
}
When the button is tapped, "animating" prints in the log; however, the status bar does not hide.
I am not sure if this is related to UITabBarController(), but the code above seems to work fine in a project without it.
How can I hide the status bar in iOS10 when using UITabBarController()?
You have taken TabBarVC as UIViewController subclass rather than UITabBarController subclass and then initialised and added the UITabBarController instance to it's view, I think TabBarVC should be subclass of UITabBarController and should be the rootViewController of the window. If you change the TabBarVC to subclass of UITabbarViewController status bar is working fine. Check the code below
class TabBarVC: UITabBarController, UITabBarControllerDelegate, UINavigationControllerDelegate {
//var mainTabBarController = UITabBarController() //not needed
init() {
super.init(nibName: nil, bundle: nil)
self.delegate = self
self.navigationController?.delegate = self
self.selectedIndex = 0
self.customizableViewControllers = []
self.setViewControllers(self.topLevelControllers(), animated: false)
}
You need to setNeedsStatusBarAppearanceUpdate() in your root view controller, i.e. TabBarVC. Here is the solution:
Override prefersStatusBarHidden in TabBarVC to return value of selectedViewController
override var prefersStatusBarHidden: Bool {
return mainTabBarController.selectedViewController?.prefersStatusBarHidden ?? false
}
Add reference to TabBarVC in ViewController class
var tabBarVC: UIViewController?
Set tabBarVC variable on topLevelControllers() method
let one = self.viewControllerFromStoryBoard(storyboardName: "One",
sceneName: "Initial",
iconName: "",
title: "Tab One") as! ViewController
one.tabBarVC = self
Finally, on your #IBAction update your status bar
self.tabBarVC?.setNeedsStatusBarAppearanceUpdate()

Navigation Between UIViewControllers Swift 2.2

I have 3 ViewControllers and want to navigate between then without appearing the second one.
what actually i want to do something like this:
FirstController
override func viewDidLoad() {
super.viewDidLoad()
let secondController = SecondController();
secondController.showThird();
}
SecondController
override func viewDidLoad() {
super.viewDidLoad()
super.didReceiveMemoryWarning()
}
func showThird() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("thirdId")
self.navigationController?.pushViewController(controller, animated: true)
}
ThirdController
override func viewDidLoad() {
super.viewDidLoad()
}
appdelegate.swift
You can push the second Viewcontroller without animation and next line you need to push Third Viewcontroller
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let controllerOne = storyboard.instantiateViewControllerWithIdentifier("oneId")
self.navigationController?.pushViewController(controllerOne, animated: false)
let controllerTwo = storyboard.instantiateViewControllerWithIdentifier("secondId")
self.navigationController?.pushViewController(controllerTwo, animated: false)
let controllerThree = storyboard.instantiateViewControllerWithIdentifier("thirdId")
self.navigationController?.pushViewController(controllerThree, animated: true)
}
Hopefully It works for you.

Swift – Instantiating a navigation controller without storyboards in App Delegate

I'm rebuilding an app without storyboards and the part of it that I'm having the most trouble with is navigating view-to-view programatically. Few things are written out there which don't use storyboards, so finding an answer for this has been tough.
My problem is pretty simple. I have my ViewController and my SecondViewController and I want to push from the former to the latter.
In AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = UIColor.whiteColor()
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}
Then in ViewController.swift:
class ViewController: UIViewController, AVAudioPlayerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
startFinishButton.setTitle("Begin", forState: .Normal)
startFinishButton.addTarget(self, action: "moveToSecondViewController", forControlEvents: .TouchUpInside)
view.addSubview <*> startFinishButton
}
func moveToSecondViewController(sender: UIButton) {
let vc = SecondViewController()
println(self.navigationController) // returns nil
self.navigationController?.pushViewController(vc, animated: true)
}
}
Printing self.navigationController returns nil. I've tried doing:
var navController = UINavigationController() when the ViewController class is created (but outside of ViewDidLoad, right under the class declaration) and done the push using the navController var but that hasn't worked.
I'm thinking maybe the solution is to create a navigation controller in App Delegate that the whole app would use, I guess as a global variable?
My hope is that this post can serve many others who are new to Swift and want to remove storyboards from their app.
Thanks for taking a look and for your help.
In Swift 3
Place this code inside didFinishLaunchingWithOptions method in AppDelegate class.
window = UIWindow(frame: UIScreen.main.bounds)
let mainController = MainViewController() as UIViewController
let navigationController = UINavigationController(rootViewController: mainController)
navigationController.navigationBar.isTranslucent = false
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
In AppDelegate
var window: UIWindow?
var navController: UINavigationController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
navController = UINavigationController()
var viewController: ViewController = ViewController()
self.navController!.pushViewController(viewController, animated: false)
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.rootViewController = navController
self.window!.backgroundColor = UIColor.whiteColor()
self.window!.makeKeyAndVisible()
return true
}
In ViewController
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "FirstVC"
var startFinishButton = UIButton.buttonWithType(UIButtonType.System) as! UIButton
startFinishButton.frame = CGRectMake(100, 100, 100, 50)
startFinishButton.backgroundColor = UIColor.greenColor()
startFinishButton.setTitle("Test Button", forState: UIControlState.Normal)
startFinishButton.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(startFinishButton)
}
func buttonAction(sender:UIButton!)
{
println("Button tapped")
let vc = SecondViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In Swift 5 and Xcode 13 there is a SceneDelegate along with the AppDelegate. So now to completely remove the storyboard from a project and embed the view controller in a navigation controller do the following:
Delete the actual storyboard in the Project Navigator
Select the project in the Project Navigator, select Target and then the General tab, then delete the storyboard from Main Interface
In the info.plist file delete the storyboard from:
Application Scene Manifest > Scene Configuration > Application Session Role > Item 0 (Default Configuration) > Storyboard Name
Then in the scene delegate change the scene function to look like this:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController(rootViewController: YourViewController())
window.rootViewController = navigationController
self.window = window
window.makeKeyAndVisible()
}

Resources