I\'m making an iOS app that has a UITextView. When closing a parenthesis in that UITextView, I want to highlight to the user which opening parenthesis it pairs to. So far I\
There's a much easier way to do this, which you can see in this answer: Animate text change in UILabel.
You just have to set the label.attributedText
property instead:
UIView.transition(
with: label,
duration: 0.15,
options: .transitionCrossDissolve,
animations: {
label.attributedText = NSAttributedString(string: text, attributes: attributes)
},
completion: nil
)
I achieved what I wanted to by getting the frames for the actual parentheses and creating new UILabels on top of my UITextView and animating those labels.
@IBAction func didPressClosingParentheses(sender: AnyObject) {
inputTextView.text = inputTextView.text + ")"
var count = 1
let currentString = inputTextView.attributedText.string
let characterArray = Array(currentString)
let closingIndex = characterArray.count - 1
for i in reverse(0...closingIndex-1) {
if characterArray[i] == "(" {
count--
}
else if characterArray[i] == ")" {
count++
}
if count == 0 {
let startingIndex = i
let openingRange = NSMakeRange(startingIndex, 1)
let closingRange = NSMakeRange(closingIndex, 1)
var openingFrame = inputTextView.layoutManager.boundingRectForGlyphRange(openingRange, inTextContainer: inputTextView.textContainer)
openingFrame.origin.y += inputTextView.textContainerInset.top
var openingLabel = UILabel(frame: openingFrame)
openingLabel.text = "("
openingLabel.font = UIFont(name: "HelveticaNeue-Thin", size: 28)
openingLabel.textColor = whiteishColor
openingLabel.backgroundColor = bluishColor
var closingFrame = inputTextView.layoutManager.boundingRectForGlyphRange(closingRange, inTextContainer: inputTextView.textContainer)
closingFrame.origin.y += inputTextView.textContainerInset.top
var closingLabel = UILabel(frame: closingFrame)
closingLabel.text = ")"
closingLabel.font = UIFont(name: "HelveticaNeue-Thin", size: 28)
closingLabel.textColor = whiteishColor
closingLabel.backgroundColor = bluishColor
inputTextView.addSubview(openingLabel)
inputTextView.addSubview(closingLabel)
UIView.animateWithDuration(0.4, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.8, options: nil, animations: {
openingLabel.transform = CGAffineTransformMakeScale(1.25, 1.25)
closingLabel.transform = CGAffineTransformMakeScale(1.25, 1.25)
}, nil)
UIView.animateWithDuration(0.4, delay: 0.2, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.8, options: nil, animations: {
openingLabel.transform = CGAffineTransformMakeScale(1.0, 1.0)
closingLabel.transform = CGAffineTransformMakeScale(1.0, 1.0)
}, nil)
UIView.animateWithDuration(0.25, delay: 0.4, options: nil, animations: {
openingLabel.alpha = 0
closingLabel.alpha = 0
}, completion: { finished in
openingLabel.removeFromSuperview()
closingLabel.removeFromSuperview()
})
break
}
}
}
Another solution which works for me (Swift 4) is to generate multiple attributed strings, assign one to label and then replace the content (attributedText
) inside transition animation block. For example:
// MARK: Extension util which generates NSAttributedString by text,font,color,backgroundColor
extension NSAttributedString {
class func generate(from text: String, font: UIFont = UIFont.systemFont(ofSize: 16), color: UIColor = .black, backgroundColor: UIColor = .clear) -> NSAttributedString {
let atts: [NSAttributedStringKey : Any] = [.foregroundColor : color, .font : font, .backgroundColor : backgroundColor]
return NSAttributedString(string: text, attributes: atts)
}
}
// MARK: Sentence
let string1 = "Hi, i'm "
let string2 = "Daniel"
// MARK: Generate highlighted string
let prefixAttString = NSAttributedString.generate(from: string1)
let highlightedSuffixAttString = NSAttributedString.generate(from: string2, backgroundColor: .red)
let highlightedString = NSMutableAttributedString()
highlightedString.append(prefixAttString)
highlightedString.append(highlightedSuffixAttString)
// MARK: Generate regular string (Same prefix, different suffix)enter image description here
let regularSuffixAttString = NSAttributedString.generate(from: string2)
let regularString = NSMutableAttributedString()
regularString.append(prefixAttString)
regularString.append(regularSuffixAttString)
self.view.addSubview(label)
label.attributedText = regularString
// UIViewAnimationOptions.transitionCrossDissolve is necessary.
UIView.transition(with: self.label, duration: 4, options: [.transitionCrossDissolve], animations: {
self.label.attributedText = highlightedString
}, completion: nil)
}
Don't forget to use .transitionCrossDissolve
in the animation options.