UITextView dataDetector is not working in tableViewCell - swift

I have a UITextView in a TableViewCell and I wanted to enable data detection in the textView in order to show clickable links and Phone numbers.
So I did this in storyBoard
These steps didn't work so I did the same in code.
#IBOutlet weak var postDescTextView: UITextView!
override func layoutSubviews() {
super.layoutSubviews()
postDescTextView.isUserInteractionEnabled = true
postDescTextView.isSelectable = true
postDescTextView.dataDetectorTypes = .all
}
But none of the steps seems to work.
There is clearly something I'm doing wrong. I'm pretty sure the answer is a simple one. I've found some similar questions like these.
Question 1
Question2
But unlike these questions, I don't want my TextView editable nor I don't want to be able to select The TableViewCell. Just need to make the Datalink detection work in a TableViewCell.

If you're trying to make postDescTextView programmatically remove everything else and add the following.
postDescTextView.isEditable = false
postDescTextView.dataDetectorTypes = .all
Or if you have a storyboard
Note: If you're using the programmatic approach there is no need for giving it in viewDidLayout which gets called every time there's a layout change. Give it in the init method instead.

Related

Prevent VoiceOver From Reading UIButton as "Possible Text"

I am writing a very simple game that is designed specifically for blind users, but may also be used by sighted users. It uses many buttons as elements, however, blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate.
The issue lies in the fact that there are no accessibility objects on the screen at all, so whenever the game loads, voiceover starts reading the labels on buttons (e.g. "Possible text: back, menu...). These buttons are read regardless of the fact that they are not enabled. I also can't remove most of them from the view for blind users.
I have tried turning off accessibility for the elements, unchecking "button" from accessibility traits, everything has allows direct interaction selected, I have tried .accessibilityElementsHidden, all the suggestions from How do you exclude a UIButton from VoiceOver? and nothing seems to work.
My current solution has a clear UILabel with no text in it, this is set to the only item in the .accessibilityElements array, and then for good measure I post an accessibility screen changed notification with that label as the object so it becomes focused, then I wait a second in a dispatch queue async after call, remove the label entirely, and set focus back to the main view so the user can interact.
Here is an example of my current solution:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.accessibilityElements = [lblVoiceOver!]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibility.post(notification: .screenChanged, argument: lblVoiceOver)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.lblVoiceOver.removeFromSuperview()
UIAccessibility.post(notification: .screenChanged, argument: self.view)
}
}
This is a silly hack, at best, and I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover. I believe the possible text feature was added in iOS 11, to help apps that are not written with accessibility in mind to be more accessibility friendly, but so far I haven't found a way to turn this off.
The issue lies in the fact that there are no accessibility objects on the screen at all.
If you want to reach this purpose, just write self.view.accessibilityElementsHidden = true in your view controller that will contain no accessible element for VoiceOver anymore: this will indicate that the content of your container isn't accessible.
blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate [...] I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover.
... following the preceding rationale, you should prevent VoiceOver from analyzing and reading anything in your view.
Now, dealing just with your buttons, I created a blank project with a simple code for the view controller hereafter (Swift 5.0, iOS 12):
import UIKit
class NonAccessibleButtonVC: UIViewController {
#IBOutlet weak var aboveLabel: UILabel!
#IBOutlet weak var belowLabel: UILabel!
#IBOutlet weak var myButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.accessibilityElementsHidden = true
}
}
... and you get the following result on your device:
The button isn't taken into account as an accessible element and if it doesn't work in your code, it means that anything else prevents this correct operation. Add maybe the setAccessibleElement button property value to false according to your context ?
However, another solution could be defining the display of the desired accessibility objects in your view thanks to its accessibilityElements property taking away the buttons for instance (see Example 2 of this link): that will definitely work in addition to ordering all your elements.

Cannot edit brand new UITextField

I just made a new Swift project, put some text fields and a couple buttons in it, and when I start the app I cannot seem to be able to edit the text fields. Like the text fields are not taking any input. Any of them!
At the moment this is my only code. There should be nothing wrong with it. I literally ran the same code on another app and everything worked perfectly.
#IBOutlet weak var monthlyTF: UITextField!
#IBOutlet weak var annualL: UILabel!
var year = Double()
#IBAction func calculateAnnB(_ sender: UIButton) {
if let monthlyInc = Double(monthlyTF.text!){
year = 12*monthlyInc
annualL.text = "\(year)"
}else{
annualL.text = "That's not a number!"
}
}
There are other buttons and text fields in the app but I have not linked them yet because I didn't get to that part. I just wanted to test it piece by piece. Should I link them all first and then try?
It should be a really simple project. I don't know why I keep getting stuck on stupid technicalities like these.
Try what dinosaysrawr said, and also make sure User Interaction Enabled is set to true in the storyboard (under "View" in the Attributes Inspector).
Try deleting and reinstalling the UITextField in the storyboard
Edit: Also, make sure that the textField is enabled in the "Attributes Inspector" under "Control". If it's not enabled, it'll be grayed out and you can't tap on it to edit it

Need to use a NSTextView (or NSTextField) for a clickable URL

