addCursorRect fails on NSButton after NSPopover loses focus - swift

I have a NSPopover that contains two buttons. When I open the popover, the following code works to change the cursor to a pointing hand when hovering over the buttons and on clicking the button, 'Button pressed' appears in the console and an NSColorPanel appears as is desired.
class Button: NSButton {
override func resetCursorRects() {
super.resetCursorRects()
addCursorRect(bounds, cursor: .pointingHand)
}
}
#IBAction func buttonTapped(action:Any) {
print("Button pressed")
let cp = NSColorPanel.shared
cp.setTarget(self)
NSColorPanel.setPickerMode(.none)
cp.setAction(#selector(colorDidChange))
cp.isContinuous = false
cp.level = NSWindow.Level.statusBar
cp.makeKeyAndOrderFront(self)
}
However if I click anywhere else on the screen, and then go back to the NSPopover, the pointing hand cursor no longer appears when hovering over the button, and while the onClick event is still fired (as evidenced by 'Button pressed' logged in the console), the NSColorPanel doesn't open.
Any suggestions as to why this might be the case?

I ran into this issue and what solved it for me was adding the below code to the popover view controller viewWillAppear function.
NSApplication.shared.activate(ignoringOtherApps: true)

Related

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 give the focus to the text field?

Very simple setting: I have a ViewController scene with a TextField on it. The ViewController is the text field's delegate.
I would like to have the following behavior: When the user enters the text field (i.e., taps on it), I would like to display a modal alert box with an OK button. After the users presses OK, the TextField should get the focus (i.e., cursor blinking inside it).
I can't get this working. I react on the user tapping in the text field by textFieldShouldBeginEditing(). This works in the sense that I can display the message box there. But after the user (in this case it's me ;o)) taps the OK button, the text field doesn't have the focus, and when I tap it again, the message box appears again.
How can I get rid of this?
Do you really want the modal dialog to show every time the text field is clicked? Bear in mind that putting the activation in textFieldShouldBeginEditing() will mean that re-activating the field after dismissing the dialog will re-show the dialog.
Maybe you just need to show the dialog once? In which case a simple boolean flag that is set on first showing will fix the issue. I.e. at view controller scope:
var hasShownWarningDialog = false
and then implement instead (after comments):
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
if !hasShownWarningDialog
{
hasShownWarningDialog = true
// Create dialog here
self.present(alert, animated: true, completion: {self.textField.becomeFirstResponder()})
return false
}
else
{
return true
}
}

Swift Trigger click in textfield

In my app I'm using some pickerviews that appear when I click on a textfield, that works fine!
I wanted to do the same when I click on a Left Bar Button Item. I can't do it with the button because buttons doesn't have inputView property, needed for associate the pickerview to the button (in this case). So I want to have a hidden textfield that is programmatically clicked when I click on the button (when it's clicked it show the pickerview and change the button name, that's all done)
Is that possible?
The best I can do right know is something like this
txtFantasma.perform(
#selector(becomeFirstResponder),
with: nil,
afterDelay: 0.1
)
It works fine, but just work at the first time.
EDIT1:
I've tried to make it with buttons... The popover is showing, now I wanted to click in one button and dismiss the popover and pass data to the main viewcontroller.
class ViewPopup:UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btTituloAsc(_ sender: UIButton) {
let next = self.storyboard?.instantiateViewController(withIdentifier: "mainview") as! ViewController
next.ordenacao = "TituloAsc"
self.present(next, animated: true,completion:nil)
}
}
This works, but the main controller is showed without the Navigation Bar! How can I do the same but show de Navigation Bar?
The simplistic way to do it, is just display the pickerView as a popover once the user taps the button and then change the buttons title accordingly.

swift how to delay mac menu bar appearing animation

