ios - ios swift 的 FFLabel 库中的标签和属性颜色问题

标签 ios regex swift uilabel hashtag

我是 swift 的新手,在我的项目中我一直在尝试使用自定义标签,例如相关标签上的可点击“#”和“@”子字符串。我在 github 上找到了一个名为 FFLabel 的库。我已经为 FFLabel 做了一些测试。一切都很好,但我想为 '#' 和 '@' 子字符串使用不同的颜色。我试图对源代码进行一些更改。但是,它无法正常工作。此外,“#”子字符串不起作用(不可点击)。我猜源代码中存在正则表达式问题。 以下是 FFLabel 的源代码:

import UIKit

@objc
public protocol FFLabelDelegate: NSObjectProtocol {
    optional func labelDidSelectedLinkText(label: FFLabel, text: String)
}

public class FFLabel: UILabel {

public var linkTextColor = UIColor(red: 0, green: 63.0/255.0, blue: 121.0/255.0, alpha: 1.0)
public weak var labelDelegate: FFLabelDelegate?

// MARK: - override properties
override public var text: String? {
    didSet {
        updateTextStorage()
    }
}

override public var attributedText: NSAttributedString? {
    didSet {
        updateTextStorage()
    }
}

override public var font: UIFont! {
    didSet {
        updateTextStorage()
    }
}

override public var textColor: UIColor! {
    didSet {
        updateTextStorage()
    }
}

// MARK: - upadte text storage and redraw text
private func updateTextStorage() {
    if attributedText == nil {
        return
    }

    let attrStringM = addLineBreak(attributedText!)
    regexLinkRanges(attrStringM)
    addLinkAttribute(attrStringM)

    textStorage.setAttributedString(attrStringM)

    setNeedsDisplay()
}

/// add link attribute
private func addLinkAttribute(attrStringM: NSMutableAttributedString) {
    var range = NSRange(location: 0, length: 0)
    var attributes = attrStringM.attributesAtIndex(0, effectiveRange: &range)

    attributes[NSFontAttributeName] = font!
    attributes[NSForegroundColorAttributeName] = textColor
    attrStringM.addAttributes(attributes, range: range)

    attributes[NSForegroundColorAttributeName] = linkTextColor

    for r in linkRanges {
        attrStringM.setAttributes(attributes, range: r)
    }
}

/// use regex check all link ranges
private let patterns = ["[a-zA-Z]*://[a-zA-Z0-9/\\.]*", "#.*?#", "@[\\u4e00-\\u9fa5a-zA-Z0-9_-]*"]
private func regexLinkRanges(attrString: NSAttributedString) {
    linkRanges.removeAll()
    let regexRange = NSRange(location: 0, length: count(attrString.string))

    for pattern in patterns {
        let regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.DotMatchesLineSeparators, error: nil)
        let results = regex?.matchesInString(attrString.string, options: NSMatchingOptions(rawValue: 0), range: regexRange)

        if let results = results {
            for r in results {
                linkRanges.append(r.rangeAtIndex(0))
            }
        }
    }
}

/// add line break mode
private func addLineBreak(attrString: NSAttributedString) -> NSMutableAttributedString {
    let attrStringM = NSMutableAttributedString(attributedString: attrString)

    var range = NSRange(location: 0, length: 0)
    var attributes = attrStringM.attributesAtIndex(0, effectiveRange: &range)
    var paragraphStyle = attributes[NSParagraphStyleAttributeName] as? NSMutableParagraphStyle

    if paragraphStyle != nil {
        paragraphStyle!.lineBreakMode = NSLineBreakMode.ByWordWrapping
    } else {
        // iOS 8.0 can not get the paragraphStyle directly
        paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle!.lineBreakMode = NSLineBreakMode.ByWordWrapping
        attributes[NSParagraphStyleAttributeName] = paragraphStyle

        attrStringM.setAttributes(attributes, range: range)
    }

    return attrStringM
}

public override func drawTextInRect(rect: CGRect) {
    let range = glyphsRange()
    let offset = glyphsOffset(range)

    layoutManager.drawBackgroundForGlyphRange(range, atPoint: offset)
    layoutManager.drawGlyphsForGlyphRange(range, atPoint: CGPointZero)
}

private func glyphsRange() -> NSRange {
    return NSRange(location: 0, length: textStorage.length)
}

