iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

あなたのアプリ iOS13 で UILabel のスタイルが間違えて表示されている可能性があります

起きたこと

UILabel の attributedTextnil (リセット) にしてもラベルのスタイルが変わらないという現象が発生していました。

f:id:yum_fishing:20200802160903j:plain

TL;DR

原因

UILabel の attributedText の値が nil(リセット)になった場合、通常(iOS12以前)は Label の attributes もリセットされるが iOS13 ではリセットされないとの報告があり、それが起因して考えられます。

解決策

UILabel の textattributedText はどちらか一方の値が変わるともう一方の値も変わるという連動性があることから(下記ドキュメント)、今回のように textattributedText を共存させて更新することはシステムのバグに限らず望ましくないので、attributedText のみ明示的に更新処理を行うように修正したところ解決しました。

※ iOS13のバグかどうかについては現在のところ不明

具体的な発生タイミングと改善

Before

チャットに表示する UI で、コメントがリンクの場合は青く表示させて Clickable にさせるように UILabel を継承したサブラクス(以降: LinkLabel)を作成していました。基本的な仕様としては LinkLabel の text プロパティに didSet をセットし、更新されたタイミングでテキストをトリミングして attributedText を更新するという感じです。

final class LinkLabel: UILabel {
    override var text: String? {
        didSet {
            updateAttributedText()
        }
    }

    func updateAttributedText() {
        guard let text = text else {
            return
        }
        let mutableAttributedString = NSMutableAttributedString(string: text)

        // 省略....
        // いろいろ Attributes をセットする

        attributedText = mutableAttributedString
    }
}

上記で作成した LinkLabel を Cell に配置して TableView で表示した時に、Cell が再利用されるタイミングで以前の attributes がキャッシュされていて関係ないコメントも青く表示されることがたびたび起こるようになりました。

After

TL;DR にも記載してある通り、attributedText だけを明示的に更新することで改善できたので下記のように修正します。

final class LinkLabel: UILabel {
    var displayText: String?

    func setText(_ text: String?) {
        displayText = text
        updateAttributedText()
    }

    func updateAttributedText() {
        guard let text = displayText else {
            return
        }
        let mutableAttributedString = NSMutableAttributedString(string: text)

        // 省略....
        // いろいろ Attributes をセットする

        attributedText = mutableAttributedString
    }
}

使用する Cell 側でもキャッシュをリセットできるように prepareForReuse() セットします

final class CommentCell: UITableViewCell {
    let linkLabel = LinkLabel()

    override func prepareForReuse() {
        super.prepareForReuse()
        linkLabel.attributedText = nil
    }

    // 省略...
}

調べたこと

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com