Hi all! I have a pitch for a new AttributedStringKey
serving as a currency type for specifying the base writing direction of a paragraph on AttributedString
s. Feel free to read below and let me know if you have any comments/questions!
Writing Direction Attribute
- Proposal: SF-NNNN
- Authors: Max Obermeier
- Review Manager: TBD
- Status: Pitch
- Implementation: Awaiting Implementation
Motivation
AttributedString
currently has no way to express the base writing direction of a paragraph as a standalone property. Some UI frameworks, such as UIKit or AppKit define a pargraph style property that includes - among other properties - the base writing direction. This attribute originated in the context of NSAttributedString
and has a couple of disadvantages:
-
It is impossible to specify only the base writing direction without also specifying values for the remaining paragraph style properties.
-
The attribute does not utilize advanced
AttributedStringKey
behaviors such asrunBoundaries
orinheritedByAddedText
. -
Writing direction is a fundamental property of strings that is not only relevant in UI frameworks, but needs to be communicated in any context that deals with (potentially) bidirectional strings.
Proposed solution
This proposal adds a new AttributedString.WritingDirection
enum with two cases leftToRight
and rightToLeft
, along with a new key WritingDirectionAttribute
, which is included in AttributeScopes.FoundationAttributes
under the name writingDirection
.
// Indicate that this sentence is primarily right to left, because the English term "Swift" is embedded into an Arabic sentence.
var string = AttributedString("Swift مذهل!", attributes: .init().writingDirection(.rightToLeft))
// To remove the information about the writing direction, set it to `nil`:
string.writingDirection = nil
Since the base writing direction is defined at a paragraph level, the attribute specifies runBoundaries = .paragraph
. Since the writing direction of one paragraph is independent of the next, the attribute is not inheritedByAddedText
.
let range = string.range(of: "Swift")!
// When setting or removing the value from a certain range, the value will always be applied to the entire paragraph(s) that intersect with that range:
string[range].writingDirection = .leftToRight
assert(string.runs[\.writingDirection].count == 1)
// When adding text to a paragraph, the existing writingDirection is applied to the new text.
string.append(AttributedString(" It is awesome for working with strings!"))
assert(string.runs[\.writingDirection].count == 1)
assert(string.writingDirection == .leftToRight)
// When adding a new paragraph, the new paragraph does not inherit the writing direction of the preceding paragraph.
string.append(AttributedString("\nThe new paragraph does not inherit the writing direction."))
assert(string.runs[\.writingDirection].count == 2)
assert(string.runs.last?.writingDirection == nil)
For the full proposal including the detailed design and alternatives considered, check out the full proposal document on the swift-foundation repo.