NSAttributedString не выполняется вовремя - Swift

Итак, у меня возникают проблемы в проекте, где некоторые строки не отображаются должным образом в некоторых старых iPhone с некоторыми версиями iOS (в частности, это не работает для iPhone 5 с iOS10, а для iOS9.3 - странно достаточно). Чтобы уменьшить проблему, я написал этот код:

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Long HTML, although we'll make it even larger to prove a point
        let string = "<h1>Thing 1</h1><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p><h1>Thing 2</h1><p>Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh. </p><h1>Thing 3</h1><p>Quisque volutpat condimentum velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis. Nulla facilisi. Ut fringilla. Suspendisse potenti. Nunc feugiat mi a tellus consequat imperdiet. Vestibulum sapien. Proin quam. Etiam ultrices. Suspendisse in justo eu magna luctus suscipit. </p><h1>Thing 4</h1><p>Sed lectus. Integer euismod lacus luctus magna. Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor. Sed non quam. In vel mi sit amet augue congue elementum. Morbi in ipsum sit amet pede facilisis laoreet. Donec lacus nunc, viverra nec, blandit vel, egestas et, augue. Vestibulum tincidunt malesuada tellus. Ut ultrices ultrices enim. Curabitur sit amet mauris. Morbi in dui quis est pulvinar ullamcorper. Nulla facilisi. </p><h1>Thing 5</h1><p>Integer lacinia sollicitudin massa. Cras metus. Sed aliquet risus a tortor. Integer id quam. Morbi mi. Quisque nisl felis, venenatis tristique, dignissim in, ultrices sit amet, augue. Proin sodales libero eget ante. Nulla quam. Aenean laoreet. Vestibulum nisi lectus, commodo ac, facilisis ac, ultricies eu, pede. Ut orci risus, accumsan porttitor, cursus quis, aliquet eget, justo. Sed pretium blandit orci. </p><h1>Thing 6</h1><p>Ut eu diam at pede suscipit sodales. Aenean lectus elit, fermentum non, convallis id, sagittis at, neque. Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula. Nulla ut felis in purus aliquam imperdiet. Maecenas aliquet mollis lectus. Vivamus consectetuer risus et tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. </p>"

        let attrStr = try! NSAttributedString(
            data: (string + string + string + string + string + string + string + string).data(using: String.Encoding.unicode,allowLossyConversion: true)!,
            options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil)

        label.attributedText = attrStr
        label.backgroundColor = UIColor(red:0.00, green:1.00, blue:0.00, alpha:1.0)
    }
}

Прост достаточно. Конечно, реальная проблема сложнее, но идея одна и та же.

Итак, в iPhone 7 все работает нормально:

введите описание изображения здесь

Однако при загрузке на iPhone 4s:

введите описание изображения здесь

При отладке attrStr, кажется, правильно установлен в обоих случаях.

Любые идеи? Моя теория заключается в том, что старые телефоны слишком медленны, чтобы сделать это вовремя, но я не совсем уверен, как это решить.

Спасибо!

EDIT: для тех, кто спросил, это раскадровка:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11201" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="JzW-qG-LFG">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="9PR-d2-hNU">
            <objects>
                <viewController id="JzW-qG-LFG" customClass="ViewController" customModule="things" customModuleProvider="target" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="VGp-YT-QQN"/>
                        <viewControllerLayoutGuide type="bottom" id="SeT-t7-CD0"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="gtg-CO-E9d">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Id1-A1-RaT">
                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                <nil key="textColor"/>
                                <nil key="highlightedColor"/>
                            </label>
                        </subviews>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                        <constraints>
                            <constraint firstAttribute="trailingMargin" secondItem="Id1-A1-RaT" secondAttribute="trailing" id="0qp-Rk-PC2"/>
                            <constraint firstItem="Id1-A1-RaT" firstAttribute="leading" secondItem="gtg-CO-E9d" secondAttribute="leadingMargin" id="3Vc-he-HqH"/>
                            <constraint firstItem="Id1-A1-RaT" firstAttribute="top" secondItem="VGp-YT-QQN" secondAttribute="bottom" id="7IE-fY-4H0"/>
                        </constraints>
                    </view>
                    <navigationItem key="navigationItem" id="Opz-px-hsl"/>
                    <connections>
                        <outlet property="label" destination="Id1-A1-RaT" id="TNv-G9-ASE"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="qlV-6d-ISJ" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2848.8000000000002" y="-280.20989505247377"/>
        </scene>
    </scenes>
</document>

Ответ 1

Проблема заключается в том, что UIView имеет недокументированный предел высоты в 8192 балла. Как только метка будет на 8192 точек выше (или выше), она больше не обновляет буфер кадра. (Предположительно, оконный сервер просто игнорирует метку CALayer, когда слой слишком велик.) Все, что уже было в буфере кадров, остается там. Предположительно, поведение undefined и может варьироваться в зависимости от устройств и версий iOS.

(Edit: подумав об этом больше, я подозреваю, что предел составляет 16 384 пикселя, а не 8,192 пункта. Я не думаю, что оконный сервер имеет дело в точках.)

Чтобы продемонстрировать, я взял ваш код и раскадровку и добавил три вещи:

  • Я ограничил высоту вашего ярлыка меньше или равным 8185 точкам.
  • Я добавил слайдер, который обновляет константу ограничения по высоте. Ползунок позволяет использовать диапазон от 8185 до 8200.
  • Я добавил еще одну метку, которая отображает константу ограничения высоты.

Вот что происходит в симуляторе iPhone SE под управлением iOS 10.2:

демо 8192 проблемы высоты точки

