NSButton RadioGroup (NSMatrix Alternative) - swift

I've tried a few times to set up several similar buttons, all connected to the same IBActions, and still can't seem to replicate the RadioButton Behaviour.
At the moment, I have 5 Buttons, all children of one NSView..
NSView
- ButtonOne - NSButton (Square Button)
- ButtonTwo - NSButton (Square Button)
- ButtonThree - NSButton (Square Button)
- ButtonFour - NSButton (Square Button)
- ButtonFive - NSButton (Square Button)
They're all connected to a common IBAction, which reads as:
#IBAction func activityButtonPressed(sender: NSButton) {
println("\(sender.title) Pressed")
}
Which works to the point where the actual mosueDown events are caught and sent to the func (as expected)..
how do I get them working in the Radio Button mode? i.e. have their NSOnState and NSOffState toggle when the various buttons are clicked?
When I try and change the button type to Radio Button, it automatically flips back to "Switch"....
Cheers,
A

just give the NSRadioButtons the same superview and action. And as noted by Good Doug in his answer,
[…] if you are using square button style, you can't use radio type. You want to use the On Off type in Interface Builder.
I really don't think you want to have your action method set the state of all buttons in the group… let AppKit do that for you.
From OS X Developer Release Notes: Cocoa Application Framework (10.10 and Earlier)
An NSButton configured as a radio button (with the -buttonType set to NSRadioButton), will now operate in a radio button group for applications linked on 10.8 and later. To have the button work in a radio group, use the same -action for each NSButton instance, and have the same superview for each button. When these conditions are met, checking one button (by changing the -state to 1), will uncheck all other buttons (by setting their -state to 0).

Since you specifically don't want to use NSMatrix...
First issue is that if you are using square button style, you can't use radio type. You want to use the On Off type in Interface Builder.
Here is the code I used for 4 buttons to test this out:
var buttonList : [NSButton] {
return [button1, button2, button3, button4]
}
#IBAction func buttonPressed(sender: NSButton) {
buttonList.forEach {
$0.state = $0 == sender ? .on : .off
}
}
As you can see, it iterates over all of the buttons, testing if it is the button that was pressed. It turns that button on while turning the other buttons off.
You will need to manage any state you want from there, or by checking the state of all of the buttons:
func whichButton() -> ActivityState {
if button1.state == .on { return .one }
if button2.state == .on { return .two }
...
}
where ActivityState.one is some enum case that you can use to determine which state you are in.

I believe that you are looking for NSMatrix:
You can configure this with as many rows and columns as you like:
You can then choose whether to allow a single choice or a variety:
You capture the selection in your IBAction with any combination of sender.selectedColumn and sender.selectedRow

Related

How to add radio button property to Custom View using NSButton and NSTextfield

I m creating a custom NSView, it has NSBUTTON of type radio and NSTextfield. Right now, it doesnot show property of radio button i.e. if I select one radio button, other radio buttons should go off. Any fix
the layer of views is like this
NSCUSTOMBUTTON has NSTEXTFIELD AND NSBUTTON, THEY are then added to the stackview, which is further added to the super view.
Edit: I found the answer, basically you have to implement your own delegate function, that handles mousedown event and then add the functionality of radio button where you extend the delegate.
private func handleClickAction(_ clickedButton: ChoiceButton) {
guard isRadioGroup else { return }
if previousButton?.value != clickedButton.value {
previousButton?.state = .off
previousButton = clickedButton
}
}

detect when button is pressed down and then up Swift macOS

I'm writing an app that send commands to a set top box.
The box can receive two types of commands: push and release.
I can get the button pressed on macOs in swift.
#IBAction func btnPressed(sender: NSButton) { } in which i send the command and the release. For any command such as change channel, mute or other, all works fine.
Instead, for volume up or done, I need, by doing what I do, to click several time to have the volume up or down.
I got the mouse up and down working, detecting where the click happened (inside the NSImageView (if not a button) corresponding to the up and down images) to simulate a long volume up or down press, but I can't get it inside the button pressed method.
is there a way in the 'buttonpressed' method to combine the mouse event in order to simulate a long press while holding the mouse down?
PS: I have googled and searched here also but did not find a hint.
if it can help:
1 subclass the button class to fire the action on mouseDown and mouseUp (the action will be fired twice)
class myButton: NSButton {
override func awakeFromNib() {
super.awakeFromNib()
let maskUp = NSEvent.EventTypeMask.leftMouseUp.rawValue
let maskDown = NSEvent.EventTypeMask.leftMouseDown.rawValue
let mask = Int( maskUp | maskDown ) // cast from UInt
//shortest way for the above:
//let mask = NSEvent.EventTypeMask(arrayLiteral: [.leftMouseUp, .leftMouseDown]).rawValue
self.sendAction(on: NSEvent.EventTypeMask(rawValue: NSEvent.EventTypeMask.RawValue(mask)))
//objC gives: [self.button sendActionOn: NSLeftMouseDownMask | NSLeftMouseUpMask];
}
}
2: in the storyboard, change the class of the NSButton to your class:
3: set the sender of the action to your subclass and check the currentEvent type:
#IBAction func volUpPressed(sender: myButton) {
let currEvent = NSApp.currentEvent
if(currEvent?.type == .leftMouseDown) {
print("volume Up pressed Down")
//... do your stuff here on mouseDown
}
else
if(currEvent?.type == .leftMouseUp) {
print("volume Up pressed Up")
//... do your stuff here on mouseUp
}
}

