Just start HelloWorld display in MacOS application development [duplicate] - swift

This question already has answers here:
(NSMenuItem): missing setter or instance variable
(3 answers)
Closed 4 years ago.
Tutorial for Hello World
I tried using the tutorial, link described above which was going until the execution but when i use the button click to display the name it show some error
Failed to connect (sayButtonClicked) outlet from
(HelloWorld.ViewController) to (NSButton): missing setter or instance
variable
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var nameField: NSTextField!
#IBOutlet weak var helloLabel: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func sayButtonClicked(_ sender: Any) {
var name = nameField.stringValue
if name.isEmpty{
name = "World"
}
let greeting = "Hello\(name)!"
helloLabel.stringValue = greeting
}
}

It sounds like you have an outlet connection in your storyboard named "sayButtonClicked" but "sayButtonClicked" is an IBAction, not an outlet. Try doing the following:
Delete that "sayButtonClicked" outlet from your storyboard.
Open your storyboard in the editor and the source-code for the view controller in an "assistant editor"
Control-drag from the dot next to your IBAction in your code onto the button in your storyboard. This will connect the action to the button. You may need to select "touch up inside" as the event you want to trigger the action.

Related

#IBAction fails to work in Swift, though I can connect it in Interface Builder, and it works in Objective-C

In all cases, I can wire user-interface buttons to actions with Interface Builder. But the buttons work for Objective-C but not for Swift.
Objective-C example (it works):
- (IBAction)TogglePlaying:(id)sender {
(details snipped for brevity)
}
Swift example (it doesn't work, though it's wired to its button):
#IBAction func Go(_ sender: Any) {
print("Going")
OutputText!.stringValue = InputText!.stringValue
}
I have no idea of what the difference might be, because everything I've found on using IBAction in Swift indicates that I've written it correctly. Also, in Interface Builder, I've set File's Owner's Custom Class correctly.
Update:
Using
ios - Find what Action is called by a Button in interface builder in debug mode - Stack Overflow
Find what Action is called by a Button in interface builder in debug mode
I used "Debug View Hierarchy", right-clicked on "NSButton - Go!" in the widget-hierarchy view, and selected "Print Description of NSButton - Go!"
I got
Printing description of $13:
<NSButton: 0x7fac1b116250>
I then did:
po [0x7fac1b116250 allTargets]
error: Execution was interrupted, reason: Attempted to dereference an invalid ObjC Object or send it an unrecognized selector.
The process has been returned to the state before expression evaluation.
Update:
I tried
po [0x7faf38011790 target]
(new address of that button) and I got
nil
Update:
The complete code of TLWindow, in Swift:
import Cocoa
class TLWindow: NSWindowController {
#IBOutlet weak var InputText: NSTextField!
#IBOutlet weak var OutputText: NSTextField!
override var windowNibName: NSNib.Name? {
return NSNib.Name("TLWindow")
}
#IBAction func Go(_ sender: Any?) {
print("Going")
OutputText!.stringValue = InputText!.stringValue
}
}
I don't know how to show that the xib is wired up correctly without doing a lot of screenshots. But it is, with the "Go!" button connected to "Go:" in "File's Owner". Also, "File's Owner" is set to "TLWindow", this class.
You are creating an instance of TLWindow in newDocument(), but then you're letting it go out-of-scope...
Try this:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
// add a property to "hang onto" the instance
var myTLWindow: TLWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
newDocument(self)
}
// Create an app window
#IBAction func newDocument(_ sender: Any?) {
let wc = TLWindow()
// add this line
myTLWindow = wc
wc.showWindow(self)
}
}

Enabling and disabling buttons in xcode

For a given simple audio app with a few buttons:
The button references inside ViewController is:
#IBOutlet weak var recordAudioButton: UIButton!
#IBOutlet weak var playAudioButton: UIButton!
#IBOutlet weak var processAudioButton: UIButton!
But where are those button names and references inside the xcode gui? Notice below that the Allow Recording button is highlighted: but there is no mention of recordAudioButton as a button name:
I want to modify the enabling/disabling logic of a different button that does not have a reference yet.. but can not see how/where to do that . The dialog does not show a way to view/change the button references. So where is the place to do that?
See in Referencing Outlets Section for each button.
You can set disable after buttons setup
#IBOutlet weak var recordAudioButton: UIButton! {
didSet { recordAudioButton.isEnabled = false }
}
#IBOutlet weak var playAudioButton: UIButton! {
didSet { playAudioButton.isEnabled = false }
}
#IBOutlet weak var processAudioButton: UIButton! {
didSet { processAudioButton.isEnabled = false }
}
Then in viewDidLoad check for permission
AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
self.recordAudioButton.isEnabled = allowed
self.playAudioButton.isEnabled = allowed
self.processAudioButton.isEnabled = allowed
}
}
Then your code
#IBAction func askForPermissions() {
recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(.playAndRecord, mode: .default)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission() { [unowned self] allowed in
// UI related work has to be executed on main thread(queue)
DispatchQueue.main.async {
self.recordAudioButton.isEnabled = allowed
self.playAudioButton.isEnabled = allowed
self.processAudioButton.isEnabled = allowed
}
}
} catch let error {
presentError(withMessage: error.localizedDescription)
}
}
You just defined the button as an outlet. So it does not appear on the Touch Up Inside, it appears at outlets area. But you connect your other button to an action function, this button appears on the Touch Up Inside.
Given the starting point / hint from #Picode here is what I was missing to get a new UIButton reference. A Medium article helped pave the way https://medium.com/#GanChau/outlet-vs-action-connections-in-xcode-b5331fb233a1 . In particular we need to have both an Editor and the Visual Designer showing:
So on my project now I control-clicked on the new button Run DSP and the context menu shows up:
Now select New Referencing Outlet and connect it to the ViewController code:
I typed in dspJsButton for the Name and we get the following code generated:
#IBOutlet weak var dspJsButton: UIButton!