The Problem:
on a full screen mac app, when you move the mouse pointer to the top, the mac menu bar will instantly drop down. I'd like to make an app which delays the showing of the menu bar by a couple seconds.
It should be possible since that is the exact behaviour that VMWare Fusion does. while in fullscreen, the app lets the mouse pointer sits at the top of the screen for a couple seconds, before dropping down the menu bar.
how would I go about approaching this problem?
---- Update ----
I'm able to hide and show the menu bar when I want using NSMenu.setMenuBarVisible().
However, this doesn't seem to work as I thought it should be if I have multiple window app in fullscreen.
it works fine for one of the fullscreen window, but if I switch to the other fullscreen window I have to move the mouse before the call to setMenuBarVisible(true) seems to take effect.
Code
public class CustomWindowController: NSWindowController, NSWindowDelegate {
override public func windowDidLoad() {
window!.delegate = self
}
public class func create() -> CustomWindowController {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateControllerWithIdentifier("CustomWindowController") as! CustomWindowController
controller.window?.collectionBehavior.insert(.FullScreenDisallowsTiling)
return controller
}
// MARK: NSWindowDelegate protocol
public func windowDidEnterFullScreen(notification: NSNotification) {
print("window did enter fullscreen")
NSMenu.setMenuBarVisible(false)
}
public func windowDidBecomeKey(notification: NSNotification) {
print("window did become key")
NSMenu.setMenuBarVisible(false)
}
}
public class CustomWindow: NSWindow {
// right now I'm using mouse click to trigger the showing of the menu bar
// but my end goal is to use timer to trigger the showing of the menu bar
// to delay the menu bar showing by a couple seconds
public override func mouseUp(theEvent: NSEvent) {
NSMenu.setMenuBarVisible(true)
}
}
class CustomViewController: NSViewController {
private var otherWindowControllers = [CustomWindowController]()
// button to spawn a second identical window
#IBAction func spawnWindowButton(sender: AnyObject) {
let controller = CustomWindowController.create()
controller.showWindow(self)
// needed at least 1 strong reference to prevent the window to go away,
// so we save the controller to a list
otherWindowControllers.append(controller)
}
deinit {
otherWindowControllers.removeAll()
}
}
Repro:
start the app
click the spawn button to spawn a second window
click the greenlight button to make both window go to fullscreen
go to one of the fullscreened app window
move the mouse to the top of the screen
menu bar shouldn't drop down (intended behaviour)
left click
menu bar should drop down immediately (intended behaviour)
go to the other fullscreened app window
move the mouse to the top of the screen
menu bar shouldn't drop down (intended behaviour)
left click
menu bar doesn't drop down
move the mouse pointer a little bit
now it should drop down
any idea why this happens? and how to fix it so both fullscreen window has the correct behavior

Cannot manually set the highlighted state of a UIButton in an IBAction method

Inside my ViewController, I have two IBOutlets signUpButton and memberButton, and both buttons are linked to same signUpOrMemberButtonPressed() IBAction func. I am trying to set highlighted property to these 2 buttons so I can do subsequent work accordingly inside my submitPressed() IBAction func. But I noticed odd behavior between signUpOrMemberButtonPressed() and submitPressed() IBActions. After tapping on either signUpButton or memberButton, the button was highlighted inside my signUpOrMemberButtonPressed(), but by the time it executed submitPressed(), debug showed it's not highlighted. Here is my signUpOrMemberButtonPressed():
#IBAction func signUpOrMemberButtonPressed(sender: AnyObject) {
var button = sender as UIButton
button.highlighted = true
if signUpButton.highlighted {
memberButton.highlighted = false
} else {
signUpButton.highlighted = false
}
if (signUpButton.highlighted) {
println("signUpButton is highlighted inside 1st button")
} else if (memberButton.highlighted) {
println("memberButton is highlighted inside 1st button")
} else {
println("nothing is highlighted inside 1st button")
}
}
My submitPressed function:
#IBAction func submitPressed(sender: AnyObject) {
if (signUpButton.highlighted) { println("signUpButton is highlighted inside 2st button") }
else if (memberButton.highlighted) { println("memberButton is highlighted inside 2st button") }
else { println("nothing is highlighted inside 2nd button")
}
When run my app, I tapped on memberButton and then tapped on submit button. Here is the log output:
memberButton is highlighted inside 1st button
nothing is highlighted inside 2nd button
Nothing was set to run between these two func calls.
What is happening here is that the button is "un-highlighting" itself (after you manually set highlighted = true).
From the documentation of the highlighted property:
UIControl automatically sets and clears this state automatically when
a touch enters and exits during tracking and when there is a touch up.
You can set the highlighted state manually but you would have to do it after the UIButton unsets the highlighted state. You would have to do this on the next run loop which you can do by using dispatch_async.
The following should work:
var button = sender as UIButton
dispatch_async(dispatch_get_main_queue()) {
self.memberButton.highlighted = button == self.memberButton
self.signUpButton.highlighted = button == self.signUpButton
}