Animate a change in part of an NSMutableAttributedString

前端 未结 3 2035
一生所求
一生所求 2021-01-13 04:27

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\

相关标签:
3条回答
  • 2021-01-13 05:01

    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
    )
    
    0 讨论(0)
  • 2021-01-13 05:11

    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
            }
        }
    
    }
    
    0 讨论(0)
  • 2021-01-13 05:21

    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.

    0 讨论(0)
提交回复
热议问题