NSForegroundColorAttributeName for UILabel using NSAttributedString

后端 未结 1 1804
梦谈多话
梦谈多话 2021-01-25 15:45

I have NSAttributed string with links in it and I want to load it inside UILabel. I works fine, however all links are blue Color.

let string = NSMutableAttribute         


        
相关标签:
1条回答
  • 2021-01-25 16:32

    Ended up doing

    class AttributedTextLabel:UILabel {
    
    var attributedString:NSAttributedString?{
        didSet{
            guard let attributedString = attributedString else {
                return
            }
            let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
    
            mutableAttributedString.enumerateAttribute(NSLinkAttributeName, inRange: NSRange(location: 0, length: attributedString.length), options: NSAttributedStringEnumerationOptions.Reverse) {[weak self] (attribute, range, other) in
    
                if let url = attribute as? NSURL {
                    mutableAttributedString.removeAttribute(NSLinkAttributeName, range: range)
                    self?.links.append(Link(url: url, range: range))
                }
            }
            self.attributedText = mutableAttributedString
    
        }
    }
    
    struct Link {
        var url:NSURL
        var range:NSRange
    }
    
    var links:[Link] = []
    
    var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero
    
    private var textContentSize:CGSize {
        let textContainerWidth = frame.width - edgeInsets.left - edgeInsets.right
        let textContainerHeight = frame.height - edgeInsets.top - edgeInsets.bottom
    
        return CGSizeMake(textContainerWidth, textContainerHeight)
    }
    
    
    func characterIndexAtPoint(point:CGPoint) -> Int? {
        guard let attributedText = attributedText else {
            return nil
        }
    
        let layoutManager = NSLayoutManager()
        let textContainer = NSTextContainer(size: textContentSize)
    
        textContainer.lineFragmentPadding = 0.0
        textContainer.lineBreakMode = self.lineBreakMode
        textContainer.maximumNumberOfLines = self.numberOfLines
        layoutManager.addTextContainer(textContainer)
    
        let storage = NSTextStorage(attributedString: attributedText)
        storage.addLayoutManager(layoutManager)
        let adjustedPoint = CGPointMake(point.x-edgeInsets.left, point.y-edgeInsets.top)
    
        let characterIndex = layoutManager.characterIndexForPoint(point, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    
        return characterIndex
    }
    
    override func drawTextInRect(rect: CGRect) {
        return super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
    }
    
    private var selectedRange:NSRange?
    
    private var highligtedLink:Link? {
        didSet{
            let string = self.attributedText as! NSMutableAttributedString
            if let oldValue = oldValue {
                if let selectedLinkColor = NativeTextKit.TextAttributes.selectedLinkColor.value {
                    string.addAttributes([
                        NSForegroundColorAttributeName:selectedLinkColor
                        ], range: oldValue.range)
                }
            }
    
            if let highligtedLink = highligtedLink {
                if let selectedLinkColor = NativeTextKit.TextAttributes.selectedLinkColor.value {
                    string.addAttributes([
                        NSForegroundColorAttributeName:selectedLinkColor
                        ], range: highligtedLink.range)
                }
            }
    
            self.attributedText = string
        }
    }
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let touch = touches.first else {
            return
        }
        let char = characterIndexAtPoint(touch.locationInView(self))
        let string = self.attributedText as! NSMutableAttributedString
    
        highligtedLink = linkForTouch(touch)
    
        string.addAttributes([
            NSForegroundColorAttributeName:UIColor.brownColor()
            ], range: NSMakeRange(char!, 1))
    
        attributedText = string
    }
    
    func linkForTouch(touch:UITouch)->Link? {
        guard let attributedText = attributedText else {
            return nil
        }
        guard let characterIndex = characterIndexAtPoint(touch.locationInView(self)) else {
            return nil
        }
        return links.filter({NSLocationInRange(characterIndex, $0.range)}).first
    }
    
    override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
        highligtedLink = nil
    }
    
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let touch = touches.first else {
            return
        }
        if let highligtedLink = highligtedLink, let lastTouchedLink = linkForTouch(touch) where highligtedLink.url == lastTouchedLink.url {
            urlInteractionHandler?(textView: UITextView(), url:lastTouchedLink.url)
        }
    
    }
    
    /// Executed on link interaction
    var urlInteractionHandler:URLInteractionHandler?
    }
    

    Does the job, took a while to figure out. Because UILabel has its own link formatting ended up

    • Remove all links from attributed string once string is set
    • Add links and ranges to array
    • After link is selected use NSTextContainer to figure out what index the character was
    • Find range that character belongs to
    • Return link
    0 讨论(0)
提交回复
热议问题