I found some example code when building a menu bar item in OS X. It makes use of the single | and I'm unsure what it actually means.
(What I'm trying to do is have a function called on left click of the menu item, but have it show the menu on right click)
Here's my code
//Get reference to main system status bar
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
statusItem.image = icon
statusItem.menu = menuBar
if let statusButton = statusItem.button
{
statusButton.target = self
statusButton.action = #selector(statusItemClicked)
statusButton.sendActionOn(Int(NSEventMask.RightMouseUpMask.rawValue | NSEventMask.LeftMouseUpMask.rawValue))
}
Original Answer with code Left vs Right Click Status Bar Item Mac Swift 2
Bitwise OR, just like it does in most C-like languages. In this context, it's being used to combine flags.
That must be really old code. Nowadays, in modern Swift, NSEventMask is an Option Set. You can just say [NSEventMask.rightMouseUp, NSEventMask.leftMouseUp], and you don't need the Int cast at all. (Or if you haven't updated to Swift 3 yet, the case names would start with capital letters.)
Related
I have a couple of UIKit pop-up menu buttons with identical menu items on the same screen in a Swift app. The buttons are built by calling a function that uses an array of strings to create the list of menu items.
The problem is that depending on the button's vertical position on the screen, the menu items may appear in the order specified by the function, or reversed. If the button is in the upper half of the screen, the menu items are listed in the correct order. If the button is in the lower half of the screen the menu items are listed in reverse order.
I would prefer the menu items to appear in the same order regardless of the button's position on the screen. I could check the button location and have the menu creation function reverse the order, but that seems kind of clunky. I am hoping there's a cleaner way to override this behaviour.
The code and array used to create the button menus:
let buttonMenuItems = ["Spring","Summer","Autumn","Winter"]
func createAttributeMenu(menuNumber: Int)->UIMenu {
var menuActions: [UIAction] = []
for attribute in buttonMenuItems {
let item = UIAction(title: attribute) { action in
self.updateMenu(menuID: menuNumber, selected: attribute)
}
menuActions.append(item)
}
return UIMenu(title: "", children: menuActions)
}
The result is this:
Versions I'm using now in testing: Xcode 14.1, iOS 16.1, but I have seen this behaviour on earlier versions as well. (back to iOS 14.x)
Starting with iOS 16, there is a .preferredMenuElementOrder property that can be set on the button:
case automatic
A constant that allows the system to choose an ordering strategy according to the current context.
case priority
A constant that displays menu elements according to their priority.
case fixed
A constant that displays menu elements in a fixed order.
Best I can tell (as with many Apple definitions), there is no difference between .automatic and .priority.
From the .priority docs page:
Discussion
This ordering strategy displays the first menu element in the UIMenu closest to the location of the user interaction.
So, we get "reversed" order based on the position of the menu relative to the button.
To keep your defined order:
buttonNearTop.menu = createAttributeMenu(menuNumber: 1)
buttonNearBottom.menu = createAttributeMenu(menuNumber: 2)
if #available(iOS 16.0, *) {
buttonNearBottom.preferredMenuElementOrder = .fixed
buttonNearTop.preferredMenuElementOrder = .fixed
} else {
// out of luck... you get Apple's "priority" ordering
}
I want to set title for my NSOpenPanel, but it does not work! Apple says:
Gets and sets the title for the panel shown at the top of the window.
But I can not make codes works, not sure what is wrong?
let nsOpenPanel = NSOpenPanel()
nsOpenPanel.canChooseFiles = true
nsOpenPanel.title = "Hello, Choose Files"
if (nsOpenPanel.runModal() == .OK) ... // rest of codes ...
The title is something from the past when windows in macOS had title bars. To show text or an instruction on top of the NSOpenPanel object, use the message property:
nsOpenPanel.message = "Hello, Choose Files"
I've got five radio buttons, and selecting one should deselect the others.
I've been over a lot of the questions here about radio buttons in Swift, but they're either for iOS or outdated versions of Swift, because Xcode isn't offering me options like ".isSelected". I've got ".isEnabled" but clearly semantics matter here, because "enabled" isn't the same thing as "selected" and it shows.
Writing my code as a series of "if-else" statements along these lines:
func disableUnselectedButtons() {
if Button2.isEnabled == true {
Button1.isEnabled = false
Button3.isEnabled = false
Button4.isEnabled = false
Button5.isEnabled = false
}
}
results in a situation where I can select all five buttons, and can't DEselect any of them after another has been selected. I've tried variations of .on/.off as well, and can't find the right one for this situation.
It's also clumsy as heck to write a method with five if-else statements along those lines. So there's that.
What's the best way to go about implementing this?
If your radio buttons have the same superview and have the same action then they should work as expected.
To set the same action for each of your radio buttons you can do one of the following.
If you are using Storyboards, open both storyboard and related NSViewController swift file. Ctrl-drag your first radio button to the swift file. Then do the same for each of the other radio buttons ensuring you are dragging onto the function generated from the first Ctrl-drag.
If you are creating the radio buttons in code then set the action parameter in the init for each radio button to be the same.
Another way to approach this is to represent the buttons as a Set and then it's easy to iterate through them and configure their state. The below code actually allows for allowing multiple selections to support a scenario that wants to "select three of the six options".
let allButtons = Set(1...5). //or however many you want
func selectActiveButtons(_ activeButtons: Set<Int>, from allButtons: Set<Int>){
let inactive = allButtons.subtracting(activeButtons)
setButtonState(forButtons: inactive, isSelected: false)
setButtonState(forButtons: activeButtons, isSelected: true)
}
func setButtonState(forButtons buttons: Set<Int>, isSelected: Bool) {
buttons.forEach{
//remove below line and replace with code to update buttons in UI
print("Button \($0): \(isSelected ? "Selected" : "Unselected")")
}
}
// select buttons 1 & 3.
//If wanting a "classic" radio button group just use one value in the arrayLiteral.
selectActiveButtons(Set(arrayLiteral: 1,3), from: allButtons)
I'm having a bit of a brain fart in Swift and I know this code could be written better. Basically what it is, I have two images and I check if a value is over 3 to show an image and hide the other.
currently I have it like this
let greaterThanThree = value > 3
image1.isHidden = greaterThanThree
image2.isHidden = !greaterThanThree
But I feel like there is a more elegant way to write this.
I'd write it like this:
image1.isHidden = value > 3
image2.isHidden = !image1.isHidden
Anything shorter than that is just code golfing.
There seems to be a rule here that exactly one of these two views should be visible at all times. If so, I'd create, as part of my view controller's viewDidLoad, an instance of this struct:
struct AlternateViews {
let views : [UIView]
init(_ v1:UIView, _ v2:UIView) {
views = [v1,v2]
}
func hide(first:Bool) {
views[0].isHidden = first
views[1].isHidden = !first
}
}
let alternateViews = AlternateViews(image1, image2)
Okay, that's a lot of work to set up initially, but the result is that later you can just say
self.alternateViews.hide(first: value > 3)
The struct is acting as a tiny state machine, making sure that your view controller's views remain in a coherent state. This technique of moving the rules for state into utility structs attached to your view controller is recommended in a WWDC 2016 video and I've been making a lot of use of it ever since.
If you have more pairs of alternating views, just make and maintain more instances of the struct.
(If the rule that I've assumed is not quite the real rule, make a struct that does express the real rule.)
You can do this:
(image1.isHidden, image2.isHidden) = (value > 3) ? (true, false) : (false, true)
Basically if the value is greater than 3, the first image will be hidden and the second one won't. Otherwise, the second image will be hidden and the first one will not.
I am trying to implement Speech-to-text feature for watchkit app.
I referred this question which has sample code.
Following is the code I tried:
self.presentTextInputControllerWithSuggestions(["Start it", "Stop it"], allowedInputMode: .Plain, completion: { (selectedAnswers) -> Void in
if selectedAnswers.count > 0 {
if let spokenReply = selectedAnswers[0] as? String {
self.label.setText("\(spokenReply)")
}
}
})
label is a label to display text I speak.
When I run it, it shows the screen where you are supposed to speak (Siri kind of screen) and you have two options on top: ‘Cancel', and ‘Done'. Once I am done speaking, I tap on ‘Done’ but screen doesn’t go away or shows me initial screen, I always have to tap on ‘Cancel’ to go back, and I don’t get any speech data in form of text. I checked it and seems like selectedAnswers is always an empty array, unless I tap on the "Start it"/"Stop it" options.
Can anyone help me with this? I want to show the spoken message on label. I have code inside awakeWithContext method in InterfaceController.swift file, am I supposed to put it somewhere else?
I am using iPhone with iOS 9 beta 2 and watchOS 2 beta on AppleWatch.
Thanks!
You can ask for user input and give him suggestion (see Swift example bellow).
self.presentTextInputControllerWithSuggestions(["suggestion 1", "suggestion 2"] allowedInputMode: .Plain, completion: { (answers) -> Void in
if answers && answers.count > 0 {
if let answer = answers[0] as? String {
println("\answer")
}
}
})
If suggestion is nil it goes directly to dictation. It is not working on the simulator but it is on real watch.
Your approach is correct but something is wrong with your SIRI , try changing the language.
It should work like these.