Skip to content

Commit d8b7982

Browse files
Mark Pospeselmpospese
authored andcommitted
Add support for paragraphIndent and paragraphSpacing
1 parent 781ff36 commit d8b7982

File tree

11 files changed

+128
-18
lines changed

11 files changed

+128
-18
lines changed

Sources/YMatterType/Extensions/UIKit/NSParagraphStyle+lineSpacing.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,27 @@ extension NSParagraphStyle {
3939
/// Combines line height with the existing style
4040
/// - Parameter lineHeight: the line height to use
4141
/// - Returns: Current paragraph style combined with minimumLineHeight and maximumLineHeight both set to lineHeight
42-
public func styleWithLineHeight(_ lineHeight: CGFloat) -> NSParagraphStyle {
42+
43+
/// Combines line height with the existing style
44+
/// - Parameters:
45+
/// - lineHeight: the line height to use
46+
/// - indent: the indent to use (ignored if `0`)
47+
/// - spacing: the spacing to use (ignored if `0`)
48+
/// - Returns: Current paragraph style combined with line height and (if non-zero) indent and spacing
49+
public func styleWithLineHeight(
50+
_ lineHeight: CGFloat,
51+
indent: CGFloat = 0,
52+
spacing: CGFloat = 0
53+
) -> NSParagraphStyle {
4354
let paragraphStyle = (mutableCopy() as? NSMutableParagraphStyle) ?? NSMutableParagraphStyle()
4455
paragraphStyle.minimumLineHeight = lineHeight
4556
paragraphStyle.maximumLineHeight = lineHeight
57+
if indent != 0 {
58+
paragraphStyle.firstLineHeadIndent = indent
59+
}
60+
if spacing != 0 {
61+
paragraphStyle.paragraphSpacing = spacing
62+
}
4663
return paragraphStyle
4764
}
4865
}

Sources/YMatterType/Typography/Typography+Font.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ extension Typography {
7676
lineHeight: scaledLineHeight,
7777
baselineOffset: baselineOffset,
7878
kerning: letterSpacing,
79+
paragraphIndent: paragraphIndent,
80+
paragraphSpacing: paragraphSpacing,
7981
textCase: textCase,
8082
textDecoration: textDecoration
8183
)

Sources/YMatterType/Typography/Typography+Mutators.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public extension Typography {
1919
fontSize: fontSize,
2020
lineHeight: lineHeight,
2121
letterSpacing: letterSpacing,
22+
paragraphIndent: paragraphIndent,
23+
paragraphSpacing: paragraphSpacing,
2224
textCase: textCase,
2325
textDecoration: textDecoration,
2426
textStyle: textStyle,
@@ -36,6 +38,8 @@ public extension Typography {
3638
fontSize: fontSize,
3739
lineHeight: lineHeight,
3840
letterSpacing: letterSpacing,
41+
paragraphIndent: paragraphIndent,
42+
paragraphSpacing: paragraphSpacing,
3943
textCase: textCase,
4044
textDecoration: textDecoration,
4145
textStyle: textStyle,
@@ -53,6 +57,8 @@ public extension Typography {
5357
fontSize: fontSize,
5458
lineHeight: lineHeight,
5559
letterSpacing: letterSpacing,
60+
paragraphIndent: paragraphIndent,
61+
paragraphSpacing: paragraphSpacing,
5662
textCase: textCase,
5763
textDecoration: textDecoration,
5864
textStyle: textStyle,
@@ -72,6 +78,8 @@ public extension Typography {
7278
fontSize: fontSize,
7379
lineHeight: lineHeight,
7480
letterSpacing: value,
81+
paragraphIndent: paragraphIndent,
82+
paragraphSpacing: paragraphSpacing,
7583
textCase: textCase,
7684
textDecoration: textDecoration,
7785
textStyle: textStyle,
@@ -91,6 +99,8 @@ public extension Typography {
9199
fontSize: fontSize,
92100
lineHeight: lineHeight,
93101
letterSpacing: letterSpacing,
102+
paragraphIndent: paragraphIndent,
103+
paragraphSpacing: paragraphSpacing,
94104
textCase: value,
95105
textDecoration: textDecoration,
96106
textStyle: textStyle,
@@ -110,6 +120,8 @@ public extension Typography {
110120
fontSize: fontSize,
111121
lineHeight: lineHeight,
112122
letterSpacing: letterSpacing,
123+
paragraphIndent: paragraphIndent,
124+
paragraphSpacing: paragraphSpacing,
113125
textCase: textCase,
114126
textDecoration: value,
115127
textStyle: textStyle,

Sources/YMatterType/Typography/Typography.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ public struct Typography {
2020
public let lineHeight: CGFloat
2121
/// Letter spacing (in points, not percentage)
2222
public let letterSpacing: CGFloat
23+
/// Paragraph indent (in points)
24+
public let paragraphIndent: CGFloat
25+
/// Paragraph spacing (in points)
26+
public let paragraphSpacing: CGFloat
2327
/// Text case
2428
public let textCase: TextCase
2529
/// Text decoration (none, underline, or strikethrough)
@@ -37,6 +41,8 @@ public struct Typography {
3741
/// - fontSize: font size to use
3842
/// - lineHeight: line height to use
3943
/// - chaacterSpacing: letter spacing to use (defaults to `0`)
44+
/// - paragraphIndent: paragraph indent to use (defaults to `0`)
45+
/// - paragraphSpacing: paragraph spacing to use (defaults to `0`)
4046
/// - textCase: text case to apply (defaults to `.none`)
4147
/// - textDecoration: text decoration to apply (defaults to `.none`)
4248
/// - textStyle: text style to use for scaling (defaults to `.body`)
@@ -47,6 +53,8 @@ public struct Typography {
4753
fontSize: CGFloat,
4854
lineHeight: CGFloat,
4955
letterSpacing: CGFloat = 0,
56+
paragraphIndent: CGFloat = 0,
57+
paragraphSpacing: CGFloat = 0,
5058
textCase: TextCase = .none,
5159
textDecoration: TextDecoration = .none,
5260
textStyle: UIFont.TextStyle = .body,
@@ -57,6 +65,8 @@ public struct Typography {
5765
self.fontSize = fontSize
5866
self.lineHeight = lineHeight
5967
self.letterSpacing = letterSpacing
68+
self.paragraphIndent = paragraphIndent
69+
self.paragraphSpacing = paragraphSpacing
6070
self.textCase = textCase
6171
self.textDecoration = textDecoration
6272
self.textStyle = textStyle
@@ -71,6 +81,8 @@ public struct Typography {
7181
/// - fontSize: font size to use
7282
/// - lineHeight: line height to use
7383
/// - chaacterSpacing: letter spacing to use (defaults to `0`)
84+
/// - paragraphIndent: paragraph indent to use (defaults to `0`)
85+
/// - paragraphSpacing: paragraph spacing to use (defaults to `0`)
7486
/// - textCase: text case to apply (defaults to `.none`)
7587
/// - textDecoration: text decoration to apply (defaults to `.none`)
7688
/// - textStyle: text style to use for scaling (defaults to `.body`)
@@ -82,6 +94,8 @@ public struct Typography {
8294
fontSize: CGFloat,
8395
lineHeight: CGFloat,
8496
letterSpacing: CGFloat = 0,
97+
paragraphIndent: CGFloat = 0,
98+
paragraphSpacing: CGFloat = 0,
8599
textCase: TextCase = .none,
86100
textDecoration: TextDecoration = .none,
87101
textStyle: UIFont.TextStyle = .body,
@@ -93,6 +107,8 @@ public struct Typography {
93107
fontSize: fontSize,
94108
lineHeight: lineHeight,
95109
letterSpacing: letterSpacing,
110+
paragraphIndent: paragraphIndent,
111+
paragraphSpacing: paragraphSpacing,
96112
textCase: textCase,
97113
textDecoration: textDecoration,
98114
textStyle: textStyle,

Sources/YMatterType/Typography/TypographyLayout.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public struct TypographyLayout {
2424
/// Kerning to apply for letter spacing with this font
2525
public let kerning: CGFloat
2626

27+
/// Paragraph indent to apply
28+
public let paragraphIndent: CGFloat
29+
30+
/// Paragraph spacing to apply
31+
public let paragraphSpacing: CGFloat
32+
2733
/// Text case to apply to text
2834
public let textCase: Typography.TextCase
2935

@@ -41,16 +47,20 @@ public struct TypographyLayout {
4147
lineHeight: CGFloat,
4248
baselineOffset: CGFloat,
4349
kerning: CGFloat,
50+
paragraphIndent: CGFloat,
51+
paragraphSpacing: CGFloat,
4452
textCase: Typography.TextCase,
4553
textDecoration: Typography.TextDecoration
4654
) {
4755
self.font = font
4856
self.lineHeight = lineHeight
4957
self.baselineOffset = baselineOffset
5058
self.kerning = kerning
59+
self.paragraphIndent = paragraphIndent
60+
self.paragraphSpacing = paragraphSpacing
5161
self.textCase = textCase
5262
self.textDecoration = textDecoration
53-
self.paragraphStyle = NSParagraphStyle.default.styleWithLineHeight(lineHeight)
63+
self.paragraphStyle = NSParagraphStyle.default.styleWithLineHeight(lineHeight, indent: paragraphIndent, spacing: paragraphSpacing)
5464
}
5565
}
5666

Tests/YMatterTypeTests/Elements/TypographyButtonTests.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,19 @@ final class TypographyButtonTests: TypographyElementTests {
5353
}
5454

5555
func testMultiLine() {
56-
let sut = makeSUT()
56+
let spacing = CGFloat(Int.random(in: 1..<10))
57+
let sut = makeSUT(spacing: spacing)
5758
sut.titleLabel?.numberOfLines = 0
5859

5960
// Given a label with text that spans multiple lines
6061
[2, 3, 5, 8, 13].forEach {
6162
let array: [String] = Array(repeating: "Hello World", count: $0)
6263
sut.setTitle(array.joined(separator: "\n"), for: .normal)
6364

64-
// we expect label height to be a multiple of lineHeight
65+
// we expect label height to be a multiple of lineHeight + paragraph spacing
6566
XCTAssertEqual(
6667
sut.intrinsicContentSize.height,
67-
sut.typography.lineHeight * CGFloat($0) + sut.contentEdgeInsets.vertical
68+
sut.typography.lineHeight * CGFloat($0) + sut.contentEdgeInsets.vertical + spacing * CGFloat($0 - 1)
6869
)
6970
// after calling sizeToFit we expect bounds to equal intrinsicContentSize
7071
sut.sizeToFit()
@@ -409,12 +410,13 @@ final class TypographyButtonTests: TypographyElementTests {
409410
}
410411

411412
private extension TypographyButtonTests {
412-
func makeSUT(file: StaticString = #filePath, line: UInt = #line) -> MockButton {
413+
func makeSUT(spacing: CGFloat = 0, file: StaticString = #filePath, line: UInt = #line) -> MockButton {
413414
let typography = Typography(
414415
fontFamily: Typography.sfProText,
415416
fontWeight: .regular,
416417
fontSize: 16,
417418
lineHeight: 24,
419+
paragraphSpacing: spacing,
418420
isFixed: true
419421
)
420422
let sut = MockButton(typography: typography)

Tests/YMatterTypeTests/Elements/TypographyLabelTests.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,17 @@ final class TypographyLabelTests: TypographyElementTests {
4646
}
4747

4848
func testMultiLine() {
49-
let sut = makeSUT()
49+
let spacing = CGFloat(Int.random(in: 1..<10))
50+
let sut = makeSUT(spacing: spacing)
5051
sut.numberOfLines = 0
5152

5253
// Given a label with text that spans multiple lines
5354
[2, 3, 5, 8, 13].forEach {
5455
let array: [String] = Array(repeating: "Hello World", count: $0)
5556
sut.text = array.joined(separator: "\n")
5657

57-
// we expect label height to be a multiple of lineHeight
58-
XCTAssertEqual(sut.intrinsicContentSize.height, sut.typography.lineHeight * CGFloat($0))
58+
// we expect label height to be a multiple of lineHeight + paragraph spacing
59+
XCTAssertEqual(sut.intrinsicContentSize.height, sut.typography.lineHeight * CGFloat($0) + spacing * CGFloat($0 - 1))
5960
// after calling sizeToFit we expect bounds to equal intrinsicContentSize
6061
sut.sizeToFit()
6162
XCTAssertEqual(sut.intrinsicContentSize, sut.bounds.size)
@@ -322,15 +323,39 @@ final class TypographyLabelTests: TypographyElementTests {
322323
sut.attributedText = NSAttributedString(string: "first name", attributes: [.underlineStyle: 1])
323324
XCTAssertEqual(sut.attributedText?.string, "First Name")
324325
}
326+
327+
func testParagraphIndent() {
328+
// Given
329+
let indent = CGFloat(Int.random(in: 1..<10))
330+
let sut = makeSUT()
331+
let sut2 = makeSUT(indent: indent)
332+
333+
// When
334+
sut.text = "Hello World"
335+
sut2.text = "Hello World"
336+
337+
// Then
338+
XCTAssertEqual(
339+
sut.intrinsicContentSize.width + indent,
340+
sut2.intrinsicContentSize.width
341+
)
342+
}
325343
}
326344

327345
private extension TypographyLabelTests {
328-
func makeSUT(file: StaticString = #filePath, line: UInt = #line) -> MockLabel {
346+
func makeSUT(
347+
indent: CGFloat = 0,
348+
spacing: CGFloat = 0,
349+
file: StaticString = #filePath,
350+
line: UInt = #line
351+
) -> MockLabel {
329352
let typography = Typography(
330353
fontFamily: Typography.sfProDisplay,
331354
fontWeight: .regular,
332355
fontSize: 24,
333356
lineHeight: 32,
357+
paragraphIndent: indent,
358+
paragraphSpacing: spacing,
334359
isFixed: true
335360
)
336361
let sut = MockLabel(typography: typography)

Tests/YMatterTypeTests/Elements/TypographyTextViewTests.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ final class TypographyTextViewTests: TypographyElementTests {
4747
func testMultiLine() {
4848
[2, 3, 5, 8, 13].forEach {
4949
// Given a text view with text that spans multiple lines
50-
let sut = makeSUT()
50+
let spacing = CGFloat(Int.random(in: 1..<10))
51+
let sut = makeSUT(spacing: spacing)
5152
let array: [String] = Array(repeating: "Hello World", count: $0)
5253
sut.text = array.joined(separator: "\n")
5354

54-
// we expect label height to be a multiple of lineHeight
55-
XCTAssertEqual(sut.intrinsicContentSize.height, sut.typography.lineHeight * CGFloat($0))
55+
// we expect label height to be a multiple of lineHeight + paragraph spacing
56+
XCTAssertEqual(sut.intrinsicContentSize.height, sut.typography.lineHeight * CGFloat($0) + spacing * CGFloat($0 - 1))
5657
// after calling sizeToFit we expect bounds to equal intrinsicContentSize
5758
sut.sizeToFit()
5859
XCTAssertEqual(sut.intrinsicContentSize, sut.bounds.size)
@@ -285,8 +286,17 @@ final class TypographyTextViewTests: TypographyElementTests {
285286
}
286287

287288
private extension TypographyTextViewTests {
288-
func makeSUT(file: StaticString = #filePath, line: UInt = #line) -> MockTextView {
289-
let sut = MockTextView(typography: .title2.fixed)
289+
func makeSUT(spacing: CGFloat = 0, file: StaticString = #filePath, line: UInt = #line) -> MockTextView {
290+
let typography = Typography(
291+
fontFamily: Typography.sfProDisplay,
292+
fontWeight: .semibold,
293+
fontSize: 22,
294+
lineHeight: 28,
295+
paragraphSpacing: spacing,
296+
textStyle: .title2,
297+
isFixed: true
298+
)
299+
let sut = MockTextView(typography: typography)
290300
sut.isScrollEnabled = false
291301
trackForMemoryLeak(sut, file: file, line: line)
292302
return sut

Tests/YMatterTypeTests/Extensions/UIKit/NSParagraphStyle+lineSpacingTests.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,24 @@ final class NSParagraphStyleLineSpacingTests: XCTestCase {
3939
func testStyleWithLineHeight() {
4040
let (sut, _, _, _, lineHeights) = makeSUT()
4141
lineHeights.forEach {
42-
let style1 = sut.styleWithLineHeight($0)
43-
let style2 = NSParagraphStyle.default.styleWithLineHeight($0)
44-
let style3 = NonMutableParagraphStyle().styleWithLineHeight($0)
42+
let indent = CGFloat(Int.random(in: 1..<10))
43+
let spacing = CGFloat(Int.random(in: 10..<32))
44+
let style1 = sut.styleWithLineHeight($0, indent: indent, spacing: spacing)
45+
let style2 = NSParagraphStyle.default.styleWithLineHeight($0, indent: indent)
46+
let style3 = NonMutableParagraphStyle().styleWithLineHeight($0, spacing: spacing)
4547

4648
for style in [style1, style2, style3] {
4749
XCTAssertEqual(style.minimumLineHeight, $0)
4850
XCTAssertEqual(style.maximumLineHeight, $0)
4951
}
52+
53+
XCTAssertEqual(style1.firstLineHeadIndent, indent)
54+
XCTAssertEqual(style2.firstLineHeadIndent, indent)
55+
XCTAssertEqual(style3.firstLineHeadIndent, .zero)
56+
57+
XCTAssertEqual(style1.paragraphSpacing, spacing)
58+
XCTAssertEqual(style2.paragraphSpacing, .zero)
59+
XCTAssertEqual(style3.paragraphSpacing, spacing)
5060
}
5161
}
5262
}

Tests/YMatterTypeTests/Helpers/Typography+YMatterTypeTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ extension Typography {
6464
fontWeight: .regular,
6565
fontSize: 15,
6666
lineHeight: 20,
67+
paragraphIndent: 8,
68+
paragraphSpacing: 10,
6769
textStyle: .body
6870
)
6971

@@ -73,6 +75,8 @@ extension Typography {
7375
fontWeight: .regular,
7476
fontSize: 14,
7577
lineHeight: 20,
78+
paragraphIndent: 8,
79+
paragraphSpacing: 8,
7680
textStyle: .callout
7781
)
7882

0 commit comments

Comments
 (0)