How do I connect text fields from child window?

I have a single-window (OSX, not iOS) app that works beautifully, but I need to add a Preferences window. I have created the window and linked it to the Preferences menu (to which it opens and displays as expected). However, now I need to interact with the text fields I have set up.
Here is my new window:
Settings.xib
Here is my main window:
MainMenu.xib
Again, I can display it just fine, but I need to be able to set the values of the text fields in the Settings window. Not real sure how to do that? I am trying to stay away from Storyboards at the moment (just trying to keep it simple; maybe phase II). I am storing the values from the Settings window in the Keychain (which works beautifully in MainMenu.xib). Now I just need to be able to enter them and save them from Settings.xib.
My file structure is pretty straightforward. I have AppDelegate.swift which is doing all of the work. Do I (and how do I) connect Settings.xib to AppDelegate? I know I can bind the text fields from Settings.xib to outlets in AppDelegate (but when I do that I get an error: [General] [<NSApplication 0x6080001005a0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key accountNumber).
I know this is a n00b question, but it is so simple it is hard to find an applicable answer.
Thanks!
Creating a new window controller was the key. My problem, as I stated in my question, was getting the text fields wired up as functional outlets. I was able to show the window as desired, just couldn't get to the text fields.
I created SettingsWindowController.swift to subclass NSWindowController. My connections are as shown:
Window connections
File's Owner connections
Additionally, in Settings.xib, the Custom Class of File's Owner is set to SettingsWindowController. Then the individual text fields are connected to SettingsWindowController.swift as outlets.
In SettingsWindowController.swift:
`import Cocoa
class SettingsController: NSWindowController, NSWindowDelegate
{
#IBOutlet weak var accountNumber: NSTextField!
#IBOutlet weak var meterNumber: NSTextField!
#IBOutlet weak var webCredentialKey: NSTextField!
#IBOutlet weak var webCredentialPassword: NSTextField!
// allows window creation without needing to specify NIB name
override var windowNibName: String! {
return "Settings"
}
override func windowDidLoad() {
super.windowDidLoad()
// initialization code removed for brevity
}
func windowWillClose(_ notification: Notification) {
// teardown code removed for brevity
NSApplication.shared().stopModal()
}
}'
In AppDelegate.swift,
`var prefs: SettingsController? = nil
#IBAction func OpenPreferences(_ sender: Any) {
DispatchQueue.main.async(execute: { () -> Void in
NSApplication.shared().runModal(for: (self.prefs?.window)!)
})
}`
func applicationDidFinishLaunching(_ aNotification: Notification)
{
// Insert code here to initialize your application
prefs = SettingsController()
}`
Hope this helps someone else.

Changing content of UITextView by pressing button.

I'm new to swift programming, so this may be a dumb question, but how can I change the content of an UITextView Object by pressing a button.
The example I tested looks like this:
#IBOutlet weak var dataButton: UIButton!
#IBOutlet weak var dataView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func testButtonPressed(sender: UIButton) {
dataView.text = "Test worked."
}
Actually it doesn't work. My Storyboard has a ViewController from the Class where the above code is written. I also have the Button and the TextLabel. Do I need to specify anything in the Interface Builder or is something wrong with my code?
Thanks in advance for any help!!!
Make sure that your IBOutlet's and IBActions are connected in storyboard. It should work. You could set a breakpoint at the line with: dataView.text = "Test worked." to see if the code is reached. If it doesn't reach the breakpoint then your IBAction is not connected properly.
IBAction's sender parameter need an underscore before it, that is:
#IBAction func testButtonPressed(_ sender: UIButton) {
dataView.text = "Test worked."
}
are you sure you connected the code correctly?

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