Check if the u key is pressed Swift Cocoa [duplicate] - swift

This question already has answers here:
Swift - Capture keydown from NSViewController
(5 answers)
Closed 6 years ago.
I'm trying to detect if the U key is pressed or not, and if it is it should print("BUT...BUT.."); but I'm not sure how to check for different keys, as the documentation for key presses is quite bad.. I found an answer with keycodes but they only work for QWERTY keyboards
viewcontroller.swift
override func viewDidLoad(){
super.viewDidLoad();
let f = Foo();
f.doSonethimg();
}
override func keyDown(theEvent: NSEvent){
let f = Foo();
f.KeyDown(theEvent);
}
Foo.swift
public func doSonething(){
print("Hello from Dylib");
}
public func keyDown(event: NSEvent){
if let keyString = theEvent.charactersIgnoringModifiers where keyString == "u" || keyString == "U" {
Swift.print("BUT...BUT…")
}
}
How would I change the keyDown Function to respond to U and what is it's default key?
I've looked at - Detecting key press event in Swift and https://superuser.com/questions/399430/mouse-button-and-keypress-counter-for-mac-os-x
also see - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingKeyEvents/HandlingKeyEvents.html - Overriding the keyDown: Method

You can easily check for a character by using NSEvents charactersIgnoringModifiers property.
func keyDown(theEvent: NSEvent) {
if let keyString = theEvent.charactersIgnoringModifiers where keyString == "u" || keyString == "U" {
Swift.print("BUT...BUT…")
}
}
Note: There is a difference between checking for 'u' and 'U'. They are modified by Shift. So if you want to have both recognized, check for both. (as in the example above)
Responder Chain:
The keyDown function is only called when the view or viewController participates in the so called Responder Chain.
To set up your viewController for being part of the Responder Chain, read the following documentation.

Related

How to Pass the data given in UITextField to another variable on another class

So i have a UITextField named apiTextField and a function of the saveButton :
func saveButton(_ sender: Any) {
}
I want when the user writes to the UITextField and press the saveButton that text the user wrote be passed to a variable which is called var baseURL:String? in another class.
I didn't find anything related to UITextField so i decided to make this question, another similar one is 10 years old!.
var anotherClass = AnotherClass()
func saveButton(_ sender: Any) {
guard let text = apiTextField.text, !text.isEmpty else { return }
anotherClass.baseURL = text
}
Is that what you are looking for?

macOS: Take Emoji from CharacterPalette (revised)

