Swift 3 : How do you wrap content in a tableviewcell with multiple labels? - ios

I've been trying to figure this out for a while. I have set constraints for each label with background color set. I set each label's line break to word wrap, but that still doesn't work. What I'm looking for is a label wrap like word wrap whether or not that exists. Thanks.

Here is a slightly different approach. You can customize the appearance of each tagged word with an attributed string. This has some limitations but depending on your requirements it could be a good fit for you. The below code is an example pointing you in the correct direction, however you still might need to write additional code for correctly wrapping the spaces or recognizing touch events.
let tags = ["Outdoors", "Working", "Learning"].map { " \($0) " }
let text = tags.joined(separator: " ")
let ranges = tags.compactMap { text.range(of: $0) }
let attributedText = NSMutableAttributedString(string: text)
for range in ranges {
attributedText.addAttributes([.backgroundColor: UIColor.green], range: NSRange(range, in: text))
}
textView.attributedText = attributedText

Related

Swift how to add background color just around text in a label?

How do you add a background color around each variable inside the interests variable? Just around text not the spaces.
var interests = "\(int01) \(int02) \(int03) \(int04) \(int05) \(int06)"
I want it to look like this:
You can use a regex to find anything but white spaces, use a while loop to find its occurrences in a string and use those ranges to change the background color of an attributed string:
Swift 4
let mutable = NSMutableAttributedString(string: interests)
var startIndex = interests.startIndex
while let range = interests.range(of: "\\S+", options: .regularExpression, range: startIndex..<interests.endIndex) {
mutable.addAttribute(.backgroundColor, value: UIColor.cyan, range: NSRange(range, in: interests))
startIndex = range.upperBound
}
label.attributedText = mutable
Note: If you would like to add space around your text you can change your regex to " \\S+ " and don't forget to add spaces at the begin and at the end of your original interests string.
let attributedString = NSMutableAttributedString(string: interests)
attributedString.addAttribute(NSBackgroundColorAttributeName, value: yourColor, range: NSMakeRange(startOfWord, lengthOfWord))
What you can do is use a UICollectionView with cells that have labels that span the entire cell. Each cell then corresponds to individual words. That way you can set either the label or the cell background color to whatever you want.
For ease of implementation, I would turn your interests variable into an array of strings. That way you can easily count the number of cells you need and give each cell the right string.

Programatically change some words' color in an UITextView

So I have an UITextView with some text and I want everytime the user inputs, for example, the word "while", after it has been written it should change into purple. Also, I have another UITextView just to display content, no user interaction enabled, and I want that everytime the view appears, all the "while" words to also be purple. How do I do this? Can you help me, please? Thank you!
Here's what I've tried so far:
let initialText = textView.text!
let string_to_color = "while"
let range = (initialText as NSString).range(of: string_to_color)
let attribute = NSMutableAttributedString.init(string: initialText)
attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.purple, range: range)
textView.attributedText = attribute
But it colors only the first word. This is for the user interaction disabled text field. I haven't figured out how to do it for the text field that contains the words that the user inputs.
You are not getting the range correctly, here is an example of using attributed strings in Swift 3.0:
// get initial text as a String type (you will get this from your textview)
let initialText = "Swift Attributed String"
// create an attribute for the text color, I chose blue color
let myAttribute = [ NSForegroundColorAttributeName: UIColor.blue ]
// create the attributed string and add the blue color attribute
let myString = NSMutableAttributedString(string: initialText, attributes: myAttribute )
// range starting at location 6 with a lenth of 10: "Attributed"
var myRange = NSRange(location: 6, length: 10)
// OR get range of specific string in initialText
let newRange = (initialText as NSString).range(of: "Attributed")
// change the range of the word "Attributed" to have red text color
myString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: newRange)
// create another attribute for highlighting
let anotherAttribute = [ NSBackgroundColorAttributeName: UIColor.yellow ]
// set the range of the "Attributed" part of the string to a yellow highlight
myString.addAttributes(anotherAttribute, range: newRange)
You can use this strategy to do whatever formatting you need to do with your string. Just make sure that the range that you are getting is correct.

Get color changed words of attributed string

I have a UITextView that allows to select words of the text by tapping it. If it is tapped the word is highlighted in color by changing the NSForegroundColor attribute.
Tapping it again deselects it by changing the color back to the textcolor.
Now I would need to know all selected words in the UITextView.
The first idea was to remove everything that is a special character and split the text at space. Then check if the color attribute is equals to the selected/highlighted color of each seperate word.
But attributed string doesn't allow to split at a character or remove components. Neither does NSAttributedString.
Second idea was to save the ranges of the highlighted parts in an array and iterate over it to get the highlighted parts. But this seems a bit too complicated for me, especially as I need the correct order of the words as they appear which is not guaranteed with an array, add/remove on each tap
(For example let's say the text is: "This is a test"
Tap this -> index 0
Tap test -> index 1
Tap this -> test becomes index 0
Tap this -> this becomes index 1
then the order is not good anymore.
I already figured out how to get the color of an attributed string. That's not the problem.
How can I iterate over the attributed string and figure out the words that have changed color or what is the best way to solve this problem?
Thank you!
Greetings
You can iterate over the attributed string looking for the color attribute.
The follow code demonstrates how:
// This generates a test attributed string.
// You actually want the attributedText property of your text view
let str = NSMutableAttributedString(string: "This is a test of the following code")
str.addAttributes([NSForegroundColorAttributeName:UIColor.red], range: NSMakeRange(0, 4))
str.addAttributes([NSForegroundColorAttributeName:UIColor.red], range: NSMakeRange(8, 1))
str.addAttributes([NSForegroundColorAttributeName:UIColor.red], range: NSMakeRange(15, 2))
print(str)
The above prints:
This{
NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
} is {
}a{
NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
} test {
}of{
NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
} the following code{
}
This code processes the attributed string. Any range of text formatted with a foreground color will be put into the words array.
var words = [String]()
str.enumerateAttribute(NSForegroundColorAttributeName, in: NSMakeRange(0, str.length), options: []) { (value, range, stop) in
if value != nil {
let word = str.attributedSubstring(from: range).string
words.append(word)
}
}
print(words)
This prints:
["This", "a", "of"]
I can suggest you to create some kind of storage for selected ranges, then based on this range you can customize look of this words, not the other way. It will allow you to access selected words every time without checking attributes of whole text.
While I agree with Piotr that you should store the Ranges, to answer your question:
attributedString.enumerateAttributes(in: NSMakeRange(0, attributedString.length), options: []) { attributes, range, _ in
if let color = attributes[NSForegroundColorAttributeName] as? UIColor,
color == YOUR_HIGHLIGHT_COLOR {
let nString = attributedString.string as NSString
let word = nString.substring(with: range)
// Do what you want with the word
}
}

Swift - Changing font size inside label

I have a label where there is text which should be bold and with another font size. Is there any possibility to do it like the line break ("Hello \n World!") with a command or do I have to make another label for this?
Thanks!
Look at the API for NSAttributedString -- it allows you to create a string that specifies portions of the string that should be styled with specific text styles and/or fonts. The resulting object can be used instead of a plain string with UILabel (and other UI elements) by setting the label's attributedText property instead of the usual text property.
To make just the word "bold" appear in 18 point bold, try something like the following:
var label = UILabel()
let bigBoldFont = UIFont.boldSystemFontOfSize(18.0)
var attrString = NSMutableAttributedString(string: "This text is bold.")
attrString.addAttribute(kCTFontAttributeName, value: bigBoldFont, range: NSMakeRange(13, 4))
label.attributedText = attrString
The range specified determines the portion of the string to which the named attributed (in this case, the font) should be applied. And note that the parameters to NSMakeRange are the starting character position and the length of the range.

UITextField - Remove Ellipses on Text Overflow

I have a UItextfieldthat holds a person's middle name. I only want it to display the first initial, which it does, but i want it to hold their entire name. It's only large enough to show the one initial, but it adds that ellipses (...) after the letter.Is it possible to remove those when a uitextfield overflows? I haven't found anything online regarding someone with the same issue.
Thankyou for your help
I do see this truncation when the text field resigns first responder. I fixed this by setting the lineBreakMode in the NSParagraphStyle attribute to .byClipping. I happened to be using a subclass of UITextField so I overrode resignFirstResponder() to do this. My textField starts out empty so there is no attributedString to start with in viewDidLoad.
override func resignFirstResponder() -> Bool {
guard let newAttributedText = (attributedText?.mutableCopy() as? NSMutableAttributedString) else {
return super.resignFirstResponder()
}
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byClipping
newAttributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedText?.length ?? 0))
attributedText = newAttributedText
return super.resignFirstResponder()
}
This might not work if you set the text in code. In that case, you might want to set the lineBreak mode in a common function that you call from both the override of resignFirstResponder() and after setting the text in code. You could make a set(text: String?) function and call the common function from there.
A UITextField shouldn't be truncating the text (because you can usually scroll/select that UI element).
A UILabel will truncate by default, you can set it to clip instead.

Resources