UILabel provides nil value - swift

I have two view controllers. 1st view controller fetches the user's location and has a delegate method. 2nd view controller conforms to the delegate method of 1st VC. I have a UILabel in 2nd VC which displays the location received from delegate method.
#IBOutlet var locationLabel: UILabel!
func locationOfTheUser(location: String) {
locationLabel.text=location
}
The locationLabel is connected in IB as an reference outlet.
But when i run the program it gives me an error saying that unexpectedly found nil value while unwrapping the optional.

You should declare your label like this:
#IBOutlet var weak locationLabel: UILabel!
The mention "#IBOutlet" makes Interface Builder recognize the outlet.
"Weak" is used because in most situations the owner of the outlet isn't the same as the owner of the view. For example, a view controller doesn't own someLabel - the view controller's view does.
For example, if there is a code path that removes an outlet from its superview, or the outlet is (intentionally) not hooked up in the storyboard, it needs to be an optional because the outlet is not guaranteed to be there when it's accessed.
#IBOutlet var someLabel: UILabel?
If there is no code path that re-adds the outlet to the view hierarchy, it would also be good to make it weak to not hold on to it unnecessarily when it gets removed:
#IBOutlet weak var someLabel: UILabel?
This ensures that if the label is removed from the superview, it's not being kept in memory by the strong reference in the view controller. In the most common case, where there is an outlet that will always be there, a strong, implicitly unwrapped optional is appropriate:
#IBOutlet var someLabel: UILabel!
The outlet isn't weak in case the code ever changes so that there is a code path that removes the view from the view hierarchy but you forget to update the optionality of the property. The object will stay in memory and using it won't crash your app.
Also, "var" is used because outlets are, by definition, set after initialization
You can after put your code:
func locationOfTheUser(location: String) {
locationLabel.text=location
}

private var locationOfUser: String?
func locationOfTheUser(location: String) {
locationOfUser = location
}
override func viewDidLoad() {
super.viewDidLoad()
locationLabel.text = locationOfUser
}
Try this. If it works, it means that the locationOfTheUser() is being called before the view of this controller has been loaded, therefore the label is not there yet.

#IBOutlet weak var heartbeats: UILabel!
guard heartbeats?.text != nil else {
print("The label is empty")
return
}
To check if the uilabel is really empty.

Related

How do I set the content of a tab view item in swift

I created a tab view in storyboard, and created an outlet in my code, and every time a user clicks a button, it creates a new tab item that has a web view in it. I have not found a way to set the content of the tab programatically. Is there some way to set the content of the tab item?
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var tabView: NSTabView!
#IBAction func addTab(_ sender: Any) {
let newTab = NSTabViewItem()
//newTab.setContent(webView)
tabView.addTabViewItem(newTab)
}
There's also another problem. When I try to create a new tab with the addTab function, I get an error :
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

Change #IBOutlet from a subview