I'm given an arbitrary NSAttributedString (parsed from markdown, not that it matters here) which may contain URLs that I want to be clickable in a text field within an NSTableView cell. The requirements state that if the user clicks the URL, they be taken to it with the default browser. IF they click anywhere else in the cell, we have default behavior (displaying an additional info popup).
I'm attempting to use a NSTextView to display the content. However, clicking outside the URL but within the view selects the text and eats the mouse click. Making the view not selectable won't allow clicking the URL either. I also don't really want the text to be selectable but that's a minor side problem.
So... I decided to make my view controller an NSTextViewDelegate so I could use some of those callbacks. But my app crashes if I set the NSTextView's delegate property to 'self'. This happens even if I don't implement any of the functions, even though they are all optional.
I'm using Swift 3 and wonder if there's some bug or other issue there? The call stack appears to be sending a textView:willChangeSelectionFromCharacterRanges:toCharacterRanges: message even though it's not implemented. And incidentally, implementing that method isn't helping either.
Any help, or sample code in Swift 3 using the delegate protocol, would be greatly appreciated.
Here's the crash I get by simply setting the delegate property on NSTextView:
By request, here's the code that set's the delegate. Currently I just set it whenever the message changes. This can obviously be optimized but for now I just want to see it work.
var notification: SSNotification! {
didSet {
guard let notificationCellView = self.view as? SSNotificationCellView else { return }
notificationCellView.subjectLabel.stringValue = notification.subject
if let description = notification.message , description != "" {
let attrString = TSMarkdownParser.standard().attributedString(fromMarkdown: description)
notificationCellView.messageLabel.textStorage?.setAttributedString(attrString)
notificationCellView.messageLabel.isHidden = false
notificationCellView.messageLabel.delegate = self
} else {
notificationCellView.messageLabel.isHidden = true
}
}
}
I never did figure out why I was crashing but I was able to come up with a workaround. I was originally trying to make the view controller for the table cell which contained NSTextView be the delegate. I changed it so that the cell's view subclass itself was the delegate and all is well.
I don't get it but it works, so that's what matters.

SWRevealViewController conflict with UISlider

I currently facing an issue trying to delegate SWRevealViewController panGestureRecognizer method in one of my view.
When i slide my UISlider, the panGesture interfer and open the sidemenu instead to move my slider.
i tried to delegate the panGesture and it works well, but if i quit my view and go to an other, the pangesture is not functionnal anymore, and i can't reveal my sidemenu from my second view.
My code :
class Search : UIViewController, UIGestureRecognizerDelegate{
#IBOutlet weak var sliderprice: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
self.revealViewController().panGestureRecognizer().delegate = self
}
override func viewDidAppear(animated: Bool) {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if (touch.view == self.sliderprice){
return false
}
else{
return true
}
}
}
While this is an old question, I'll answer it anyways, maybe for someone coming from Google search it will be helpful.
After many hours of research and thinking, finally I was able to come up with multiple solutions to this problem.
The Lazy
This solution is not recommended, but working, so I decided to list it.
Before setting self as self.revealViewController().panGestureRecognizer()'s delegate, store the original self.revealViewController().panGestureRecognizer().delegate to a property, and when you leave the screen at viewWillDisappear(), set self.revealViewController().panGestureRecognizer().delegate back to the one you stored in viewDidLoad(). So in the end it gets back its original delegate. Tampering with delegates like this is never really recommended, but I said, it works.
The Nicer One
I consider this still not the best solution, but we are getting there. Find a class, a controller that you use in the whole application and gets called when you start the app. Here set the SWRevealViewController's panGestureDelegate to this class, and overwrite the gestureRecognizerShouldBeginmethod appropriately (see below).
The Best One - (in my opinion)
Now this is the best and most clear solution.
For the time being (May, 2018) the last commit to SWRevealViewController was in 2015.
Fork the original project ( https://github.com/John-Lluch/SWRevealViewController ) or simply copy the two necessary files (SWRevealViewController.m and SWRevealViewController.h) and place them into a separate folder to handle 3rd party libraries. Then you can remove SWRevealViewController from your Podfile. Don't forget to fix your imports for SWRevealViewController where you have it.
Now you are free to modify the files. What I suggest is the following.
Go to SWRevealViewController.m and implement the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
return ![touch.view isKindOfClass:UISlider.class];
}
If the touched view is a UISlider (or a custom class that inherits from UISlider) the gesture won't begin, meaning the pan gesture will no longer be conflicted with the UISlider's pan gesture.
I seriously hope I could help anyone out there as this problem was a pain in my back for quite some time now.

preferredFocusedView for tvOS

I am looking at Apple documentation here and not able to implement it in my swift project. I want to set a UIButton as the first focused in View when the app starts but cant figure out how.
I am primarily looking to do something like this:
override func preferredFocusedView .... but obviously that is not it
It's a property, not a function, so you need to do this in Swift:
override weak var preferredFocusedView: UIView? {
get {
return viewYouWantToFocus
}
}
In your UIViewController override get{} for preferredFocusedView to return your UIButton. Then make sure to call setNeedsFocusUpdate(). That should do it. (You can also implement set{} so that you can easily change which object receives focus, but again, remember to call setNeedsFocusUpdate() after changing it).