如何将 ASTextNode 与 TTTAttributedLabel 一起使用

Posted

技术标签:

【中文标题】如何将 ASTextNode 与 TTTAttributedLabel 一起使用【英文标题】:How can I use ASTextNode with TTTAttributedLabel 【发布时间】:2015-06-16 11:24:23 【问题描述】:

我已经构建了一个ASCellNode,它运行良好。然而,当我使用传统的UICollectionViewCell 时,我使用了带有链接的TTTAttributedLabel

我不知道应该如何使用AsyncDisplayKit 复制它

我可以将TTTAttributedLabel 中的attriubtedText 分配给ASTextNode,但它当然不会保留链接。我怎么能有效地做到这一点。下面以我的ASCellNode为例。

protocol EmailSentDelegator : class 
    func callSegueFromCell(data object: JSON)


class EmailCellNode: ASCellNode, TTTAttributedLabelDelegate 

    let cardHeaderNode: ASTextNode
    var frameSetOrNil: FrameSet?

    init(mailData: JSON) 
        // Create Nodes
        cardHeaderNode = ASTextNode()

        super.init()

        // Set Up Nodes

        cardHeaderNode.attributedString = createAttributedLabel(mailData, self).attributedText

        // Build Hierarchy
        addSubnode(cardHeaderNode)
    

    override func calculateSizeThatFits(constrainedSize: CGSize) -> CGSize 
        var cardSize = CGSizeZero
        cardSize.width = UIScreen.mainScreen().bounds.size.width - 16

        // Measure subnodes
        let cardheaderSize = cardHeaderNode.measure(CGSizeMake(cardSize.width - 56, constrainedSize.height))
        cardSize.height = max(cardheaderSize.height,40) + subjectLabelSize.height + timeStampSize.height + emailAbstractSize.height  + 30

        // Calculate frames
        frameSetOrNil = FrameSet(node: self, calculatedSize: cardSize)
        return cardSize
    

    override func layout() 
        if let frames = frameSetOrNil 
            cardHeaderNode.frame = frames.cardHeaderFrame
        
    

    func attributedLabel(label: TTTAttributedLabel!, didSelectLinkWithTransitInformation components: [NSObject : AnyObject]!) 
            self.delegate.callSegueFromCell(data: mailData)
    

    func createAttributedLabel(mailData: JSON, cell: EmailCellNode) -> TTTAttributedLabel
        let senderName = mailData["From"]["Name"].string!
        var recipients:[String] = []

        for (key: String, subJson: JSON) in mailData["To"] 
            if let recipientName = subJson["Name"].string 
                recipients.append(recipientName)
            
        
        var cardHeader = TTTAttributedLabel()
        cardHeader.setText("")
        cardHeader.delegate = cell
        cardHeader.userInteractionEnabled = true

        // Add sender to attributed string and save range

        var attString = NSMutableAttributedString(string: "\(senderName) to")
        let senderDictionary:[String:String] = ["sender": senderName]
        let rangeSender : NSRange = (attString.string as NSString).rangeOfString(senderName)

        // Check if recipients is nil and add undisclosed recipients
        if recipients.count == 0 
            attString.appendAttributedString(NSAttributedString(string: " undisclosed recipients"))
            let rangeUndisclosed : NSRange = (attString.string as NSString).rangeOfString("undisclosed recipients")
            attString.addAttribute(NSFontAttributeName, value: UIFont(name: "SourceSansPro-Semibold", size: 14)!, range: rangeUndisclosed)
            attString.addAttribute(NSForegroundColorAttributeName, value: UIColor.grayColor(), range: rangeUndisclosed)
         else 

            // Add recipients (first 5) to attributed string and save ranges for each

            var index = 0
            for recipient in recipients 
                if (index == 0) 
                    attString.appendAttributedString(NSAttributedString(string: " \(recipient)"))
                 else if (index == 5)
                    attString.appendAttributedString(NSAttributedString(string: ", and \(recipients.count - index) other"))
                    break
                 else 
                    attString.appendAttributedString(NSAttributedString(string: ", \(recipient)"))
                
                index = index + 1
            
        
        cardHeader.attributedText = attString

        // Adding recipients and sender links with recipient object to TTTAttributedLabel
        cardHeader.addLinkToTransitInformation(senderDictionary, withRange: rangeSender)

        if recipients.count != 0 
            var index = 0
            var position = senderName.length + 2
            for recipient in recipients 
                let recipientDictionary:[String: AnyObject] = ["recipient": recipient,"index": index ]
                let rangeRecipient : NSRange = (attString.string as NSString).rangeOfString(recipient, options: nil, range: NSMakeRange(position, attString.length-position))
                cardHeader.addLinkToTransitInformation(recipientDictionary, withRange: rangeRecipient)
                index = index + 1
                if (index == 5) 
                    let recipientsDictionary:[String: AnyObject] = ["recipients": recipients]
                    let rangeRecipients : NSRange = (attString.string as NSString).rangeOfString("and \(recipients.count - index) other")
                    cardHeader.addLinkToTransitInformation(recipientsDictionary, withRange: rangeRecipients)
                
                position = position + rangeRecipient.length
            
        
        return cardHeader
    