I'm trying to enable or disable an #IBOutlet UIButton Item of a toolbar from a UIView.
The button should get disabled when the array that I'm using in EraseView.Swift is empty.
I tried creating an instance of the view controller but it gives me the error (found nil while unwrapping):
in EraseView:
class EraseView: UIView {
...
let editViewController = EditImageViewController()
//array has item
editViewController.undoEraseButton.enabled = true //here I get the error
...
}
I tried to put a global Bool that changed the value using it in EditImageViewController but it doesn't work:
var enableUndoButton = false
class EditImageViewController: UIViewController {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
viewDidLoad() {
undoEraseButton.enabled = enableUndoButton
}
}
class EraseView: UIView {
...
//array has item
enableUndoButton = true //here I get the error
...
}
I know it's simple but I can't let it work. Here's the situation:
The root of the problem is the line that says:
let editViewController = EditImageViewController()
The EditImageViewController() says "ignore what the storyboard has already instantiated for me, but rather instantiate another view controller with no outlets hooked up and use that." Clearly, that's not what you want.
You need to provide some way for the EraseView to inform the existing view controller whether there was some change to its "is empty" state. And, ideally, you want to do this in a way that keeps these two classes loosely coupled. The EraseView should only be informing the view controller of the change of the "is empty" state, and the view controller should initiate the updating of the other subviews (i.e. the button). A view really shouldn't be updating another view's outlets.
There are two ways you might do that:
Closure:
You can give the EraseView a optional closure that it will call when it toggles from "empty" and "not empty":
var emptyStateChanged: ((Bool) -> ())?
Then it can call this when the state changes. E.g., when you delete the last item in the view, the EraseView can call that closure:
emptyStateChanged?(true)
Finally, for that to actually do anything, the view controller should supply the actual closure to enable and disable the button upon the state change:
override func viewDidLoad() {
super.viewDidLoad()
eraseView.emptyStateChanged = { [unowned self] isEmpty in
self.undoEraseButton.enabled = !isEmpty
}
}
Note, I used unowned to avoid strong reference cycle.
Delegate-protocol pattern:
So you might define a protocol to do that:
protocol EraseViewDelegate : class {
func eraseViewIsEmpty(empty: Bool)
}
Then give the EraseView a delegate property:
weak var delegate: EraseViewDelegate?
Note, that's weak to avoid strong reference cycles. (And that's also why I defined the protocol to be a class protocol, so that I could make it weak here.)
The EraseView would then call this delegate when the the view's "is empty" status changes. For example, when it becomes empty, it would inform its delegate accordingly:
delegate?.eraseViewIsEmpty(true)
Then, again, for this all to work, the view controller should (a) declare that is conforms to the protocol; (b) specify itself as the delegate of the EraseView; and (c) implement the eraseViewIsEmpty method, e.g.:
class EditImageViewController: UIViewController, EraseViewDelegate {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
eraseView.delegate = self
}
func eraseViewIsEmpty(empty: Bool) {
undoEraseButton.enabled = !empty
}
}
Both of these patterns keep the two classes loosely coupled, but allow the EraseView to inform its view controller of some event. It also eliminates the need for any global.
There are other approaches that could solve this problem, too, (e.g. notifications, KVN, etc.) but hopefully this illustrates the basic idea. Views should inform their view controller of any key events, and the view controller should take care of the updating of the other views.

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.

Why am I getting a nil for a UITableViewCell's UILabel?

Why am I getting a 'nil' error/UILabel during my second pass thru the table cell listing iteration?
1) Inside cell
2) Inside cell
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb) po cell?.contentView.viewWithTag(TitleLabelTag)
nil
Here I link the elements in the code; and register the cell:
class DiaryTableViewCell: UITableViewCell {
#IBOutlet weak var TitleLabel: UILabel!
#IBOutlet weak var SubTitleLabel: UILabel!
#IBOutlet weak var leftImageView: UIImageView!
#IBOutlet weak var rightImageView: UIImageView!
}
class DiaryTableViewController: UITableViewController {
let kCellIdentifier = "DiaryCell"
var cellNib:UINib?
var diaryCell:DiaryTableViewCell?
var objects = NSMutableArray() //...global var.
override func viewDidLoad() {
self.title = "My Diary"
cellNib = UINib(nibName: "TableViewCells", bundle: nil)
tableView.registerClass(DiaryTableViewCell.self, forCellReuseIdentifier: kCellIdentifier)
}
...
Yet I'm getting the runtime error here:
Here's what I get in the console:
1) Inside cell
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb) po cell!.TitleLabel
nil
What's missing here?
It's a pretty bad idea to select a view with a tag. It's a much better idea to subclass your UITableViewCell and give it a property to access the elements.
If you are creating static cells and loading you need to create an IBoutlet for them in your .h correctly.
Moreover remove line
tableView.registerClass(...) statement from your code. Look at this link might help and is very similar except its for collectionview. -
Why is UICollectionViewCell's outlet nil?
1) I moved the cell registration to the viewDidLoad().
2) I forgot to place the '?' after the TitleLabel & SubTitleLabel; to notify the compiler that these labels could be nil.
I don't see the altered cell yet (empty rows); but I'm not getting runtime errors.
Unfortunately I merely cured the symptom; not the cause. I'm still getting nil UILabels.
...working on revision and cleaner code.

