When I set the Viewcontroller is the root navigation controller, I recieve the found nil error - swift

When I set the ViewController is the root navigation controller in the file Appdelegate.swift, like this:
var viewcontroller=ViewController();
var rootnavigationcontroller=UINavigationController.init(rootViewController: viewcontroller);
self.window?.rootViewController=rootnavigationcontroller;
Then I configure the ViewController like this:
class ViewController: UIViewController,CLLocationManagerDelegate,MKMapViewDelegate{
#IBOutlet weak var MapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
loadLaunchScreen();
Initilize();//Here configure the MapView parameters;
}
Then in the function Initilize(); I receive the error that means the MapView has found nil.
But if I do not set this Viewcontroller as the rootnavigationcontroller in the Appdelegate.swift, I will run well.
I want to ask why and how to solve it?

if you are using storyboards then try this
let stroyboard = UIStoryboard.init(name: "storyboardname", bundle: nil)//nil if its not out of your project
let ViewController = storyboard.instantiateViewController(withIdentifier: "yourstoryboadid")
var rootnavigationcontroller=UINavigationController.init(rootViewController: viewcontroller);
self.window?.rootViewController=rootnavigationcontroller;
above problem can happen when you use storybords and you are instantiate the viewController not from storyboard thats when your outlet found nil because is is not bounded to the outlet
if you are not using storyboards then your code is perfet

If you are not adding rootnavigationcontroller then your ViewController code will not be executed. So you will not get any error. For successfully MapView implementation, bind your MapView #IBOutlet in storyboard or XIB.

Related

All ViewController Views are Nil in Unit Testing?

Sorry if this is a beginner question but I'm relatively new to Unit testing and didn't see this asked anywhere.
When I start my unit tests in Swift, I setUp my tests by instantiating my viewController.
My code is set up using MVVM (Model - View - ViewModel). So when I test some of my viewModel methods, they will update the Views (in the ViewController) in the UI. The problem is, Xcode keeps crashing and says that the views in the ViewController are nil? How do I prevent these views from being nil? Am I doing something wrong? How do I instantiate the views within the viewController? I thought this would be automatic.
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.tattooModel = ARModel(imageName: "blank", tattooType: .new)
self.tattooViewModel = ARViewModel(tattooModel: tattooModel!, delegate: viewController!)
self.mainUIModel = MainUIModel()
self.mainUIViewModel = MainUIViewModel(model: mainUIModel!, delegate: viewController!)
}
Turns out I was missing the self.viewController?.loadView() call in the setUp() method.
The correct code that works without nil views is as follows:
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.viewController?.loadView()

how to segue to storyboard viewcontroller from xib view with swift 3

