Last active
September 21, 2015 19:46
-
-
Save algal/17c73d28e562ccc07372 to your computer and use it in GitHub Desktop.
Playground gist illustrating relation between line and paragraph breaks, and between line spacing and paragraph spacing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
//: # Paragraphs, lines, breaks, and spacing in attributed strings | |
/*: | |
The cocoa text system lets you separately adjust line spacing and paragraph spacing. | |
This playground investigates a few questions concerning these spacing configurations and text layout: | |
1. Do empty UILabels collapse to zero height? | |
2. What defines the difference between a line break and a paragraph break, for the purpose of whether line spacing or paragraph spacing is used? | |
3. What kind of "spacing collapse" behavior applies? | |
What motivates question 3 in particular is the alternative example of the HTML5 box model. In this model, the `<p>` paragraph elements define boxes with vertical margin distances. There is a relatively complicated rule which requires when margins "collapse" into each other and overlap to define a shared margin. A paragraph's margins can collapse with its sibling paragraph, with its parent block, etc.. | |
Answers: | |
1. Yes, empty UILabels collapse to zero height. | |
2. It seems paragraph breaks are defined by _either_ the LINE FEED or PARAGRAPH SEPARATOR characters, and line breaks are defined by word wrapping. (Maybe there is also Unicode character which expicitly defines a soft line break?) | |
3. There is no spacing collapse behavior. Paragraph spacing is just added to line spacing. | |
*/ | |
//: ## Question 1: Do empty labels collapse? | |
let oneLineLabel = UILabel() | |
oneLineLabel.text = "Hello World" | |
let ics = oneLineLabel.intrinsicContentSize() | |
let stf = oneLineLabel.sizeThatFits(CGSize.zero) | |
let emptyLabel = UILabel() | |
emptyLabel.text = "" | |
let emptyICS = emptyLabel.intrinsicContentSize() | |
//: Answer: yes, empty labels collapse to zero height. | |
//: ## Question 2: Does the PARAGRAPH SEPARATOR characater break paragraphs and is paragraph spacing different from line spacing by default? | |
let twoLineLabel = UILabel() | |
twoLineLabel.numberOfLines = 0 | |
twoLineLabel.text = "Hello\nWorld" | |
let twolineICS = twoLineLabel.intrinsicContentSize() | |
let twoParaLabel = UILabel() | |
twoParaLabel.numberOfLines = 0 | |
twoParaLabel.text = "Hello\u{2029}World" | |
let twoParaICS = twoParaLabel.intrinsicContentSize() | |
//: Answer: uncertain. The heights of these two labels are identical, despite that one uses LINE FEED and the other uses PARAGRAPH SEPARATOR. So either PARAGRAPH SEPARATOR does not break paragraphs, or `lineSpacing` and `paragraphSpacing` are indistiguishable. | |
//: ## Narrowed Question 3: What defines paragraph breaks, for the purpose of distinguishing `lineSpacing` and `paragraphSpacing`? | |
//: Let us set explicit distinct values for line spacing and paragraph spacing | |
let p = NSMutableParagraphStyle() | |
let ps = p.paragraphSpacing | |
let psb = p.paragraphSpacingBefore | |
p.lineSpacing = 5 | |
p.paragraphSpacing = 10 | |
twoLineLabel.attributedText = NSAttributedString(string: twoLineLabel.text!, attributes: [NSParagraphStyleAttributeName:p]) | |
twoParaLabel.attributedText = NSAttributedString(string: twoParaLabel.text!, attributes: [NSParagraphStyleAttributeName:p]) | |
let twolineICSP = twoLineLabel.intrinsicContentSize() | |
let twoParaICSP = twoParaLabel.intrinsicContentSize() | |
//: Answer: Both have the same hieght. So the paragraph separator does not cause `paragraphSpacing` to be used in one label but not the other. In fact both are 15 taller than the default case without custom spacing, which is the sum of the `lineSpacing` and `paragraphSpacing`. So it seems both values are being added, without any collapsing of vertical space (unlike the HTML5 box model). So it seems that: _both LINE FEED and PARAGRAPH SEPARATOR are being treated as defining a new paragraph_. | |
//: ## Question 4: So what defines a line break, not a paragraph break? | |
//: Let us check to see if the difference between a line break and a paragraph break is just that a line break is produced by word wrapping, and a paragraph break is forced by the LINE FEED character. We will setup one label with a space and with a LINE FEED. We will request the labels' sizes, forced to a width which requires a word wrapping at the space. Then we will observe the resulting heights, to see if the break produced by word wrapping has a different height from the break produced by the LINE FEED. | |
twoLineLabel.attributedText = NSAttributedString(string: "Hello World", attributes: [NSParagraphStyleAttributeName:p]) | |
twoParaLabel.attributedText = NSAttributedString(string: "Hello\nWorld", attributes: [NSParagraphStyleAttributeName:p]) | |
let wrappedTwoLineFittingSize = twoLineLabel.systemLayoutSizeFittingSize(CGSize(width: 50, height: 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel) | |
let wrappedTwoParaFittingSize = twoParaLabel.systemLayoutSizeFittingSize(CGSize(width: 50, height: 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel) | |
//: Answer: Yes, the word-wrapped break uses the `lineSpacing` height, and the LINE FEED-forced break uses the `lineSpacing`+`paragraphSpacing` height. So _word wrapping defines line breaks, LINE FEEDs defines paragraph breaks, and the vertical space between paragraphs is the sum of the `lineSpacing` and `paragraphSpacing`_. | |
//: ## Question: does LINE SEPERATOR cause a line not parapgraph break? | |
twoLineLabel.attributedText = NSAttributedString(string: "Hello\u{2029}World", attributes: [NSParagraphStyleAttributeName:p]) | |
twoParaLabel.attributedText = NSAttributedString(string: "Hello\u{2028}World", attributes: [NSParagraphStyleAttributeName:p]) | |
let twolineICSP2 = twoLineLabel.intrinsicContentSize() | |
let twoParaICSP2 = twoParaLabel.intrinsicContentSize() | |
//: Answer: Nope, nope, nope. Despite the recommendation in the gloriously nerdy section 5.8 of the Unicode standard, which is that LINE SEPARATOR and PARAGRAPH SEPARATOR should be treated differently, where one marks intra-paragraph line breaks, and othe other marks paragraph breaks, the Cocoa text system treats them both as paragraph separators, as it applies the paragraph spacing to both. | |
//: Question: is the default lineSpacing in a multiline label, the same as the result of directly stacking two labels? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment