• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Why Does Justified Text Alignment Give Wrong Rects in Swift?

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155
Introduction


Many developers face challenges when using UILabel subclasses in Swift, especially when it comes to rendering NSAttributedString with text alignment options such as .justified. In this article, we'll explore why the use of .justified text alignment might cause the background rectangles around links to misalign, and we'll provide clear steps to fix this issue.

Understanding the Problem


When using .justified text alignment in your custom UILabel subclass, the bounding rectangles for the text can become miscalculated. This occurs due to how the NSLayoutManager and UITextContainer calculate the layout and visual representation of text. Justified text stretches the text to fill the width of the label, which dynamically alters the line fragment calculations made by the layout manager.

Why This Happens


The alignment mechanism of .justified modifies the positioning of characters and space between words. As a result, the layoutManager may generate incorrect CGRects for link areas, leading to a situation where your background color does not align accurately with the actual text elements. Additionally, when you change the font size or the contents of the text, as you've noted in your example, the layout system can potentially recalculate and yield the expected results.

Code Explanation


Let’s check the existing implementation of your MyLabel subclass, which is responsible for drawing attributed strings along with their link backgrounds. Here is the core implementation:

import UIKit

class MyLabel: UILabel {
private var linkRects = [[CGRect]]()
private let layoutManager = NSLayoutManager()
private let textContainer = NSTextContainer(size: .zero)
private let textStorage = NSTextStorage()

override func draw(_ rect: CGRect) {
let path = UIBezierPath()
linkRects.forEach { rects in
rects.forEach { linkPieceRect in
path.append(UIBezierPath(roundedRect: linkPieceRect, cornerRadius: 2))
}
}
UIColor.systemGreen.withAlphaComponent(0.4).setFill()
path.fill()

super.draw(rect)
}

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

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

private func setup() {
numberOfLines = 0
adjustsFontForContentSizeCategory = true
isUserInteractionEnabled = true
lineBreakMode = .byWordWrapping
contentMode = .redraw
clearsContextBeforeDrawing = true
isMultipleTouchEnabled = false

backgroundColor = .red.withAlphaComponent(0.1)

layoutManager.textStorage = textStorage
layoutManager.addTextContainer(textContainer)
}

override func layoutSubviews() {
super.layoutSubviews()
calculateRects()
}

private func calculateRects() {
linkRects.removeAll()
guard let attributedString = attributedText else {
return
}
textStorage.setAttributedString(attributedString)
let labelSize = frame.size
textContainer.size = labelSize
layoutManager.ensureLayout(for: textContainer)
let textBoundingBox = layoutManager.usedRect(for: textContainer)

// Logic to calculate CGRects for links
...

setNeedsDisplay()
}
}

Steps to Fix the Misalignment


To address the issue, consider the following approaches:


  1. Adjust the Calculation Method: When calculating the CGRects in your calculateRects method, ensure to account for the adjustments made by the .justified alignment. You may need to render text in a single line temporarily to calculate the link rectangles accurately.

    Here’s an adjusted way to compute link areas:

    private func calculateRects() {
    ...
    if lineBreakMode == .byWordWrapping {
    layoutManager.enumerateLineFragments(forGlyphRange: linkRange) { _, rect, _, range, _ in
    ... // Calculate rects based on non-justified conditions
    }
    }
    ...
    }

  2. Use Different Text Alignments: If the justification does not alter your design significantly, using .left, or .center aligns will ensure correct area calculations without manipulation.


  3. Test Different Scenarios: Experiment with varying fonts and sizes to test if certain conditions lead to misalignment, as you pointed out. Customize your calculations based on observed behaviors.
Frequently Asked Questions

1. What is the best practice for determining link rectangles?


Always perform viewport calculations considering the text style and alignment. It’s advisable to recalculate width/height as the layout updates.

2. Can I use .justified text alignment with links?


Yes, but be mindful that it may require additional adjustment logic to account for misalignments introduced by justifying text.

Conclusion


Dealing with .justified text alignment in Swift can introduce challenges, especially when it comes to accurately rendering backgrounds for links within a custom UILabel. By understanding the layout intricacies and making targeted adjustments in your MyLabel subclass, you can ensure that your text links are displayed as intended. Experiment with the provided solutions and continue to refine your approach based on user feedback and testing scenarios.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу