SWIFT: cannon insert value to NSTextView From ViewController - swift

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.

Related

Setting .attributedText on properties (accessible via outlets inside NIB-loaded UIView) from external reference appears to fail

A nib has been loaded manually via UINib(nibName, bundle).instantiate().
The outlets connecting the nib to a UIView subclass are being successfully initialized and are accessible.
Two of these outlets represent a UILabel and a UITextView - which are being used to present attributed text strings.
Changes to the attributed strings are being performed on mutable copies before replacement via the .attribtedText setter method.
Everything works as expected whenever functions intended to update the attributed text are called either directly in the UIView subclass or the View Controller that loads the nib.
However, when the same function is called via a reference kept inside some other class object elsewhere in the codebase, the updates don't happen.
The Nib's UIView subclass:
class MyView: UIView {
#IBOutlet weak var aLabel: UILabel!
#IBOutlet weak var someText: UITextView!
...
public func applySomeStyle() {
guard
let aLabelMAS = aLabel.attributedText?.mutableCopy() as? NSMutableAttributedString,
var someTextMAS = someText.attributedText.mutableCopy() as? NSMutableAttributedString
else {
return
}
let labelRange = NSRange(location: 0, length: aLabelMAS.length)
aLabelMAS.addAttribute(.backgroundColor, value: UIColor.yellow, range: labelRange)
let someTextRange = NSRange(location: 0, length: someTextMAS.length)
someTextMAS.removeAttribute(.backgroundColor, range: someTextRange)
aLabel.attributedText = aLabelMAS
someText.attributedText = someTextMAS
}
public func doStuff() {
...
applySomeStyle() // No problems -- the attributed strings inside the UILabel and UITextView are updated as intended.
...
}
}
From inside the ViewController that loads the Nib, calls to the applySomeStyle function via the reference to the MyView object work fine.
class MyViewController: UIViewController {
weak var myView: MyView!
override func viewDidLoad() {
super.viewDidLoad()
...
myView = UINib(nibName: "MyView", bundle: Bundle.main).instantiate(
withOwner: self, options: nil).first as? MyView
self.view.addSubview(myView)
...
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myView.applySomeStyle() // Again, no problems.
}
}
But if the nib class object is referenced elsewhere -- the changes don't occur.
class SomeOtherViewController: UIViewController
var myVC: MyViewController!
...
func foo() {
myVC.myView.applySomeStyle() // Fails to update the UILabel/UITextView
}
...
}
Executing the attributedText setter, i.e.
someText.attributedText = someNewAttributedString
triggers viewDidLayoutSubviews().
Inside viewDidLayoutSubviews(), I was calling a function (e.g. a 'load content' function) that was also responsible for setting some string attributes. I was using a boolean flag to ensure that subsequent calls to viewDidLayoutSubviews weren't going to trigger that function again.
In this case, that boolean condition was not working as it was intended to, and so other functions that set the attributedText property were leading it to be triggered again, overwriting the changes made by other functions.
I thought I was dealing with an obscure bug.
I thought maybe it was a thread issue.
I thought maybe it was a broken reference of some kind.
I thought wrong.
But I hope that this proves useful to someone out there.

Swift PushViewController From A Different Storyboard [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
iOS - Push viewController from code and storyboard
(5 answers)
Closed 4 years ago.
I'm trying to show one of my ViewControllers of a different storyboard programmatically. But for some reasons the properties of the ViewController I'm trying to show are nil.
That's my error: Unexpectedly found nil while unwrapping an Optional value
I'm getting the error in my SecondViewController. It is showing but
my app crashes due to the properties being nil.
That's my code:
let secondStoryBoard = UIStoryboard(name: "SecondStoryBoard", bundle: nil)
let secondViewController = secondStoryBoard.instantiateViewController(withIdentifier: "secondVC") as! SecondVC
self.navigationController?.pushViewController(secondViewController, animated: true)
That's the code producing the error:
class SecondVC: ViewController
{
#IBOutlet weak var mapView: MKMapView!
mapView.isHidden = true // ERROR!! Unexpectedly found nil while unwrapping an Optional value
}
SOLVED: just had to reconnect my #IBOutlet connections
This means that the following line is either nil or not of type SecondVC.
secondStoryBoard.instantiateViewController(withIdentifier: "secondVC")
I would verify in your storyboard that you have an identifier secondVC and that view controller is of type SecondVC.
Edit:
Based on the new code the OP posted in the question, changing the SecondVC to the following should work.
class SecondVC: ViewController
{
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
mapView.isHidden = true
}
}
Basically this will wait until the view has loaded before accessing the mapView. For the original code, you were trying to access mapView before it has actually been loaded.
Edit 2:
It looks like mapView outlet is not properly linked to the storyboard.

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

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.

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.

Sending data to another view: can't unwrap option

I know that this has to be a simple fix, but can't seem to understand why my code is not working. Basically I am trying to send a value from a text field in 1 view to a 2nd view's label.
ViewController.swift
#IBOutlet var Text1st: UITextField
#IBAction func Goto2ndView(sender: AnyObject) {
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2
//view2.Label2nd.text=text;
self.navigationController.pushViewController(view2, animated: true)
}
MyView2.swift
#IBOutlet var Label2nd: UILabel
override func viewDidLoad() {
super.viewDidLoad()
var VC = ViewController()
var string = (VC.Text1st.text) //it doesn't like this, I get a 'Can't unwrap Option.. error'
println(string)
}
-------EDITED UPDATED CODE FROM (drewag)-------
ViewController.swift
let text = "text"
var sendString = Text1st.text
println(sendString) //successfully print it out.
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2
view2.Label2nd.text=sendString;
self.navigationController.pushViewController(view2, animated: true)
MyView2.swift
#IBOutlet var Label2nd: UILabel
override func viewDidLoad() {
super.viewDidLoad()
var VC = ViewController()
var string = self.Label2nd.text
println(string) //still getting the error of an unwrap optional.none
}
var VC = ViewController() creates a new instance of ViewController. Unless there is a default value, you are not going to get any value out of VC.Text1st.text. You really should use a string variable on your second view controller to pass the data to it.
Also, a note on common formatting:
Class names should start with a capital letter (as you have)
Method / function names should start with a lower case letter
UIViewController subclasses should have "Controller" included in their name, otherwise, it looks like it is a subclass of UIView which is an entirely different level of Model View Controller (the architecture of all UIKit and Cocoa frameworks)
Edit:
Here is some example code:
class ViewController1 : UIViewController {
...
func goToSecondView() {
var viewController = ViewController2()
viewController.myString = "Some String"
self.navigationController.pushViewController(viewController, animated: true)
}
}
class ViewController2 : UIViewController {
var myString : String?
func methodToUseMyString() {
if let string = self.myString {
println(string)
}
}
...
}
Note, I am not creating ViewController2 using a storyboard. I personally prefer avoiding storyboards because they don't scale well and I find editing them to be very cumbersome. You can of course change it to create the view controller out of the storyboard if you prefer.
jatoben is correct that you want to use optional binding. IBOutlets are automatically optionals so you should check the textfield to see if it is nil.
if let textField = VC.Text1st {
println(textField.text)
}
This should prevent your app from crashing, but it will not print out anything because your text field has not yet been initialized.
Edit:
If you want to have a reference to your initial ViewController inside your second you're going to have to change a few things. First add a property on your second viewcontroller that will be for the first view controller:
#IBOutlet var Label2nd: UILabel //existing code
var firstVC: ViewController? //new
Then after you create view2, set it's firstVC as the ViewController you are currently in:
let view2 = self.storyboard.instantiateViewControllerWithIdentifier("view2") as MyView2 //already in your code
view2.firstVC = self //new
Finally in your viewDidLoad in your second view controller, use firstVC instead of the ViewController you recreated. It will look something like this:
override func viewDidLoad() {
super.viewDidLoad()
if let textField = firstVC?.Text2nd {
println(textField.text)
}
}
Use optional binding to unwrap the property:
if let string = VC.Text1st.text {
println(string)
}