Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disappearing list bullets on iOS 18 caused by DTCoreText NSAttributedString category #1289

Open
amja opened this issue Jan 16, 2025 · 4 comments

Comments

@amja
Copy link

amja commented Jan 16, 2025

I'm trying to display a UITextView with a bulleted list paragraph style but when I add DTCoreText as a Cocoapod, the bullets disappear!

Before After
Bullet list with dots before each item. Same list but dots are missing.

After setting breakpoints on DTCoreText, I observed that the library is called while displaying my text view (note: I haven't used any DTCoreText objects anywhere). Here is the backtrace:

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 12.993
  * frame #0: 0x0000000101291c80 DTCoreText`-[NSAttributedString(self=0x0000000101604190, _cmd="rangeOfTextList:atIndex:", list=0x0000600000cebf90, location=0) rangeOfTextList:atIndex:] at NSAttributedString+DTCoreText.m:217:2
    frame #1: 0x0000000184be288c UIFoundation`+[NSTextListElement _rootTextListElementFromAttributedString:atIndex:options:effectiveRange:textListElementInstantiationCallback:] + 132
    frame #2: 0x0000000184c22220 UIFoundation`-[NSTextContentStorage enumerateTextElementsFromLocation:options:usingBlock:] + 2356
    frame #3: 0x0000000184bc5340 UIFoundation`-[NSTextLayoutManager _estimatedTextLocationForVerticalOffset:originLocation:originOffset:deltaFromOffset:] + 1040
    frame #4: 0x0000000184bc7d68 UIFoundation`-[NSTextLayoutManager ensureLayoutForBounds:] + 180
    frame #5: 0x0000000185ee293c UIKitCore`-[_UITextContainerView _ensureLayoutForCappedSize] + 176
    frame #6: 0x0000000185ee265c UIKitCore`-[_UITextContainerView sizeToFit] + 100
    frame #7: 0x0000000185ecc4ec UIKitCore`-[UITextView _layoutText] + 56
    frame #8: 0x0000000185ecb04c UIKitCore`-[UITextView setAttributedText:] + 1448
    frame #9: 0x00000001cbfb8f8c UIKit`-[UITextViewAccessibility setAttributedText:] + 164
    frame #10: 0x0000000101174148 iOSTester.debug.dylib`BulletedListViewController.viewDidLoad() at SwiftViewController.swift:60:33
    frame #11: 0x0000000101174810 iOSTester.debug.dylib`@objc BulletedListViewController.viewDidLoad() at <compiler-generated>:0

I know - (NSRange)rangeOfTextList:(DTCSSListStyle *)list atIndex:(NSUInteger)location is supposed to mimic AppKit's - (NSRange)rangeOfTextList:(NSTextList *)list atIndex:(NSUInteger)location but I can't tell if this calling behaviour is intentional or not. Either way, the logic of this method doesn't work as expected and breaks the bullets.

If unintentional, should this method be prefixed to avoid this collision? Or is there an alternative approach to resolve this issue?

Note: This only occurs on iOS 18. The method is still called on iOS 17 but doesn't seem to break bulleted lists.

Here is the example view controller I am using to trigger the scenario:

final class BulletedListViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemGreen
        
        let textView = UITextView.withAutoLayout()
        textView.isScrollEnabled = false
        view.addSubview(textView)
        
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.textLists = [.init(markerFormat: .disc, options: .zero)]
        
        let text = NSAttributedString(string: "One\nTwo\nThree", attributes: [.paragraphStyle: paragraphStyle])
        textView.attributedText = text
        
        NSLayoutConstraint.activate([
            textView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            textView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
}
@odrobnik
Copy link
Collaborator

sure, try prefixing to see if that fixes it. Happy to get your PR.

@amja
Copy link
Author

amja commented Jan 16, 2025

Yes, prefixing resolves the issue :) I've not contributed in the past—is there a preferred prefix to use and can you confirm that this name change would be ok, given it will be a breaking change for users who might be calling this API.

@odrobnik
Copy link
Collaborator

Let's do _DT as prefix

@amja
Copy link
Author

amja commented Jan 21, 2025

@odrobnik raised a PR: #1290. Please review :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants