UITextView link detect bug with iOS 8.3 - hyperlink

I put into a non editable UITextView only one URL as string in order to make a link that work great until iOS8.3, set is :
myTextView.text = aLink;
First set is ok nice and link work well when touch, but another set will failed, text
is ok with the new url text, link is well detected with blue color but touch will always responds with first url as if UITextView be not uptaded.
Does I have to set another thing into UITextView ?

One solution:
Just after setting new string (with a link) I remove detection property:
self.myTextView.dataDetectorTypes = UIDataDetectorTypeNone;
and trig after 0.1 second a new setting as:
self.myTextView.dataDetectorTypes = UIDataDetectorTypeLink;
Works fine.

I found a slightly alternative way that seems to fix it in my case. It avoids using a timer so the fix seems instant.
self.textviewText.dataDetectorTypes = UIDataDetectorTypes.init(rawValue: 0)
self.textviewText.text = newstring
self.textviewText.dataDetectorTypes = .link

Related

UILabel wrong word wrap in iOS 11

I have problem with application using XIBs without autolayout. I don't know if this is important information.
I have UILabel with 2 lines using word wrap. In iOS 10 word wrap was working correctly, and first line contained one word + special character, for example ampersand. Example:
Then on iOS 11 word wrap is working somehow wrong and puts ampresand to the second line:
This is problematic as longer words, that normally fitted on second line now are not being shown correctly. Any idea what has changed? I know about safeArea but it doesn't look like reason. Any ideas how to move that ampersand to the top where is plenty of space for it?
Rest of the settings:
This is a change by Apple to prevent widowed lines. From a design perspective, it is preferred to avoid having a single word on a line of text. UILabel now breaks the line in a way that the second line of text always has at least 2 words on it.
See the answer below for an option to disable it.
Also here's a good article about "widowed" and "orphaned" text.
Since iOS 14 you can use lineBreakStrategy property of UILabel instance to control this behavior.
Available values are:
NSParagraphStyle.LineBreakStrategy() // none
NSParagraphStyle.LineBreakStrategy.pushOut
NSParagraphStyle.LineBreakStrategy.hangulWordPriority
NSParagraphStyle.LineBreakStrategy.standard
To disable this behavior using Swift:
if #available(iOS 14.0, *) {
label.lineBreakStrategy = []
}
// Alternatives
// label.lineBreakStrategy = NSParagraphStyle.LineBreakStrategy()
// label.lineBreakStrategy = .init(rawValue: 0)
// label.lineBreakStrategy = .init()
To make it work on lower iOS versions, you can use NSAttributedString:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakStrategy = []
let attributedString = NSAttributedString(string: "Your text here", attributes: [
.paragraphStyle: paragraphStyle
])
let label = UILabel()
label.attributedText = attributedString
Objective-C:
if (#available(iOS 14.0, *)) {
label.lineBreakStrategy = NSLineBreakStrategyNone;
}
Launching the app with the arguments -NSAllowsDefaultLineBreakStrategy NO (an undocumented defaults setting) seems to force back to the old behavior. Alternatively, you can set NSAllowsDefaultLineBreakStrategy to NO in NSUserDefaults at startup (Apple registers a default of YES for that value when UILabel or the string drawing code is initialized, it appears, so you would need to register an overriding value after that, or insert it into the NSArgumentDomain, or just set the default persistently).
Apple may consider that private API and reject apps that use it; I'm not sure. I have not tried this in a shipping app. However, it does work in quick testing -- saw the setting in NSUserDefaults and found changing it altered the behavior.
This is not really an answer, but I want to add an illustration of how it is a general problem, not at all related to ampersands.
Both of these UILabels have identical width constraints, and the text is almost identical. But the second has the word wrap I would expect. The first is incorrect, the "about" can clearly stay on the first line.
A bit of a hack but you can add some zero width spaces to the end of the string to restore the old behaviour, without affecting the layout of the string otherwise that you'd get from normal spaces:
let zeroWidthSpace: Character = "\u{200B}"
let spacingForWordWrapping = String(repeating: zeroWidthSpace, count: 6)
label.text = "oneText & two" + spacingForWordWrapping
It seems that replacing the space before the ampersand with a non-breaking space (U+00A0) keeps the ampersand on the same line. Depending on how you are generating the text for the label, this might not be easy to automate (maybe you really do need the ampersand to be on the second line in some cases).
An option may be to use a UITextView instead -- that does not seem to have this behavior. If you set the NSTextContainer.lineFragmentPadding to 0, the textContainerInset to UIEdgeInsetsZero, and turn off all scrolling (scrollEnabled, bounces, scroll indicators, etc.) it will display similarly to a UILabel, though not with as much constraint flexibility. It's not a drop-in replacement, but in some situations it's acceptable.
As a simple (hacky) workaround, you can often get the correct behaviour with a UILabel by adding spaces at the end of your text. Using your example:
Wraps the new (undesired) way:
"oneText & two."
Wraps the old way:
"oneText & two. " (note the 2 extra spaces at the end of the string)
The obvious downside is if those extra spaces get forced to a new line by themselves, but for something simple like a title it's often enough.

UIsegmentedControl title appearance

