11

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?

Steve
  • 592
  • 9
  • 24
  • [This question](http://stackoverflow.com/questions/24016527/strong-and-weak-references-in-swift) describes how to use a weak reference in swift. – 67cherries Jun 05 '14 at 14:22
  • Tried using weak in front of var, but no difference. – Steve Jun 05 '14 at 18:50

6 Answers6

15

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)?

Ian Bytchek
  • 8,804
  • 6
  • 46
  • 72
Joel
  • 2,285
  • 2
  • 21
  • 22
  • 1
    Whoa, that's obscure: 243 results on Google. Here's the full Swift error message, in case people Google this confused: "Cannot form weak reference to instance (0x...) of class NSTextView. It is possible that this object was over-released, or is in the process of deallocation." Can't even see the `NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE` tag in the Swift "headers"! – Archagon Sep 12 '16 at 21:11
  • 1
    Swift 3.1 appears to have lifted this restriction. – Feldur Jun 01 '17 at 15:19
  • 1
    @Feldur still happens on 10.11, [behaviour changed in 10.12](https://github.com/phracker/MacOSX-SDKs/blob/9fc3ed0ad0345950ac25c28695b0427846eea966/MacOSX10.12.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSTextView.h#L59-L61). – Ian Bytchek Dec 28 '17 at 13:15
12

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
    }
}
Marcus Rossel
  • 3,196
  • 1
  • 26
  • 41
taggon
  • 1,896
  • 13
  • 11
  • Thanks - got another error. 'NSTextView' is not convertible to 'NSScrollView' – Steve Jun 09 '14 at 12:34
  • Did you use Interface Builder to make a link of scrollView? I've executed above code successfully. – taggon Jun 09 '14 at 15:08
  • Can you share the project file, please? – Steve Jun 09 '14 at 19:03
  • @Steve [See this helloworld.zip](https://dl.dropboxusercontent.com/u/4415859/helloworld.zip) which is created with XCode 6 Beta (6A215l). – taggon Jun 10 '14 at 06:48
  • Thank you! So you really did it completely different. The book had me creating references between the button and the text view tied to the function, but I only see one reference in your xib. I'm going to study this in more detail today. – Steve Jun 10 '14 at 12:37
1

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:

enter image description here

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.

alondono
  • 2,496
  • 2
  • 28
  • 34
0

@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
    }
}
Will Fancher
  • 245
  • 3
  • 10
0

Does the "dong" error suggest a responder chain problem? What if you call becomeFirstResponder on the text field before inserting the text?

Andrew Ebling
  • 10,175
  • 10
  • 58
  • 75
  • No luck removing the "dong" when trying that method on the text field first. Here's a pastebin of the code: http://pastebin.com/A2ZaXXPD – Steve Jun 09 '14 at 12:41
-2

To create a weak reference user the weak keyword:

example:

@IBOutlet weak var myView: UIView

In your case

@IBOutlet weak var textField: NSTextView
Daniel
  • 577
  • 2
  • 12