3

I'm using a UICollectionView with compositional layout that contains UICollectionViewListCells. Each cell contains several UILabels. I set the dynamic type with custom font weight with

extension UIFont {
    static func preferredFont(for style: TextStyle, weight: Weight) -> UIFont {
        let metrics = UIFontMetrics(forTextStyle: style)
        let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style)
        let font = UIFont.systemFont(ofSize: desc.pointSize, weight: weight)
        return metrics.scaledFont(for: font)
    }
}

source: https://mackarous.com/dev/2018/12/4/dynamic-type-at-any-font-weight

I then change the font size:

enter image description here

When I scroll through my UICollectionView, some cells' font size remain unchanged while most are correctly resized (everything works perfectly when using something simpler like UIFont.preferredFont(forTextStyle: .subheadline). What is the best approach to getting the proper font size to render in response to the change in preferred content size?

Additionally I'm creating an attributed string which contains SF Symbols https://stackoverflow.com/a/58341241 and setting the size of the symbol with

let configuration = UIImage.SymbolConfiguration(textStyle: .title1)

When I change the font size, the image doesn't dynamically scale. Both of these issues go away when I restart the app. Is there any code I can execute when UIContentSizeCategory.didChangeNotification posts, or other approaches to tackling these two issues?

EDIT I was able to solve the first part of the question by using the approach laid out here https://spin.atomicobject.com/2018/02/02/swift-scaled-font-bold-italic/.

extension UIFont {
    func withTraits(traits:UIFontDescriptor.SymbolicTraits) -> UIFont {
        let descriptor = fontDescriptor.withSymbolicTraits(traits)
        return UIFont(descriptor: descriptor!, size: 0) //size 0 means keep the size as it is
    }

    func bold() -> UIFont {
        return withTraits(traits: .traitBold)
    }

}

let boldFont = UIFont.preferredFont(forTextStyle: .headline).bold()

I'm still stuck on how to get the sf symbol UIImage in the attributed string to resize when the preferred content size changes.

XLE_22
  • 5,124
  • 3
  • 21
  • 72
user784637
  • 15,392
  • 32
  • 93
  • 156

1 Answers1

0

To get the attributed string to resize when the preferred content size changes.

    func attributedStringDynamicTypeExample() {
        let font = UIFont(name: "Times New Roman", size: 20)!
        let fontMetrics = UIFontMetrics(forTextStyle: .title3)
        let scaledFont = fontMetrics.scaledFont(for: font)
        
        let attachment = NSTextAttachment()
        attachment.image = UIImage(named: "BigPoppa")
        attachment.adjustsImageSizeForAccessibilityContentSizeCategory = true
        
        let attributes: [NSAttributedString.Key: Any] = [.font: scaledFont]
        let attributedText = NSMutableAttributedString(string: "Your text here!", attributes: attributes)
        attributedText.append(NSAttributedString(attachment: attachment))
        
        label.adjustsFontForContentSizeCategory = true
        label.attributedText = attributedText
    }
 

As far as the best approach to getting the proper font size to render in response to the change in preferred content size I prefer overriding the traitCollectionDidChange like so:

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        guard previousTraitCollection?.preferredContentSizeCategory
                != traitCollection.preferredContentSizeCategory
        else { return }
        collectionView.collectionViewLayout.invalidateLayout()
    }

But using the notification should be just as effective:

NotificationCenter.default.addObserver(self,
                   selector: #selector(sizeCategoryDidChange),
                   name: UIContentSizeCategory.didChangeNotification,
                   object: nil)

@objc private func sizeCategoryDidChange() {
            collectionView.collectionViewLayout.invalidateLayout()
        }
Daniel Lyon
  • 1,499
  • 10
  • 15