Stop listening to NSEvent in Swift - swift

I am trying to get NSEvent to stop listening to local event. Is there a way to access the event after creating it?(which is what I unsuccessfully tried in removeEvent)
override func viewDidLoad() {
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: sayHello(event:))
}
func sayHello(event: NSEvent) -> NSEvent {
print("Welcome")
return event
}
#IBAction func removeEvent(_ sender: NSButton) {
let event = NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: sayHello(event:))
NSEvent.removeMonitor(event)
}

Just create a reference to your event monitor adding an instance property to your view controller and remove it with your button action:
var monitor: Any?
func sayHello(_ event: NSEvent) -> NSEvent {
print(#function)
return event
}
override func viewDidLoad() {
super.viewDidLoad()
monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: sayHello)
}
#IBAction func removeEvent(_ sender: NSButton) {
if let monitor = monitor {
NSEvent.removeMonitor(monitor)
}
}

Related

MacOS-Drag String onto NSStatusItem

I am trying to detect when a string is getting dragged onto the NSStatusItem. Here is the implementation of my code in AppDelegate:
func applicationDidFinishLaunching(_ aNotification: Notification) {
if let button = statusItem.button {
button.image = NSImage(named:NSImage.Name("StatusBarButtonImage"))
button.action = #selector(menubarAction(_:))
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
button.window?.registerForDraggedTypes([.string ])
button.window?.delegate = self
}
}
And here is my NSWindow delegate calls:
extension AppDelegate:NSWindowDelegate{
func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation{
NSApplication.shared.activate(ignoringOtherApps: true )
return .copy
}
func performDragOperation(_ sender: NSDraggingInfo) -> Bool{
NSApplication.shared.activate(ignoringOtherApps: true )
return true
}
}
However these delegates do not get called when a string is dragged onto the NSStatusItem. I know that drag has been detected as:
applicationDidBecomeActive(_ notification: Notification)
gets called. Any suggestions why my delegate is not getting called.
Thanks
Reza
draggingEntered and performDragOperation are methods of protocol NSDraggingDestination. Swift doesn't call a protocol method if the class doesn't adopt the protocol. AppDelegate must adopt NSDraggingDestination.
extension AppDelegate: NSWindowDelegate {
}
extension AppDelegate: NSDraggingDestination {
func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation{
NSApplication.shared.activate(ignoringOtherApps: true )
return .copy
}
func performDragOperation(_ sender: NSDraggingInfo) -> Bool{
NSApplication.shared.activate(ignoringOtherApps: true )
return true
}
}

Trying to create a drop-down menu that displays options when the button is pressed

I am trying to create a drop down menu, that when you press the "Select" button two options drop down. I want these options to be a Timesheet button and Expense report button. I can't get it to work, here is my code.
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var SelectBTN: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func handleSelection(_ sender: UIButton) {
SelectBTN.ForEach { (button) in
UIView.animate(withDuration: 0.3, animations: {
button.isHidden = !button.isHidden
self.view.layoutIfNeeded()
})
}
}
#IBAction func cityTapped(_ sender: UIButton) {
}
#IBAction func ERBTNpressed(_ sender: UIButton) {
self.performSegue(withIdentifier: "ERSegue", sender: self)
}
#IBAction func TSBTNpressed(_ sender: UIButton) {
self.performSegue(withIdentifier: "TSSegue", sender: self)
}
}
You could set a menu as a primary action for a button in viewDidLoad:
SelectBTN.showsMenuAsPrimaryAction = true
SelectBTN.menu = buttonMenu()
In the menu you could add actions:
func buttonMenu() -> UIMenu {
let ERAction = UIAction(title: "Expense report", image: nil) { (_) in
self.performSegue(withIdentifier: "ERSegue", sender: self)
}
let TSAction = UIAction(title: "Timesheet", image: nil) { (_) in
self.performSegue(withIdentifier: "TSSegue", sender: self)
}
return UIMenu(title: "", options: .displayInline, children: [ERAction, TSAction])
}
You could also add images to actions and specify attributes (i.e. .destructive for Delete action)

What's the replacements of override in Rx?

