How to give the focus to the text field? - swift

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
}
}

Related

addCursorRect fails on NSButton after NSPopover loses focus

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)

TextField user interaction disabled, but display an error view when touched

I have a UITextField and in my ViewController's code it is set depending on it's value to
textField.isUserInteractionEnabled = false
or
textField.isUserInteractionEnabled = true
Now, when user interaction is disabled, I would like it still to react to touches and show an error message (e.g. unhide another view), which tells the user that editing this text field is not possible.
How can I achieve this in the most lean way? This solution here (https://stackoverflow.com/a/9117285) suggests to not disable user interaction, but reject content changes - which is what I don't want (the keyboard should not show up - it won't show up when user interaction is disabled, but I can't react to touches either).
You either need to add a view a bove the textfield when it's disabled with a gesture to show the appropriate message , or do this
NotificationCenter.default.addObserver(self, selector: #selector(keyShow), name:UIResponder.keyboardWillShowNotification, object: nil)
}
#objc func keyShow ( _ not:NSNotification) {
if shouldHideKeyB {
self.view.endEditing(true)
// show disabled message
}
}
where shouldHideKeyB is the current state of the textfield
Instead of using isUserInteractionEnabled you could implement your own isDisabled Bool and UITextFieldDelegate and implement func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool. When isDisabled is true show the error view and return false, otherwise return true. In the didSet of isDisabled you can hide the error view.
Returning false from this method should stop the keyboard from popping up and will still allow you to interact with the view.

Is it possible use different instance of UIMenuController for UITextField in Swift?

According to link we should use a singleton UIMenuController instance which is referred to the editing menu.
The problem is I want to show extra items in different situations. For instance, I want to just show "copy" item when keyboard is up. and show "copy" and "reply" when tapping on a tableview row.
When I add "reply" to the UIMenuController instance it is shown when tapping on UITextField too. therefore, I added these codes:
func textViewDidBeginEditing(_ textView: UITextView) {
var nonReplyMenuItems: [UIMenuItem] = []
if let allMenuItems = UIMenuController.shared.menuItems {
for menuItem in allMenuItems {
if menuItem.title != "reply".localized {
nonReplyMenuItems.append(menuItem)
}
}
}
UIMenuController.shared.menuItems = nonReplyMenuItems
UIMenuController.shared.setMenuVisible(true, animated: true)
}
It fixed the problem in most situations, but not all.
when keyboard is up and tapping on a row in tableview "reply" will be added. Then when I tap on UITextView the reply will be shown there too.
It seems your scenario is like it:
tap on textfield ----> shows copy
tap on tableview ---> shows copy and reply
tab on textfield ----> shows copy and reply (you want only copy shows)
As I know the textViewDidBeginEditing calls when your text filed is not editing and you tap on that; So if you have two textfileds by switching on that method calls every time but when you are switching between a text filed and another action base object your text field is editing and its state has not changed.
When you touch on tableview you must call textfield.resignFirstResponder() so when you tap on text field again the textViewDidBeginEditing calls again, the problem of this is hiding keyboard; The better way I preferÙˆ is adding function to touch down of text field or on gesture to do what you write on textViewDidBeginEditing method

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.

OSX NSTextView becomes uneditable after showing NSAlert,NSOpenPanel

I've got a NSTextView in my view that works as it should, but when I show a NSAlert and close it, it becomes uneditable (with text still selectable). The NSAlert is a save/cancel alert that updates the textView's string when the user selects save, the string is not updated when the user presses cancel. In both cases the textView was uneditable, the alert is shown when the users has made changes and wants to change the tableView selection.
It feels like the textView refuses first responder but when breaking and checking in the console its "true", I also checked some other values after the view was uneditable:
isEditable was True
isSelectable was True
canBecomeKeyView was True
acceptsFirstResponder was True
acceptsTouchEvents was False, tested True but did not work
My "test" setup:
Video, same when triggering the popup from a tableview selection change and a button : video
My popup code
func dialogOKCancel(question: String, text: String) -> Bool {
let myPopup: NSAlert = NSAlert()
myPopup.messageText = question
myPopup.informativeText = text
myPopup.alertStyle = NSAlertStyle.warning
myPopup.addButton(withTitle: "OK")
myPopup.addButton(withTitle: "Cancel")
return myPopup.runModal() == NSAlertFirstButtonReturn
}
let answer = self.dialogOKCancel(question: "Ok?", text: "Choose your answer.")
also tried:
let a = NSAlert()
a.messageText = "Delete the document?"
a.informativeText = "Are you sure you would like to delete the document?"
a.addButton(withTitle: "Delete")
a.addButton(withTitle: "Cancel")
a.alertStyle = NSAlertStyle.critical
a.beginSheetModal(for: self.view.window!, completionHandler: { (modalResponse) -> Void in
if modalResponse == NSAlertFirstButtonReturn {
print("Document deleted")
}
})
Stuff I tried:
Remove any textView updates, showing the alert still breaks it
Dragging a new "untouched" textView in my storyboard but now both textViews became uneditable
I tried showing the NSAlert when clicking a button instead of when changing the tableView's selection. Here the textView I was editing stays first responder but as soon as Ieave the textView, its uneditable again.
I tried triggering just an animation instead of the NSAlert, here the textView keeps working
I Tried replacing the NSAlert with a overlay View that had a title/description/buttons. when showing this dialog, the textView also became uneditable
I'm stuck on this for a long time and any help is greatly appreciated,
Thanks
After long time of debugging, I found this line to be the one that broke the textfields, I will leave this post online in case someone else stumbles upon this weird problem
window?.styleMask = NSFullSizeContentViewWindowMask
removing this line fixed the problem.