HI, i set my uisegmentedcontrol's width manually, but when the width gets too small, the words becomes ...
Is that anyway that it won't behave in this way? Instead, i just want to show the text just like the picture shown below.
I'd suggest changing your design here and going for a different approach.
The design that you seem to want makes readability pretty much impossible.
Plus, what happens if I'm using your app and add another "Active Project". What happens if I have 10 active projects?
Take the fact that the UI does not work as a sign that you are using the wrong UI for the problem you are trying solve.
I'd suggest possibly just have the current project title here with a button to maybe present a list of projects to switch to... or something.
The text has been truncated. If you want it to fit your segment, you need to update the segment control size based on the text length. If you just want to get rid of truncation, you can use the following snippet. However, it's not recommended, as later Apple might change the UISegmentControl hierarchy.
for item in segmentedControl.subviews {
for subview in item.subviews {
if subview.isKind(of: UILabel.self) {
let _label = subview as! UILabel
_label.numberOfLines = 0
_label.lineBreakMode = .byWordWrapping
}
}
}

iOS - rectangle appears after tapping button

I have two UIButtons and a strange rectangle appears when tapping one of them. I don't know why. I set the images as background images on the button and it worked fine until now.
What I changed now is that I set each button isSelected property and before I did not
Like this:
thumbsDownButton.isSelected = true
thumbsUpButton.isSelected = false
Obviously what I want is for that rectangle to go away
The style was set to System. Setting it to Custom fixed the problem and the rectangle is not appearing anymore. I set it from the xib (if it makes any difference)
Add this Code
thumbsDownButton.tintColor = UIColor.white
It seems your frame may be calculated in the wrong way.
Try to use this one to check it.
button.clipToBounds = true
Please share more code related of initializing and setting up the frame of your view.

iOS Instagram Tags Text Layout? How Is It Done?

I've always been curious how instagram gets it's tags to layout fluidly and wanted to know what goes behind this functionality? To me, it seems like they're using UIButton's and just calculating placement of the buttons line by line? Is that right? Or is there a simpler method of doing it without having to manual calculate the placement of each tag line by line / or side by side given that multiple tag's width's fit together?
Or, are they using NSAttributedStrings?
Any ideas?
Here's an example of the layout I'm referring to.
Well, I'm currently working on NSAttributedString and UITextView to get all this working, and my current code can do it. So I'm going to explain a possible version of doing this.
Here are the tips and big steps, with iOS7:
NSAttributedString, in iOS7 has a new attribute : NSLinkAttributeName. You can add it to your NSAttributedString, and with a detection method (combining: how to make #key and #key clickable in IOS7 and Get word from tap in UITextView) you can manage it. That's for the general way of doing it. For previous version, you'll have to do a little more of work, but keeping this general idea.
But since, it only for hashtags, since you can detect the world behind the tap, you can do this: Detect what word has been tapped (see previous links), detect if this word has an hashtag, and if yes, you can do whatever action you want. You can play with a NSRegularExpression to know where are the hashtags, and know when to "color" them.
Here is a simplified way to do it using UITextView and NSAttributedString:
Use regular expressions to get character ranges for links.
Add link styling to the matched ranges.
Add custom attribute to range that references tap action to execute.
On touch, if index of character is inside a matched range, change the link formatting to highlight it and run the associated tap action.
How to get index of character at location in iOS7:
- (NSUInteger) getCharIndexAt: (CGPoint) location
{
NSLayoutManager *layoutManager = self.layoutManager;
NSUInteger characterIndex = NSNotFound;
characterIndex = [layoutManager characterIndexForPoint:location inTextContainer:self.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
return characterIndex;
}
Also check out WWDC 2013 intro to TextKit Demo
ios7 text utilities

How to push key value into UITextView from custom keyboard in iOS

I am building a virtual keyboard for a UITextView. All works well, I can display the keyboard and remove the keyboard as well as display the keystrokes. I am using the "stringByReplacingCharactersInRange: withString" method to place the new keystroke into the text view. This works but since it is replacing the string each time, the entry point goes to the end of the string. This all makes sense, but is not very usable. I can fix this by doing checks in my code but there might be something better...
So my question is this; Is there a way to push the new key to the UITextView directly without modifying the UITextView's text property's string manually? The manual states that I will have to manage the target/action myself, but I was wondering if I've missed an action to call. It seems that the Apple Default keyboard must have a slick way of punching the keystroke into the object without the obvious number of checks that I will have to do to make the typing smooth.
Any help will be greatly appreciated.
Thanks,
Rob
Well, I didn't get any responses. So I figured out a way to do it so it is reasonable and simple. It's a matter of tracking the selected range. So when I wish to Backspace I use this technique.
NSRange currentRange = self.myTextView.selectedRange;
if (currentRange.length == 0) {
currentRange.location--;
currentRange.length++;
}
self.myTextView.text = [self.myTextView.text stringByReplacingCharactersInRange:currentRange withString:[NSString string]];
currentRange.length = 0;
self.myTextView.selectedRange = currentRange;
If I need to type a character this works.
NSRange currentRange = self.myTextView.selectedRange;
self.myTextView.text = [self.myTextView.text stringByReplacingCharactersInRange:currentRange withString:someCharacter];
currentRange.location++;
currentRange.length = 0;
self.myTextView.selectedRange = currentRange;
If anyone has a better way, I'd like to hear it.
Thanks,
Rob

Resources