I'm new to rx and curious about a question: What's the replacement of override in Rx?
For codes I have read about rx, a button is configured like:
override func viewDidLoad() {
super.viewDidLoad()
updateConversation()
self.naviAvatar.rx.tap
.debug("naviAvatar tap")
.subscribe(onNext: { _ in
print("didTapNaviAvatar")
})
.disposed(by: disposeBag)
}
and it works perfect.
However I meet a question that in a subclass I want to silent the button and I don't know how to achieve in rx.
In my previous code, I have following codes:
class A: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapNaviAvatar(_:)))
self.naviAvatar.addGestureRecognizer(tapRecognizer)
}
#objc func didTapNaviAvatar(_ sender: Any) {
print("didTapNaviAvatar")
}
//...
}
class B: A {
// Silent the method, do nothing.
override func didTapNaviAvatar(_ sender: Any) {}
//...
}
I came up with an idea that I can reconfigure the naviAvatar in B's viewDidLoad method. But what if I have number of codes(like 20 lines, including mapping, filtering, configuring) about the button's behavior but I just want to change only one line(like just override the button title on touch down)?
Any helps would be appreciated.
Weak self isn't needed because there is no delay or async method fired on a tap action. So you can just pass the method and it will trigger the method overridden in class B.
class A: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
naviAvatar.rx.tap
.debug("naviAvatar tap")
.subscribe(onNext: didTapNaviMoreButton)
.disposed(by: disposeBag)
}
#objc func didTapNaviAvatar(_ sender: Any) {
print("didTapNaviAvatar")
}
}
class A: B {
override func didTapNaviAvatar(_ sender: Any) {}
}

Swift App runs but no buttons appear

I wrote a Swift app but only the window appears when it runs. I can't see any buttons.
Here is my code... I've tried removing the .white attribute thinking maybe it was hidden behind a layer. Nothing.
//
// ViewController.swift
// BraviaRemote
//
// Created by Ed Gilroy on 7/2/17.
// Copyright © 2017 Edward Williams. All rights reserved.
//
import Cocoa
import Alamofire
class ViewController: NSViewController, NSTextFieldDelegate {
#IBAction func MenuButton(_ sender: NSButtonCell) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAABgAw==")
}
#IBAction func ReturnButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAgAAAJcAAAAjAw==")
}
#IBAction func InfoButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAA6Aw==")
}
#IBAction func GuideButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAgAAAKQAAABbAw==")
}
#IBAction func SelectButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAABlAw==")
}
#IBAction func ChnUpButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAAQAw==")
}
#IBAction func ChnDownButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAARAw==")
}
#IBAction func VolUpButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAASAw==")
}
#IBAction func VolDownButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAATAw==")
}
#IBAction func LeftButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAA0Aw==")
}
#IBAction func RightButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAAzAw==")
}
#IBAction func UpButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAB0Aw==")
}
#IBAction func DownButton(_ sender: NSButton) {
triggerRemoteControl(irccc: "AAAAAQAAAAEAAAB1Aw==")
}
#IBAction func OnOffButton(_ sender: NSSegmentedControl){
}
#IBOutlet weak var IPField: NSTextField!
var IPAddress: String? {
didSet {
if IPField != nil { IPAddress = "http://\(IPAddress!)/sony/IRCC?" }
else {IPAddress = "http://192.168.2.7/sony/IRCC?"}
if let ip = IPAddress { print (ip) } //Unwraps optional
}
}
override func controlTextDidChange(_ obj: Notification) {
if let txtField = obj.object as? NSTextField {
if txtField.tag == 0 {
//Validation (for later)
IPAddress = txtField.stringValue
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
func viewDidLoad() {
super.viewDidLoad()
}
}
override func viewDidAppear() {
// Window Properties, including solid colour, lack of resize, movable by background.
view.window?.titlebarAppearsTransparent = true
view.window?.backgroundColor = NSColor.white
view.window?.styleMask.remove(.resizable)
view.window?.isMovableByWindowBackground = true
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
struct SOAPEncoding: ParameterEncoding {
let service: String
let action: String
let IRCCC: String
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard parameters != nil else { return urlRequest }
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("text/xml", forHTTPHeaderField: "Content-Type")
}
let soapBody = "<?xml version=\"1.0\" encoding=\"utf-8\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body><u:\(action) xmlns:u=\"\(service)\"><IRCCCode>\(IRCCC)</IRCCCode></u:X_SendIRCC></s:Body></s:Envelope>"
urlRequest.httpBody = soapBody.data(using: String.Encoding.utf8)
return urlRequest
}
}
func triggerRemoteControl(irccc: String) {
Alamofire.request(IPAddress!,
method: .post,
parameters: ["parameter" : "value"],
encoding: SOAPEncoding(service: "urn:schemas-sony-com:service:IRCC:1",
action: "X_SendIRCC", IRCCC: irccc)).responseString { response in
print(response)
}
}
}
Three errors:
First, you are overriding viewDidLoad() and defining another viewDidLoad() inside of it.
Your code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
func viewDidLoad() {
super.viewDidLoad()
}
}
Should just look like this:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
Second, you are overriding viewDidAppear but never calling super.
Your code:
override func viewDidAppear() {
// Window Properties, including solid colour, lack of resize, movable by background.
view.window?.titlebarAppearsTransparent = true
view.window?.backgroundColor = NSColor.white
view.window?.styleMask.remove(.resizable)
view.window?.isMovableByWindowBackground = true
}
Should look like this:
override func viewDidAppear() {
super.viewDidAppear()
// Window Properties, including solid colour, lack of resize, movable by background.
view.window?.titlebarAppearsTransparent = true
view.window?.backgroundColor = NSColor.white
view.window?.styleMask.remove(.resizable)
view.window?.isMovableByWindowBackground = true
}
Third, you are overriding the IPAdress didSet and then setting it again. This will cause an infinite loop. You are also comparing a textField to nil, which it will never be, because it's a NSTextField!, instead of checking whether it's empty or not. I can't really make sense of what you're trying to achieve here but you should rip all this overriding nonsense out until you can clearly formulate your intention.