(This is a revised question - including answer - following on from macOS: Take emoji from characterPalette which describes the problems encountered in more detail)
Background/use case
I have an app where, instead of creating and maintaining an icon library, I let users type an emoji as a placeholder graphic. This works beautifully within the context of my app, but I am not happy with the input mechanism I use.
Problem
I would like to simplify this so I open the characterPalette, select an emoji, and display it either as the button's StringValue or in a Label (=non-editable NSTextField).
This does not seem possible. Unlike NSColorPanel or NSFontPanel, the characterPanel is not exposed to the Cocoa framework, so I cannot take its selectedValue, set its action, or catch a notification. The documentation for orderFrontCharacterPalette simply says Opens the character palette which ... is not helpful.
Attempted solutions and problems encountered
I tried to work with making my receiver the firstResponder, but unlike NSTextView, NSTextField cannot process emoji. I found a workaround using an NSTextView with an NSBox in front, making it the firstResponder, and using NSApp.orderFrontCharacterPalette(sender)but found that under various circumstances which all seem to involve an extra drawing call – setting the button's title, showing a label in SystemFont Mini size (regular size worked fine) the CharacterPalette will open (=the system menu now offers 'Hide Emoji & Symbols') without being displayed. (This persists until the application closes, even if you try to open the CharacterPalette through the regular menu/shortcut)
For the partial solution involving NSTextInputClient (the no-show seems to be a persistent bug), see answer below.
The emoji picker needs a minimal implementation of NSTextInputClient. For example a button:
class MyButton: NSButton, NSTextInputClient {
override var acceptsFirstResponder: Bool {
get {
return true
}
}
override func becomeFirstResponder() -> Bool {
return true
}
override func resignFirstResponder() -> Bool {
return true
}
func insertText(_ string: Any, replacementRange: NSRange) {
// this method is called when the user selects an emoji
if let string = string as? String {
self.title = string
}
}
func setMarkedText(_ string: Any, selectedRange: NSRange, replacementRange: NSRange) {
}
func unmarkText() {
}
func selectedRange() -> NSRange {
return NSMakeRange(0, 0)
}
func markedRange() -> NSRange {
return NSMakeRange(NSNotFound, 0)
}
func hasMarkedText() -> Bool {
return false
}
func attributedSubstring(forProposedRange range: NSRange, actualRange: NSRangePointer?) -> NSAttributedString? {
return nil
}
func validAttributesForMarkedText() -> [NSAttributedString.Key] {
return []
}
func firstRect(forCharacterRange range: NSRange, actualRange: NSRangePointer?) -> NSRect {
// the emoji picker uses the returned rect to position itself
var rect = self.bounds
rect.origin.x = NSMidX(rect)
rect.size.width = 0
return self.window!.convertToScreen(self.convert(rect, to:nil))
}
func characterIndex(for point: NSPoint) -> Int {
return 0
}
}
NSTextInputClient needs a NSTextInputContext. NSView returns a context from inputContext if the class conforms to NSTextInputClient unless isEditable is implemented and returns false. A label doesn't return a NSTextInputContext, the solution is to override inputContext:
class MyTextField: NSTextField, NSTextInputClient {
var myInputContext : NSTextInputContext?
override var inputContext: NSTextInputContext? {
get {
if myInputContext == nil {
myInputContext = NSTextInputContext(client:self)
}
return myInputContext
}
}
// and the same methods as the button, set self.stringValue instead of self.title in insertText(_:replacementRange:)
}
Willeke pointed me at NSTextInputClient which has provided the best solution so far. Apple's only example is in ObjectiveC, convoluted, and overly complex for what I was trying to do, so I am reproducing my code here.
Caveat: this is not a full implementation of NSTextInputClient, just enough to capture emoji input
I have created an NSButton subclass:
class TextReceiverButton: NSButton, NSTextInputClient {
//specific methods
func setButtonTitle(_ string: String?){
self.title = string ?? 🦊
}
//NSTextInputClient methods
func insertText(_ string: Any, replacementRange: NSRange) {
let receivedText = string as? String
setButtonTitle(receivedText)
}
func validAttributesForMarkedText() -> [NSAttributedString.Key] {
return [.font, .paragraphStyle, .writingDirection]
}
//Omitted: For anything else that wants a value, I return NSMakeRange(0, 0)/NSRect.zero or 0 as well as false for marked text and nil for attributed substring
}
(If you add the protocol to your class, it will offer stubs for the other methods)
The full set for NSAttributedString.Key is
[.font, .foregroundColor, .glyphInfo, .kern, .ligature, .link, .markedClauseSegment, .obliqueness, .paragraphStyle, .shadow, .spellingState, .strikethroughColor, .strikethroughStyle, .strokeColor, .strokeWidth, .superscript, .textAlternatives, .textEffect, .toolTip, .underlineColor, .underlineStyle, .verticalGlyphForm, .writingDirection]
(I have tested the short form with simple and composite emoji and nothing else seems necessary.)
The button's action is
#IBAction func displayEmojiInButton(_ sender: Any) {
NSApp.orderFrontCharacterPalette(self)
view.window?.makeFirstResponder(textReceiverButton)
}
Problems/Bugs
The NSTextInputClient document says 'you can subclass NSView' and Apple's code turns an NSView into a fully functional (receiving and drawing) text view class (I can't built it, but I assume it worked). So theoretically, you should be able to use the same code for NSTextField, which also ultimately inherits from NSView.
However, it turns out that NSTextField displays the 'CharacterPalette allegedly opens but never displays' bug I talked about earlier; though it does work with NSView. (I have not tested this further).
Furthermore, NSTextInputClient is not a complete replacement for NSTextView: it does not receive input from the keyboard viewer. (See Willecke's answer/comment for explanation/solution to these).
Verdict
NSApp.orderFrontCharacterPalette(self) fails 95% of the time when called from a view in the vincinity of a tab view (in splitView next to TabViewController, embedded in TabViewController), so while this code may be correct, it's also useless a lot of the time, at least under 10.13.

How to link text fields to functions

I have to create an app, with a label, a text box and a button, that will take the text that the user inputs and test if it is a palindrome and true a yes or no. It requires that I write the palindrome function in another file and call it when the button is press. The return from the function will also have to be displayed in the label field.
I have written and tested out the function but I don't know how connect it with the 3 objects on my app. They didn't teach how to do this yet in the course and googling the terms "linking text field to function in swift" only confused me more.
How should I go about learning how to do this? What terms should I search for to learn about this? I also included my palindrome funciton as reference.
func isPalindrome(word: String) -> Bool{
let word2 = word
let reversedWord = String(word.reversed())
if word2 == reversedWord {
return true
} else {
return false
}
}
isPalindrome(word: "racecar")
I presume that you know how to connect elements from storyboard to your view controller.
Link your textfield to your ViewController
#IBOutlet weak var textfield: UITextField!
then your button.
let text = textfield.text
#IBAction func checkBtn(_ sender: Any) {
isPalindrome(word: text!)
}
func isPalindrome(word: String) -> Bool{
let word2 = word
let reversedWord = String(word.reversed())
if word2 == reversedWord {
print("Is Palindrome")
return true
} else {
print("Isn't Palindrome")
return false
}
}
Also I don't think that you need to return something, except you want to use it.
for example:
if isPalindrome(word: text!){
//do something
} else{
//do something
}
If you are using storyboard, check how the #IBAction and IBOutlet works. its a visual way to link objects from storyboard into your code. If you created these label, textfield, button from code then checkout the addTarget method of UIButton for how to listen for click events.