private func glyphsOffset(range: NSRange) -> CGPoint {
    let rect = layoutManager.boundingRectForGlyphRange(range, inTextContainer: textContainer)
    let height = (bounds.height - rect.height) * 0.5

    return CGPoint(x: 0, y: height)
}

// MARK: - touch events

public override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    let location = (touches.first as! UITouch).locationInView(self)

    selectedRange = linkRangeAtLocation(location)
    modifySelectedAttribute(true)
}

public override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
    let location = (touches.first as! UITouch).locationInView(self)

    if let range = linkRangeAtLocation(location) {
        if !(range.location == selectedRange?.location && range.length == selectedRange?.length) {
            modifySelectedAttribute(false)
            selectedRange = range
            modifySelectedAttribute(true)
        }
    } else {
        modifySelectedAttribute(false)
    }
}


public override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    if selectedRange != nil {
        let text = (textStorage.string as NSString).substringWithRange(selectedRange!)
        labelDelegate?.labelDidSelectedLinkText!(self, text: text)

        let when = dispatch_time(DISPATCH_TIME_NOW, Int64(0.25 * Double(NSEC_PER_SEC)))
        dispatch_after(when, dispatch_get_main_queue()) {
            self.modifySelectedAttribute(false)
        }
    }
}

public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
    modifySelectedAttribute(false)

}

private func modifySelectedAttribute(isSet: Bool) {
    if selectedRange == nil {
        return
    }

    var attributes = textStorage.attributesAtIndex(0, effectiveRange: nil)
    attributes[NSForegroundColorAttributeName] = linkTextColor
    let range = selectedRange!

    textStorage.addAttributes(attributes, range: range)

    setNeedsDisplay()
}

private func linkRangeAtLocation(location: CGPoint) -> NSRange? {
    if textStorage.length == 0 {
        return nil
    }

    let offset = glyphsOffset(glyphsRange())
    let point = CGPoint(x: offset.x + location.x, y: offset.y + location.y)
    let index = layoutManager.glyphIndexForPoint(point, inTextContainer: textContainer)

    for r in linkRanges {
        if index >= r.location && index <= r.location + r.length {
            return r
        }
    }

    return nil
}

// MARK: - init functions
override public init(frame: CGRect) {
    super.init(frame: frame)

    prepareLabel()
}

required public init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    prepareLabel()
}

public override func layoutSubviews() {
    super.layoutSubviews()

    textContainer.size = bounds.size
}

private func prepareLabel() {
    textStorage.addLayoutManager(layoutManager)
    layoutManager.addTextContainer(textContainer)

    textContainer.lineFragmentPadding = 0

    userInteractionEnabled = true
}

// MARK: lazy properties
private lazy var linkRanges = [NSRange]()
private var selectedRange: NSRange?
private lazy var textStorage = NSTextStorage()
private lazy var layoutManager = NSLayoutManager()
private lazy var textContainer = NSTextContainer()
}

谢谢你的回答

国王问好

注意:我通过将主题标签表达式更改为 #[\\u4e00-\\u9fa5a-zA-Z0-9_-]*< 解决了主题标签可点击问题(正则表达式问题)/。但其余问题仍在继续。

最佳答案

你也可以试试 ActiveLabel.swift 这是 UILabel 的替代品,支持用 Swift 编写的标签 (#)、提及 (@) 和 URL (http://)。 p>

也许这正是您要寻找的。给用户名、主题标签和链接不同的颜色就这么简单:

label.textColor = .blackColor()
label.hashtagColor = .blueColor()
label.mentionColor = .greenColor()
label.URLColor = .redColor()

免责声明:我是图书馆的作者。

关于ios - ios swift 的 FFLabel 库中的标签和属性颜色问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32033362/

相关文章:

c# - 如何编写多行 RegEx 表达式

python - 多行 python 正则表达式

ios - 从编程 uibutton 属性访问全局变量

ios - Swift-获取自计时器启动以来经过了多少秒/毫秒

Javascript - 在字符串中屏蔽帐号

ios - 如何检索另一个 firebase 子项中的子项(数组)

ios - Collectionview 和分页

swift - 快速点击 Post Method api 时出现问题

ios - 如何识别哪个按钮点击了 UICollectionView iOS 的哪个部分?

ios - 通过 watchConnectivity 发送一个 NSObject