Cannot form weak reference to instance of class NSTextView

Using Swift only, here's my code in AppDelegate.swift:
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet var window: NSWindow
#IBOutlet var textField: NSTextView
#IBAction func displaySomeText(AnyObject) {
textField.insertText("A string...")
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification?) {
// Insert code here to tear down your application
}
}
In the interface builder, I have an object hooked up to receive input from a button, then the output goes to a text view. I'm trying to get the text view to populate with some text when I hit the button.
I tried this with a text field as well, and didn't get the error, but got a "dong" error sound and it didn't do anything else. In Objective-C, you had to use the (assign) parameter to get this to work from what I understand.
What am I doing wrong?
You cannot store a weak reference to an NSTextView due to historical issues with Cocoa and AppKit. See details in the Clang documentation. NSTextView is marked as NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE in NSTextView.h, there are also a few other classes to lookout.
Have you tried a Swift unowned reference instead of weak, which is kind of like Objective-C's assign (what you'd use for an NSTextView outlet in Objective-C)?
Use #IBOutlet var scrollView: NSScrollView instead of #IBOutlet var textField: NSTextView.
Then create a property returns documentView in scrollView.
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet var window: NSWindow
#IBOutlet var scrollView: NSScrollView
var textField: NSTextView {
get {
return scrollView.contentView.documentView as NSTextView
}
}
#IBAction func displaySomeText(AnyObject) {
textField.insertText("A string...")
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification?) {
// Insert code here to tear down your application
}
}
I have tried to replicate what you described. I have created a new OS X app using Xcode 6 beta 7. I have dropped a button and text view in the main form.
I think your problem is that the connection to the Text View object is not correct for some reason. To make things easier, I've connected the objects using control-drag, which adds the required code automatically. So first I've connected the Text View. To do this click on the text view object until Text View is selected. When I do this in my version of Xcode, the first time I click on the object, Bordered Scroll View is selected. Clicking on it again then selects Clip View. Finally, clicking on it again selects Text View. Now I control-drag from the object to the AppDelegate.swift code (It helps to display the Assistant Editor so that you have your form UI and code side-by-side).
By doing this I get this little window:
Notice that the type is NSTextView and the storage is Weak. I've only had to add the name and click Connect. This adds the following code in AppDelegate.swift:
#IBOutlet var textField: NSTextView!
The code is almost exactly like the one you have, except for the ! at the end of the line, which forces to unwrap the value of textField.
Just with that, the code as you have it in your question should work.
The other thing I would suggest is not to use insertText. According to Apple's documentation for NSTextView.insertText:
This method is the entry point for inserting text typed by the user
and is generally not suitable for other purposes. Programmatic
modification of the text is best done by operating on the text storage
directly.
As far as I understand this, programmatic modification of the text by operating on the text storage directly means dealing with NSText, which NSTextView inherits from. So instead, use NSText.string. This is how the click button action looks in my code:
#IBAction func displaySomeText(sender: NSButton) {
// If you want to add a new 'A string... ' every time you click the button
textField.string! += "A string... "
// otherwise just use
//textField.string = "A string..."
}
I have added the Button Action in the same way as I've added the Text View Outlet, by control-dragging, and, in this case, selecting NSButton as the sender, instead of leaving the default AnyObject.
#IBOutlet automatically makes a property weak IIRC, but weak doesn't automatically make a property optional. But it is required that a weak property be made optional, as the property could at any time be deallocated and made nil. So you have to declare your #IBOutlets as optional.
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet var window: NSWindow? // Optional
#IBOutlet var textField: NSTextView?
#IBAction func displaySomeText(AnyObject) {
textField?.insertText("A string...") // Only call the method if the object exists
}
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification?) {
// Insert code here to tear down your application
}
}
Does the "dong" error suggest a responder chain problem? What if you call becomeFirstResponder on the text field before inserting the text?
To create a weak reference user the weak keyword:
example:
#IBOutlet weak var myView: UIView
In your case
#IBOutlet weak var textField: NSTextView