В демонстрации вы можете увидеть, что, как только высота превысит 8192 точек, большой палец ползунка начинает смазываться, а текст метки высоты заметно перезаписывается. Это связано с тем, что зеленая метка больше не обновляет буфер кадра, поэтому все, что было ранее (как показано на слайдере и метке высоты), остается только перезаписанным, когда ползунок и метка высоты перерисовываются. Как только высота вернется вниз по пороговому значению 8192, зеленая метка снова натягивается, очищая беспорядок.

Я думаю, что вы не видите этого в своем тестировании на iPhone 7, потому что iPhone 7 имеет более широкий экран, чем iPhone SE. Все iPhone с 3,5-дюймовым и 4-дюймовым экранами имеют ширину 320 точек. iPhone с 4,7-дюймовыми экранами составляет 375 точек, а iPhone с 5,5-дюймовыми экранами - 414 точек.

Более широкий экран означает более широкий UILabel. Это означает, что для каждой строки требуется больше текста, поэтому метка не должна быть такой же высокой, чтобы соответствовать всему тексту. Я подозреваю, что на больших экранах ваши метки короче 8192 точек, поэтому они не попадают в поведение undefined.

Обходные:

  • Если вы не намереваетесь пропустить прокрутку пользователя, чтобы увидеть весь текст, просто поставьте ограничение на высоту на ярлыке.

  • Если вы хотите, чтобы пользователь прокручивал, попробуйте использовать UIWebView или WKWebView вместо UILabel, чтобы отобразить текст. Я считаю, что эти взгляды могут обрабатывать контент более чем на 8192 пункта.

Кстати, вы не первый человек, столкнувшийся с этой проблемой: UILabel вид исчезает, когда высота больше 8192. Тем не менее, ваш вопрос в настоящее время имеет открытую щедрость, которая предотвращает ее закрытие как дубликат.

Ответ 2

Вы не указали какие-либо сведения о своей раскадровке или настройках или ограничениях для label.

Итак, я инициировал лейбл программным путем, и это решение работает на iPhone 4s с iOS 9.3:

введите описание изображения здесь

Вот рабочий исходный код:

override func viewDidLoad() {
    super.viewDidLoad()

    label = UILabel(frame: CGRect(x: 0.0, y: 44.0, width: view.frame.size.width, height: view.frame.size.height - 88.0))
    label.numberOfLines = 0
    label.isUserInteractionEnabled = false
    label.contentMode = .left
    label.textAlignment = .natural
    label.lineBreakMode = .byTruncatingTail
    label.baselineAdjustment = .alignBaselines
    label.adjustsFontSizeToFitWidth = false
//  label.translatesAutoresizingMaskIntoConstraints = false //setting this causes the text to be layout wrong
    label.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]

    view.addSubview(label)

    // Long HTML, although we'll make it even larger to prove a point
    let string = "<h1>Thing 1</h1><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. </p><h1>Thing 2</h1><p>Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor. Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh. </p><h1>Thing 3</h1><p>Quisque volutpat condimentum velit. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis. Nulla facilisi. Ut fringilla. Suspendisse potenti. Nunc feugiat mi a tellus consequat imperdiet. Vestibulum sapien. Proin quam. Etiam ultrices. Suspendisse in justo eu magna luctus suscipit. </p><h1>Thing 4</h1><p>Sed lectus. Integer euismod lacus luctus magna. Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor. Sed non quam. In vel mi sit amet augue congue elementum. Morbi in ipsum sit amet pede facilisis laoreet. Donec lacus nunc, viverra nec, blandit vel, egestas et, augue. Vestibulum tincidunt malesuada tellus. Ut ultrices ultrices enim. Curabitur sit amet mauris. Morbi in dui quis est pulvinar ullamcorper. Nulla facilisi. </p><h1>Thing 5</h1><p>Integer lacinia sollicitudin massa. Cras metus. Sed aliquet risus a tortor. Integer id quam. Morbi mi. Quisque nisl felis, venenatis tristique, dignissim in, ultrices sit amet, augue. Proin sodales libero eget ante. Nulla quam. Aenean laoreet. Vestibulum nisi lectus, commodo ac, facilisis ac, ultricies eu, pede. Ut orci risus, accumsan porttitor, cursus quis, aliquet eget, justo. Sed pretium blandit orci. </p><h1>Thing 6</h1><p>Ut eu diam at pede suscipit sodales. Aenean lectus elit, fermentum non, convallis id, sagittis at, neque. Nullam mauris orci, aliquet et, iaculis et, viverra vitae, ligula. Nulla ut felis in purus aliquam imperdiet. Maecenas aliquet mollis lectus. Vivamus consectetuer risus et tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. </p>"

    let attrStr = try! NSAttributedString(
        data: (string + string + string + string + string + string + string + string).data(using: String.Encoding.unicode,allowLossyConversion: true)!,
        options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
        documentAttributes: nil)

    label.attributedText = attrStr
    label.backgroundColor = UIColor(red:0.00, green:1.00, blue:0.00, alpha:1.0)
}

Полный рабочий проект можно загрузить из здесь.

Ответ 3

Ты определенно прав. Это вопрос времени.

NSHTMLTextDocumentType of NSDocumentTypeDocumentAttribute пресловутый для принятия очень долгое время. По мере того как вы увеличиваете длину строки attrStr (добавив пару строк + в данные), вы сможете реплицировать ошибку для более поздних версий после определенной длины строки. Я могу воспроизвести это в эмуляторе iPhone 6 plus, используя iOS 10.1, так как эмулятор работает с меньшей вычислительной мощностью, чем фактическое устройство.

Если это вам подходит, вы можете попробовать DTCoreText решить эту проблему. Эта библиотека обрабатывает строку html со способами, отличными от встроенных, что сокращает время рендеринга. Это сделает ошибку менее заметной на мобильных телефонах нижнего уровня.