带有超链接的标准 UITableView 部分页脚视图

jrc*_*jrc 2 iphone uitableview ios

我正在尝试实现UITableView带有超链接的节页脚视图。我希望它看起来像所有其他标准分组UITableView部分页脚视图,具有相同的指标、字体、颜色等,只是带有可点击的超链接。我希望尽可能避免对任何布局值(包括高度)进行硬编码。理想情况下,我想避免外部依赖项,例如TTAttributedLabel.

编辑:超链接是带下划线的文本链接,而不是带下划线的 URL,因此数据检测器不适用。

jrc*_*jrc 5

我以为这很容易,但看来我错了。

获取标准字体和颜色:

  • 如果我实例化自己的 UITableViewHeaderFooterView 实例,则textLabel字体(在 iOS 9 上)与 UITableView 的标准部分页脚视图字体(SF UI Text Regular 17 与 SF UI Text Regular 13)不匹配。默认的 NSAttributedString 文本颜色也是黑色,与标准灰色不匹配。

  • -[UITableView footerViewForSection:]仅适用于由 提供的自定义页脚视图-tableView:viewForFooterInSection:,否则返回 nil。获得标准页脚视图(以确定标准字体和颜色值)的唯一方法是不提供自定义页脚视图。然后可以将传递给的视图-tableView:willDisplayFooterView:forSection:强制转换UITableViewHeaderFooterView并检查。这似乎是未记录的行为,并且字体属性来得太晚,无法用于计算页脚视图高度等。

确定页脚视图高度:

  • -tableView:heightForFooterInSection:UITableView在请求视图 ( ) 之前先请求页脚的高度( -tableView:viewForFooterInSection:)。这意味着我需要实例化视图以便尽早(例如在某个-viewDidLoad时间)计算其高度。但-[UITableViewHeaderFooterView sizeToFit]计算出的高度为 0。事实上,textLabel直到 AFTER 后才计算 的高度-tableView:willDisplayFooterView:forSection:,大概是因为页脚视图尚未添加到任何容器视图中。

  • 如果我使用UITableViewAutomaticDimension, UITableView 会根据标题(来自-tableView:titleForFooterInSection:Storyboard/xib 中编码的或等效值)计算页脚视图的高度。直到 之前,这种情况才会发生-tableView:willDisplayFooterView:forSection:

实现超链接:

  • UITableViewHeaderFooterView 提供文本标签,但 UILabel 不支持可点击链接,因此我需要将 UITextView 添加到页脚视图。当时-tableView:willDisplayFooterView:forSection:, UITableViewHeaderFooterViewtextLabel仍然有零帧和零超级视图,所以我不知道如何匹配它的框架。

可能的解决方案

这是我能想到的最短、最干净的解决方案。我仍在对字体 和 进行硬编码textContainerInset,这并不理想。

class LinkTableViewHeaderFooterView: UITableViewHeaderFooterView {
    let textView = UITextView(frame: CGRectZero)

    private func _setUpTextView() {
        textView.editable = false
        textView.scrollEnabled = false
        textView.textContainerInset = UIEdgeInsetsMake(0.0, -5.0, 0.0, -5.0) // hard-coded
        textView.backgroundColor = UIColor.clearColor()
        self.addSubview(textView)
    }

    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        _setUpTextView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        _setUpTextView()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.textView.frame = self.textLabel!.frame // Match frame
    }

}

class MyViewController: UITableViewController {

    var footerAttributedText: NSAttributedString!

    override func viewDidLoad() {
        :

        self.tableView.registerClass(LinkTableViewHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "LinkTableViewHeaderFooterView")

        let html = "<a href=\"https://en.wikipedia.org/wiki/Lorem_ipsum\">Lorem ipsum</a> dolor sit amet, <a href=\"http://www.example.com/\">consectetur</a> adipisicing elit, sed do eiusmod <a href=\"http://stackoverflow.com\">tempor incididunt ut</a> labore et dolore magna aliqua."
        // HTML -> NSAttributedString
        let data = html.dataUsingEncoding(NSUTF8StringEncoding)!
        let options: [String: AnyObject] = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding]
        self.footerAttributedText = try? NSAttributedString(data: data, options: options, documentAttributes: nil)
}

    override func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
        if section == 1 {
            return self.footerAttributedText.string // UITableView will use this value to determine footer height
        }

        return nil
    }

    override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        if section == 1 {
            return tableView.dequeueReusableHeaderFooterViewWithIdentifier("LinkTableViewHeaderFooterView")
        }

        return nil
    }

    override func tableView(tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) {
        if section == 1 {
            guard let footerView = view as? LinkTableViewHeaderFooterView else { return }

            let fontAttribute = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote) // hard-coded
            let textColorAttribute = (footerView.textLabel?.attributedText?.attribute(NSForegroundColorAttributeName, atIndex: 0, effectiveRange: nil))! // preserve font color
            let attributes = [NSFontAttributeName: fontAttribute, NSForegroundColorAttributeName: textColorAttribute]

            let mutableAttributedText = self.footerAttributedText.mutableCopy() as! NSMutableAttributedString
            mutableAttributedText.addAttributes(attributes, range: NSMakeRange(0, mutableAttributedText.length))
            footerView.textView.attributedText = mutableAttributedText
            footerView.textView.tintColor = tableView.tintColor

            footerView.textLabel?.attributedText = mutableAttributedText // UITableView will use this value to layout the footer view
            footerView.textLabel?.hidden = true
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

如果有人有任何改进或想法,我很想听听。