MouseMoved function is never called

I'm trying to override mouseMoved function in NSViewController.
import Cocoa
class MainViewController: NSViewController {
override var acceptsFirstResponder: Bool {get {return true} }
#IBOutlet var background: RandomNumberBackground!
override func viewDidLoad() {
super.viewDidLoad()
}
override func awakeFromNib() {
NSTimer.scheduledTimerWithTimeInterval(0.04, target: background, selector: "updateNumbers", userInfo: nil, repeats: true)
}
#IBAction func btnLevelClicked(sender: AnyObject) {
self.presentViewControllerAsSheet(LevelScrollController())
}
override func mouseMoved(theEvent: NSEvent) {
Swift.print("MOVED!")
}
}
I've overrided acceptsFirstResponder but mouseMoved is never called. Why? Where I go wrong?
You need to set acceptsMouseMovedEvents in the windows property.
Add the following code to applicationDidFinishLaunching
func applicationDidFinishLaunching(aNotification: NSNotification) {
window.acceptsMouseMovedEvents = true
}
No need to set anything on the window.
A tracking area is the proper way to handle this.
Just add this in the VC's vieWDidLoad() method.
let ta = NSTrackingArea(rect: CGRect.zero, options: [.activeAlways, .inVisibleRect, .mouseMoved], owner: self, userInfo: nil)
self.view.addTrackingArea(ta)
This is the code I add when I need to track mouse moves in a view.
It is longer than previous answers, but this is what it takes to be done properly :)
class MyView: NSView {
var trackingArea: NSTrackingArea?
// MARK: - Tracking area management
/// Will install tracking area on the view if a window is set
override func viewDidMoveToSuperview() {
super.viewDidMoveToSuperview()
installTrackingArea()
}
/// Install tracking area if window is set, remove previous one if needed.
func installTrackingArea() {
guard let window = window else { return }
window.acceptsMouseMovedEvents = true
if trackingArea != nil { removeTrackingArea(trackingArea!) }
let trackingOptions = [.activeAlways, .mouseEnteredAndExited, .mouseMoved]
trackingArea = NSTrackingArea(rect: bounds,
options: trackingOptions,
owner: self, userInfo: nil)
self.addTrackingArea(trackingArea!)
}
/// Called when layout is modified
override func updateTrackingAreas() {
super.updateTrackingAreas()
installTrackingArea()
}
//MARK: - Mouse Events handling
override func mouseExited(with event: NSEvent) {
print("Good bye mouse")
}
override func mouseEntered(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
print("Hello mouse, welcome at \(point)")
}
override func mouseMoved(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
print("Mouse moved \(point)")
}
}
You need to set the acceptsMouseMovedEvents property on the window the view belongs to. See
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/index.html#//apple_ref/occ/instp/NSWindow/acceptsMouseMovedEvents
In swift 3 you can put this in your viewDidLoad()
self.view!.window?.acceptsMouseMovedEvents = true;