I'm having the hardest time finding an answer for this.
I have a xib view that is within a scrollview that is within a view controller. In the xib I have a button with an action and I need to segue to a view controller I have in my storyboard. I also would like to be able to use a custom segue.
So far, I have read that I can instantiate the viewcontroller from the storyboard to segue to it. But then I don't know how to present that controller.
thanks for any help...
UPDATE:
this is the code I'm using to perform the segue.
In parent ViewController:
static var referenceVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
LevelSelectViewController.referenceVC = self
setupScrollView()
}
code in xib view file
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sightWordController")
let parent = LevelSelectViewController.referenceVC!
let segue = InFromRightCustomSegue(identifier: "test", source: parent, destination: vc)
segue.perform()
As noted in the comments, Segues are typically confined to storyboard usage as noted in the documentation. You can implement a custom xib view in a storyboard via #IBDesignable like approaches and have you're view load from the xib into the storyboard file/class. This way, you gain the benefits of both worlds. Otherwise, you may want to approach this in another fashion (such as delegates/target-action events, etc).
You may also climb the responder chain and call a segue related to the VC loaded from the storyboard (the segue doesn't necessarily have to be attached to any particular action) via getting a reference to the VC and calling the segue. You can climb the responder chain in a manner such as the example code below:
protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
protocol ViewControllerTraversable {
func viewController<T: UIViewController>() -> T?
}
extension UIView: ViewControllerTraversable {
func viewController<T: UIViewController>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UITableViewCell: ChildViewControllerContainer {
weak var parentViewController: UIViewController? {
return viewController() as UIViewController?
}
}

How to subclass custom UIViewController in Swift?

I'd like to create a reusable view controller UsersViewControllerBase.
UsersViewControllerBase extends UIViewController, and implements two delegates (UITableViewDelegate, UITableViewDataSource), and has two views (UITableView, UISegmentedControl)
The goal is to inherit the implementation of the UsersViewControllerBase and customise the segmented items of segmented control in UsersViewController class.
class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var tableView: UITableView!
//implementation of delegates
}
class UsersViewController: UsersViewControllerBase {
}
The UsersViewControllerBase is present in the storyboard and all outlets are connected, the identifier is specified.
The question is how can I init the UsersViewController to inherit all the views and functionality of UsersViewControllerBase
When I create the instance of UsersViewControllerBase everything works
let usersViewControllerBase = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewControllerBase") as? UsersViewControllerBase
But when I create the instance of UsersViewController I get nil outlets
(I created a simple UIViewController and assigned the UsersViewController class to it in the storyboard )
let usersViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewController") as? UsersViewController
It looks like views are not inherited.
I would expect init method in UsersViewControllerBase that gets controller with views and outlets from storyboard:
class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var tableView: UITableView!
init(){
let usersViewControllerBase = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewControllerBase") as? UsersViewControllerBase
self = usersViewControllerBase //but that doesn't compile
}
}
And I would init UsersViewController:
let usersViewController = UsersViewController()
But unfortunately that doesn't work
When you instantiate a view controller via instantiateViewControllerWithIdentifier, the process is essentially as follows:
it finds a scene with that identifier;
it determines the base class for that scene; and
it returns an instance of that class.
And then, when you first access the view, it will:
create the view hierarchy as outlined in that storyboard scene; and
hook up the outlets.
(The process is actually more complicated than that, but I'm trying to reduce it to the key elements in this workflow.)
The implication of this workflow is that the outlets and the base class are determined by the unique storyboard identifier you pass to instantiateViewControllerWithIdentifier. So for every subclass of your base class, you need a separate storyboard scene and have hooked up the outlets to that particular subclass.
There is an approach that will accomplish what you've requested, though. Rather than using storyboard scene for the view controller, you can instead have the view controller implement loadView (not to be confused with viewDidLoad) and have it programmatically create the view hierarchy needed by the view controller class. Apple used to have a nice introduction to this process in their View Controller Programming Guide for iOS, but have since retired that discussion, but it can still be found in their legacy documentation.
Having said that, I personally would not be compelled to go back to the old world of programmatically created views unless there was a very compelling case for that. I might be more inclined to abandon the view controller subclass approach, and adopt something like a single class (which means I'm back in the world of storyboards) and then pass it some identifier that dictates the behavior I want from that particular instance of that scene. If you want to keep some OO elegance about this, you might instantiate custom classes for the data source and delegate based upon some property that you set in this view controller class.
I'd be more inclined to go down this road if you needed truly dynamic view controller behavior, rather than programmatically created view hierarchies. Or, even simpler, go ahead and adopt your original view controller subclassing approach and just accept that you'll need separate scenes in the storyboard for each subclass.
So, you have your base class:
class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var tableView: UITableView!
//implementation of delegates
}
[A] And your subclass:
class UsersViewController: UsersViewControllerBase { var text = "Hello!" }
[B] A protocol that your subclass will be extending:
protocol SomeProtocol {
var text: String? { get set }
}
[C] And some class to handle your data. For example, a singleton class:
class MyDataManager {
static let shared = MyDataManager()
var text: String?
private init() {}
func cleanup() {
text = nil
}
}
[D] And your subclass:
class UsersViewController: UsersViewControllerBase {
deinit {
// Revert
object_setClass(self, UsersViewControllerBase.self)
MyDataManager.shared.cleanup()
}
}
extension UsersViewController: SomeProtocol {
var text: String? {
get {
return MyDataManager.shared.text
}
set {
MyDataManager.shared.text = newValue
}
}
}
To properly use the subclass, you need to do (something like) this:
class TestViewController: UIViewController {
...
func doSomething() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
//Instantiate as base
let usersViewController = storyboard.instantiateViewControllerWithIdentifier("UsersViewControllerBase") as! UsersViewControllerBase
//Replace the class with the desired subclass
object_setClass(usersViewController, UsersViewController.self)
//But you also need to access the property 'text', so:
let subclassObject = usersViewController as! UsersViewController
subclassObject.text = "Hello! World."
//Use UsersViewController object as desired. For example:
navigationController?.pushViewController(subclassObject, animated: true)
}
}
EDIT:
As pointed out by #VyachaslavGerchicov, the original answer doesn't work all the time so the section marked as [A] was crossed out. As explained by an answer here:
object_setClass in Swift
... setClass cannot add instance variables to an object that has already been created.
[B], [C], and [D] were added as a work around. Another option to [C] is to make it a private inner class of UsersViewController so that only it has access to that singleton.
The problem is that you created a scene in the storyboard, but you didn't give the view controller's view any subviews or connect any outlets, so the interface is blank.
If your goal is to reuse a collection of views and subviews in connection with instances of several different view controller classes, the simplest way, if you don't want to create them in code, is to put them in a .xib file and load it in code after the view controller's own view-loading process (e.g. in viewDidLoad).
But if the goal is merely to "customise the segmented items of segmented control" in different instances of this view controller, the simplest approach is to have one view controller class and one corresponding interface design, and perform the customization in code. However, you could load just that segmented control from its own .xib in each case, if it's important to you design it visually.

