I have created a subclass of NSView and assigned that to the main ViewController's view.
I have two NSTableViews on that view. These subviews have NSTextFields.
I start editing one of these text fields and press Cmd C.
performKeyEquivalent is triggered.
How do I know which text field triggered it?
I haved added this line
override func performKeyEquivalent(with event: NSEvent) -> Bool {
super.performKeyEquivalent(with:event)
let firstResponder = self.window?.firstResponder
but as expected it does not helper.
macOS tells me that firstResponder is a NSTextView, but I have no text view on the project.
tag does not helper either. I have the cells of my tableViews with tags equal to 100, 200, and 300 but
let tag = (firstResponder! as! NSTextView).tag
gives me -1 (?)
any ideas?
NSTextField is a delegate of own editor, which is NSTextView, so here how it should go
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if let editor = self.window?.firstResponder as? NSTextView,
let textField = editor.delegate as? NSTextField {
print("Identified by: \(textField.tag)")
}
return super.performKeyEquivalent(with:event)
}
Related
I am using 'UIKeyboardTypeDefault' keyboard type for my UITextfields.
The done button for the keyboard appears in lowercase as 'done'.
Is there anyway to make this a capitalised as 'Done' ?
You can try using the POD - > IQKeyboardManagerSwift
LINK -->
pod 'IQKeyboardManagerSwift', :git =>
'https://github.com/hackiftekhar/IQKeyboardManager.git'
This is very useful pod for handling stuff for keyboard
We cannot set a custom text for the return key, we can only select from the available options (refer attached screenshot).
In order to set a custom text, for e.g capitalised 'done' button, you will have to create a custom keyboard. attached screenshot
I've been struggling with the same question for tvOS for the last day or so, and figured out the solution is to get the 'to' viewController of the presented keyboard, iterate over its view hierarchy to find the button, then grab its text, capitalise it, and set it back.
Ended up with the following function:
fileprivate func CapitaliseReturnKey() {
let scene = UIApplication.shared.connectedScenes.first
guard
let windowSceneDelegate = scene?.delegate as? UIWindowSceneDelegate,
let window = windowSceneDelegate.window,
// This gets the presented keyboard view, which is the 'to' view for the presentation transition.
let keyboardView = window?.rootViewController?.transitionCoordinator?.view(forKey: .to)
else {
return
}
capitaliseReturnKey(in: keyboardView)
/// Traverses the view heirarchy of the passed-in view looking for a UIButton and capitalises its title.
/// - Parameter view: the view to iterate over
func capitaliseReturnKey(in view: UIView) {
for subView in view.subviews {
if subView.subviews.count > 0 {
capitaliseReturnKey(in: subView)
}
if subView.isKind(of: UIButton.self) {
if let buttonView = subView as? UIButton {
buttonView.setTitle(buttonView.currentTitle?.localizedCapitalized, for: .normal)
}
}
}
}
I have a UITextView embedded in a UITableViewCell.
The text view has scrolling disabled, and grows in height with the text in it.
The text view has a link-like section of text that is attributed with a different color and underlined, and I have a tap gesture recognizer attached to the text view that detects whether the user tapped on the "link" portion of the text or not (This is accomplished using the text view's layoutManager and textContainerInset to detect whether the tap falls within the 'link' or not. It's basically a custom hit test function).
I want the table view cell to receive the tap and become selected when the user "misses" the link portion of the text view, but can't figure out how to do it.
The text view has userInteractionEnabled set to true. However, this does not block the touches from reaching the table view cell when there is no gesture recognizer attached.
Conversely, if I set it to false, for some reason cell selection stops altogether, even when tapping outside of the text view's bounds (but the gesture recognizer still works... WHY?).
What I've Tried
I have tried overriding gestureRecognizer(_ :shouldReceive:), but even when I return false, the table view cell does not get selected...
I have also tried implementing gestureRecognizerShouldBegin(_:), but there too, even if I perform my hit test and return false, the cell does not get the tap.
How can I forward the missed taps back to the cell, to highlight it?
After trying Swapnil Luktuke's answer(to the extent that I understood it, at least) to no avail, and every possible combination of:
Implementing the methods of UIGestureRecognizerDelegate,
Overriding UITapGestureRecognizer,
Conditionally calling ignore(_:for:), etc.
(perhaps in my desperation I missed something obvious, but who knows...)
...I gave up and decided to follow the suggestion by #danyapata in the comments to my question, and subclass UITextView.
Partly based on code found on this Medium post, I came up with this UITextView subclass:
import UIKit
/**
Detects taps on subregions of its attributed text that correspond to custom,
named attributes.
- note: If no tap is detected, the behavior is equivalent to a text view with
`isUserInteractionEnabled` set to `false` (i.e., touches "pass through"). The
same behavior doesn't seem to be easily implemented using just stock
`UITextView` and gesture recognizers (hence the need to subclass).
*/
class LinkTextView: UITextView {
private var tapHandlersByName: [String: [(() -> Void)]] = [:]
/**
Adds a custom block to be executed wjhen a tap is detected on a subregion
of the **attributed** text that contains the attribute named accordingly.
*/
public func addTapHandler(_ handler: #escaping(() -> Void), forAttribute attributeName: String) {
var handlers = tapHandlersByName[attributeName] ?? []
handlers.append(handler)
tapHandlersByName[attributeName] = handlers
}
// MARK: - Initialization
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
commonSetup()
}
private func commonSetup() {
self.delaysContentTouches = false
self.isScrollEnabled = false
self.isEditable = false
self.isUserInteractionEnabled = true
}
// MARK: - UIView
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard let attributeName = self.attributeName(at: point), let handlers = tapHandlersByName[attributeName], handlers.count > 0 else {
return nil // Ignore touch
}
return self // Claim touch
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
// find attribute name
guard let touch = touches.first, let attributeName = self.attributeName(at: touch.location(in: self)) else {
return
}
// Execute all handlers for that attribute, once:
tapHandlersByName[attributeName]?.forEach({ (handler) in
handler()
})
}
// MARK: - Internal Support
private func attributeName(at point: CGPoint) -> String? {
let location = CGPoint(
x: point.x - self.textContainerInset.left,
y: point.y - self.textContainerInset.top)
let characterIndex = self.layoutManager.characterIndex(
for: location,
in: self.textContainer,
fractionOfDistanceBetweenInsertionPoints: nil)
guard characterIndex < self.textStorage.length else {
return nil
}
let firstAttributeName = tapHandlersByName.allKeys.first { (attributeName) -> Bool in
if self.textStorage.attribute(NSAttributedStringKey(rawValue: attributeName), at: characterIndex, effectiveRange: nil) != nil {
return true
}
return false
}
return firstAttributeName
}
}
As ususal, I'll wait a couple of days before accepting my own answer, just in case something better shows up...
Keep all your views active (i.e. user interaction enabled).
Loop through the text view's gestures and disable the ones you do not need.
Loop through the table view's gestureRecognisers array, and make them depend on the text view's custom tap gesture using requireGestureRecognizerToFail.
If its a static table view, you can do this in view did load. For a dynamic table view, do this in 'willDisplayCell' for the text view cell.
I have a view in my storyboard that by default the alpha is set to 0. In certain cases the Swift file sets the alpha to 1. So either hidden or not. Before this view just contained 2 labels. I'm trying to add 2 buttons to the view.
For some reason the buttons aren't clickable at all. So when you tap it normally buttons change color slightly before you releasing, or while holding down on the button. But that behavior doesn't happen for some reason and the function connected to the button isn't being called at all.
It seems like an issue where something is overlapping or on top of the button. The button is totally visible and enabled and everything but not clickable. I tried Debug View Hierarchy but everything looks correct in that view.
Any ideas why this might be happening?
EDIT I tried making a class with the following code and in interface builder setting the container view to be that class.
class AnotherView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for view in self.subviews {
if view.isUserInteractionEnabled, view.point(inside: self.convert(point, to: view), with: event) {
return true
}
}
return false
}
}
Go with hitTest(_:with:) method. When we call super.hitTest(point, with: event), the super call returns nil, because user interaction is disabled. So, instead, we check if the touchPoint is on the UIButton, if it is, then we can return UIButton object. This sends message to the selector of the UIButton object.
class AnotherView: UIView {
#IBOutlet weak var button:UIButton!
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if self.button.frame.contains(point) {
return button
}
return view
}
#IBAction func buttnTapped(sender:UIButton) {
}
}
I'm not going to ask how to hide the keyboard after you are done with editing a textField. My question is : Is there a way to do this on each view ? (like a setting) or do I need to write the two following functions and set the delegate properly every time ?
func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.
{
textField.resignFirstResponder()
return true;
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
I'm developing an app with a lot of textfields (and also views) so I try to avoid redundance code. What is the best solution to avoid this repetition?
Thank you!
You can create your own text field, which is subclass of UITextField. See the simple custom UITextField below:
import UIKit
class CustomTextField: UITextField, UITextFieldDelegate {
override func awakeFromNib() {
super.awakeFromNib()
self.delegate = self
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.resignFirstResponder()
return true
}
}
Use your custom text field name to Custom Class in your Storyboard.
See my example at Github
The easiest thing to do, is put one giant invisible button the size of the screen underneath your text fields, then when a non text field is tapped, you call the invisible button action to close it. If this does not apply in your scenario please let me know.
Create an IBAction method to dismiss keyboard
#IBAction func backgroundTapped (sender: UIView)
{
sender.endEditing(true)
}
Change the class of your UIView to UIControl which contains the textfields in storyboard (You can even do that to your view of the viewcontroller as shown)
It looks like this:
Now you can connect the IBAction to the Touch Up Inside event of this view, in the storyboard, as shown.
first of, I'm using xcode7.1 swift 2.
I have two nib/xib files, first is the main nib/xib and second one is pickview nib/viewcontroller file. How do I show the pickview nib/viewcontroller when textfield is tapped?
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
var readableTextfield: Bool? = true
textField.inputView = pickerViewCustom
return readableTextfield!
}
pickerViewCustom is a UIPickerView from pickview nib/viewcontroller
What I'm trying to to make is when a user is filling up a form, a UIPickerview will popup and will select values from an array and display the selected value to the textview. Am I doing it right?
Try set inputView when initializing UITextField, such as viewDidLoad:
func viewDidLoad() {
...
textField.inputView = pickerViewCustom
...
}