How to handle escape key press event? [duplicate]

This question already has answers here:
Swift - Capture keydown from NSViewController
(5 answers)
the "funk" sound when hitting escape key in app
(1 answer)
Closed 5 years ago.
In Swift, is there a function in Cocoa framework to handle the press so that you can register which keyboard key has been hit?
I'd like to get the escape key
UPDATED:
i just found that the noise was casued by
super.keyDown(with: event)
wich is not needed for the thing to work
why did u add that line?
Create a subclass of NSWindow and implement keyDown event:
import Cocoa
import Carbon.HIToolbox
class CustomWindow: NSWindow {
override func keyDown(with event: NSEvent) {
switch Int(event.keyCode) {
case kVK_Escape:
print("Esc pressed")
default:
break
}
super.keyDown(with: event)
}
}
This line:
import Carbon.HIToolbox
Lets you use handy constants for keys, such as kVK_Escape.
Set this class as your main window class in the Interface Builder and you're all set:
P.S. To do the same form NSViewController, in viewDidLoad do:
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
self.keyDown(with: $0)
return $0
}
P.P.S. To mute "bang" sound, don't call super upon Escape key press - move super call to default:
default:
super.keyDown(with: event)
EDIT:
If you don't want any sound on Escape key press, then the following approach should be used:
Make an NSView subclass and set it to main view of the view controller:
import Cocoa
import Carbon.HIToolbox
class CustomView: NSView {
override func performKeyEquivalent(with event: NSEvent) -> Bool {
switch Int(event.keyCode) {
case kVK_Escape:
print("Esc pressed")
return true
default:
return super.performKeyEquivalent(with: event)
}
}
}

Best strategy in swift to detect keyboad input in NSViewController

I want to detect keyboard input in my NSViewController.
The idea is to have several actions performed if the user presses certain keys followed by ENTER/RETURN.
I have checked if keyDown would be a appropriate way. But I would receive an event any time the user has pressed a key.
I also have though on using an NSTextField, set it to hidden and let it have the focus.
But maybe there are other better solution.
Any ideas?
Thanks
I've finally got a solution that I like.
First it has nothing todo with any hidden UI Elements but rather let the viewcontroller detect keyboard input.
var monitor: Any?
var text = ""
override func viewDidLoad() {
super.viewDidLoad()
self.monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown, handler: myKeyDownEvent)
}
override func viewWillDisappear() {
//Clean up in case your ViewController can be closed an reopened
if let monitor = self.monitor {
NSEvent.removeMonitor(monitor)
}
}
// Detect each keyboard event
func myKeyDownEvent(event: NSEvent) -> NSEvent {
// keyCode 36 is for detect RETURN/ENTER
if event.keyCode == 36 {
print(text)
text = ""
} else {
text.append( event.characters! )
}
return event
}