Storyboard UIView Objects Not Instantiating

I am working on a project with Swift and Storyboards. It's a conversion project from a traditional IB and Objective-C project. I am having an issue with a UITableView instantiating when the view is loaded. Let me explain.
The project is a navigation project. Here is an overview of the Storyboard.
The Storyboard's first viewController is HomeViewController and is a landing page that displays general info. The next VC is called FeedViewController shows a number of RSS feeds. You can see an expanded screen shot of the NavigationController, HomeViewController and FeedViewController in the picture below.
My problem is that I can't get the tableView to Instantiate. I first checked to make sure that my tableView was connected as an outlet and that the dataSource and delegate properties were connected. You can see this in the pic below.
In my FeedViewController class I have an Outler property called feedsTableView. You can see the declaration in the code below.
class FeedViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, FLODataHandlerDelegate
{
// View Contoller and Protocol Properties
var floView : FLOViewController?
var dataHandler : FLODataHandler?
// Interface and Content Properties
var refreshControl : UIRefreshControl?
// IBOutlets
#IBOutlet weak var feedsTableView: UITableView!
#IBOutlet weak var backgroundImage: UIImageView!
In the HomeViewController I have a FeedViewController property that I intend to use to gain access to FeedViewController's feedsTableView.
class HomeViewController: UIViewController, FLODataHandlerDelegate, MFMailComposeViewControllerDelegate
{
// View Contoller and Protocol Properties
var feedViewController : FeedViewController?
var dataHandler : FLODataHandler?
When HomeViewController's viewDidLoad() method is called I start the dataHandler - which instantiates the FeedViewController - and set it to my FeedViewController property.
override func viewDidLoad()
{
super.viewDidLoad()
// Set up the gesture recognizer to allow for swiping to the feed VC.
let recognizer = UISwipeGestureRecognizer(target: self, action: Selector("goToNext"))
recognizer.direction = UISwipeGestureRecognizerDirection.Left
self.view.addGestureRecognizer(recognizer)
// Start the data handler
self.setUpDataHandler()
}
setUpDataHandler()
func setUpDataHandler()
{
// Intitalize FeedVC for use later in the VC
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("FeedViewController") as! FeedViewController
self.feedViewController = vc
}
I also have a fail safe that if someone were to go to the FeedViewController before the setUpDataHandler() method is called then I instantiate FeedViewController here as well.
func goToNext()
{
// Grab the feedViewController so it can be pushed onto the stack. Make sure you set up the storyboard identifier.
let feedVC = self.storyboard!.instantiateViewControllerWithIdentifier("FeedViewController") as! FeedViewController
self.feedViewController = feedVC
self.navigationController!.pushViewController(self.feedViewController!, animated: true)
}
However the feedsTableView is not getting instantiated. In the viewDidLoad() method of FeedViewController I attempt to add the feedsTableView to a UIRefreshController.
override func viewDidLoad()
{
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
self.refreshControl!.addTarget(self, action: "refreshInvoked:state:", forControlEvents: UIControlEvents.ValueChanged)
// See the note in viewDidLoad in FLOViewController.
self.feedsTableView.addSubview(self.refreshControl!)
}
When the app runs I get the following error.
fatal error: unexpectedly found nil while unwrapping an Optional value
The image below shows were this is called. It's the viewDidLoad() of the FeedViewController. As you can see in the picture I even tried instantiating the feedsTableView before adding it to the UIRefreshController and I still get the error.
Any help would be greatly appreciated.
Take care,
Jon
The reason why it doesn't work in the very last case, where you manually instantiate UITableView and assign that to self.feedsTableView, is that self.feedsTableView is declared weak. Thus, the table view comes into existence, is assigned, and vanishes in a puff of smoke because it has no memory management. By the time you get to the last line, self.feedsTableView is nil once again.
Thus, the solution for that last case is to remove the weak designation from your feedsTableView declaration.
That will get you past the crash in that last case. But of course you won't see anything because you are not also inserting the table view into your interface.

SWIFT: cannon insert value to NSTextView From ViewController

I created file ViewController.swift
Here is the content of it
import Cocoa
public class ViewController: NSObject {
#IBOutlet var textView: NSTextView!
func InsertText(string:String) {
textView.insertText(string)
}
}
Then I bind textView to my NSTextView in my xib
and
now I want to insert text from AppDelegate via Controller
I do
let controller = ViewController()
controller.InsertText("Hello")
and it throw an error
fatal error: unexpectedly found nil while unwrapping an Optional value
which means that textView in nil, but why it is nil and how do I insert text using another class. if I do the same just in AppDelegate it works.
Instead of let controller = ViewController() ctrl-drag the view into your AppDelegate, name it reasonable and use that.