how to hide button title but can still call it using sender.currentTitle?

I added a button in the main storyboard in Xcode. This button has an image as the background and its title "blueDoor" is shown on top of the background (see photo below).
There will be three buttons like this and they are linked to one IBAction. I would like to use sender.currentTitle to let the program know which button is clicked, but I don't want the text to show on the image.
How can I hide the text but still keep the title attribute so sender.currentTitle can be used? Or is there another way to do so?
A button with an image as the background:
You can use tag to do this.
Open storyboard > select your button > select Show the Attributes Inspector section in the right bar of the Xcode > scroll down and find Tag under the View section. And give different tags to your buttons.
Then your IBAction should be like this ->
#IBAction func didTapMyButtons(_ sender: UIButton) {
switch sender.tag:
case 0: // it is your first button's tag
// Do something with button which has tag `0`
case 1: // it is your second button's tag
// Do something with button which has tag `1`
case 2: // it is your third button's tag
// Do something with button which has tag `2`
default: break
}
Shortly: you can not do that. The currentTitle property is essentially a getter for button.titleLable.text. So is title(for: UIControl.State) setter (setTitle(String?, for: UIControl.State))
What docs say:
var currentTitle: String? { get }
Discussion
The value for this property is set automatically whenever
the button state changes. For states that do not have a custom title
string associated with them, this method returns the title that is
currently displayed, which is typically the one associated with the
normal state. The value may be nil.
This means whatever changes you bring to titleLabel.text, it is automatically reflected on a currentTitle property.
You can use tags as suggested or add a custom action handler for every button as you create them. E.g. for cancel and proceed buttons one will do it like so:
self.cancelButton.addTarget(self, action: #selector(self.cancelActionMethod), for: .touchUpInside)
self.proceedButton.addTarget(self, action: #selector(self.proceedActionMethod), for: .touchUpInside)
And then somewhere inside self:
#objc func cancelActionMethod() {
// some cancel specific actions
}
#objc func proceedActionMethod() {
// some cancel specific actions
}

How to create toggle button that only toggles once

I want to create a button that, when pressed, toggles to a different format and stays that way, not allowing the user to toggle it back. For example, I am trying to create a button that says "Special 1" then when it is clicked by the user, it toggles to say "USED". When the word "USED" pops up on the UIButton, it stays that way and the user cannot change it back. Please help me out I can't figure this out. I am also open to other ideas to execute this other then a UIButton, for example maybe a UITableView??
What you can do is create a boolean (true or false) variable in your ViewController, and whenever the button is tapped you set this variable so that the tap function associated with the button can be manipulated.
1.) Make an action connection from your ViewController to your code this can be done by clicking the dual view button (the two circles in the top right corner) then holding control and drag your button into your code. :
For more information on connecting views to your code: https://codewithchris.com/9-hooking-it-all-up-swift-iboutlet-properties/
Skip to 6 min for the actual connection.
2.) Now we can just make our boolean variable, and make a basic if statement:
var didClick:Bool = false
#IBAction func touchButton(_ sender: UIButton) {
if !didClick { // If didClick is false
didClick = true // Set it to true
sender.setTitle("USED", for: .normal)
}
}
Note: The sender should be set to UIButton NOT Any
Should look like this:
_ sender: UIButton
If you are using a storyboard you can write this inside the IBAction function:
#IBAction func buttonName(_ sender: UIButton) {
sender.isUserInteractionEnabled = false
// Do whatever you need to do after...
}
sender's type could be UIButton or Any depending on how you made the connection from the storyboard.

How to determine which UIBarButtomSytemItem has been pressed

in a navigation controller i have 2 bar button items linked in the StoryBoard: 1 on the right with System Item = Add and 1 on the left with System Item = Cancel. Both buttons are linked to the same action. how can i determine which one has been triggered using a switch statement?
#IBAction func pressedBarButtonItem(sender: UIBarButtonItem) {
switch sender {
case UIBarButtonSystemItem.Add:
print("UIBarButtonSystemItem.Add button has been pressed ...")
default:
break
}
}
this causes an error "Enum case "Add" is not a member of type 'UIBarButtonItem'" so is there an Enum attribute to the bar button that says it is a system type?
thanks
UIBarButtonSystemItem is used only at initialization to define a system image, those are not styles or types and are not stored or affected to the button.
You may want to use tags or outlet references to select the right action, or use different IBActions, which seems more appropriate.
Give tag to both the UIBarButtonItems in storyborad.
Say Add have tag value 1001 and Cancel have tag value 1002.
Compare tags in you IBAction.
#IBAction func pressedBarButtonItem(sender: UIBarButtonItem) {
switch sender.tag {
case 1001:
print("UIBarButtonSystemItem.Add button has been pressed ...")
case 1002:
print("UIBarButtonSystemItem.Cancel button has been pressed ...")
default:
break
}
}