extension EmailCellNode 
    class FrameSet 
        let cardHeaderFrame: CGRect
        init(node: EmailCellNode, calculatedSize: CGSize) 
            var calculatedcardHeaderFrame = CGRect(origin: CGPointMake(senderPhotoFrame.maxX + 8, senderPhotoFrame.minY) , size: node.cardHeaderNode.calculatedSize)
            cardHeaderFrame = calculatedcardHeaderFrame.integerRect.integerRect
        
    

【问题讨论】:

【参考方案1】:

我不熟悉AsyncDisplayKit,但是你使用TTTAttributedLabel有一些问题:

您正在使用TTTAttributedLabel() 初始化标签,它调用init。您必须改用指定的初始化程序initWithFrame:initWithCoder:,因为init 不会正确初始化links 数组和其他各种内部属性。在最新版本的TTTAttributedLabel 中,init 被标记为不可用。

您正在分配给attributedText 属性。请在TTTAttributedLabel.h 中查看此说明:

@bug 不建议直接设置attributedText,因为它可能会在尝试访问之前设置的任何链接时导致崩溃。相反,调用setText:,传递NSAttributedString

您永远不应分配给attributedText 属性。

【讨论】:

感谢您提供有关 TTTAttributedLabel 的最佳实践。我最终只使用了ASTextNode。我将在下面发布我的解决方案【参考方案2】:

我最终只使用了ASTextNode,它没有TTTAttributedLabel 那么多的功能,但足以满足我的需要。此外,因为它是一个沉重的ASCollectionView,所以最好完全异步。下面是我如何在ASCellNode 中创建复合体ASTextNode 的示例。

这是带有可点击名称的最终结果,通过转场传递 JSON 数据。

这是构建NSAttributedString的简化版本

func createAttributedLabel(mailData: JSON) -> NSAttributedString var 收件人:[String] = []

for (key: String, subJson: JSON) in mailData["To"] 
    if let recipientName = subJson["Name"].string 
        recipients.append(recipientName)
    

// Add recipients to attributed string and save ranges for each
    var index = 0
    var position = senderName.length + 2
    for recipient in recipients 
            let recipientDictionary:[String: AnyObject] = ["recipient": recipient,"index": index ]
            let recipientJSON = mailData["To"][index]
            attString.appendAttributedString(NSAttributedString(string: ", \(recipient)"))
            let rangeRecipient : NSRange = (attString.string as NSString).rangeOfString(recipient, options: nil, range: NSMakeRange(position, attString.length-position))
            attString.addAttributes([
                kLinkAttributeName: recipientJSON.rawValue,
                NSForegroundColorAttributeName: UIColor.blackColor(),
                NSFontAttributeName:  UIFont(name: "SourceSansPro-Semibold", size: 14)!,
                NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleNone.rawValue],
                range: rangeRecipient)
    
    index = index + 1
return attString

然后结束以检测链接点击。我必须将我的 JSON 转换为原始值才能传递数据。

func textNode(textNode: ASTextNode!, tappedLinkAttribute attribute: String!, value: AnyObject!, atPoint point: CGPoint, textRange: NSRange) 

//    The node tapped a link; open it if it's a valid URL
    if  (value.isKindOfClass(NSDictionary)) 
        var jsonTransferred = JSON(rawValue: value as! NSDictionary)
        self.delegate.callSegueFromCellUser(data: jsonTransferred!)
     else 
        var jsonTransferred = JSON(rawValue: value as! NSArray)
        self.delegate.callSegueFromCellRecipients(data: jsonTransferred!)
    

【讨论】:

【参考方案3】:

我是 ASDK 的主要维护者之一,并且很乐意帮助您解决任何挑战 — 随时打开项目的 GitHub 问题(即使只是提问)。

您喜欢 TTT 的 ASTextNode 缺少哪些功能?它确实处理链接,并在多个、不相交和换行的链接之间完成基于质心的抽头消歧。可能缺少一些功能,但由于该项目被广泛使用,我敢打赌其他开发人员会发现添加任何您需要的功能很有用。

也就是说,如果您不需要移动文本布局和从主线程渲染所带来的显着性能提升,您可以将 TTT 包装在 initWithViewBlock 中 - 或者直接创建和添加视图,无需节点包装,在任何节点的 -didLoad 方法中。通常使用 ASDK,不需要包装视图(这就是我询问 ASTextNode 的原因)。

祝你工作顺利!

【讨论】:

嗨@Scott,你是对的!我最终使用了 ASTextNode,它完成了我需要的一切。我在下面记录了我是如何做到的!感谢您对堆栈和 github 的响应! 很高兴听到这个消息,@JonasRomainWiesel!现在,我们正在添加各种其他文本功能,例如在堆栈布局(甚至跨多个堆栈)中的基线对齐(设计师喜欢的方法)。当您遇到任何限制/错误时,请务必提交问题,以便我们可以进行增强。祝愿您表现出色,-- Scott :)

以上是关于如何将 ASTextNode 与 TTTAttributedLabel 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Swinject:如何将委托模式与接口隔离(类与接口)一起使用?

如何将 'afterColumnResize' 事件与 'rhandsontable' 一起使用?

如何将 Quickblox 与 angularjs 集成?

如何将手表与 iPhone 连接

如何将两个整数与 Twisted 相加?

如何将 Celery 与 asyncio 结合使用?