-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathAttributedTextView.swift
More file actions
56 lines (44 loc) · 2.12 KB
/
AttributedTextView.swift
File metadata and controls
56 lines (44 loc) · 2.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import SwiftUI
public struct AttributedTextView: NSViewRepresentable {
public let attributedString: NSAttributedString
public init(attributedString: NSAttributedString) {
self.attributedString = attributedString.trimmed
}
public func makeNSView(context: Context) -> NSTextField {
let textField = NSTextField(labelWithAttributedString: attributedString)
textField.isEditable = false
textField.isSelectable = true
textField.allowsEditingTextAttributes = true
textField.lineBreakStrategy = .standard
textField.lineBreakMode = .byWordWrapping
textField.usesSingleLineMode = false
return textField
}
public func updateNSView(_ textField: NSTextField, context: Context) {
if textField.attributedStringValue != attributedString {
textField.attributedStringValue = attributedString
}
}
public func sizeThatFits(_ proposal: ProposedViewSize, nsView textField: NSTextField, context: Context) -> CGSize? {
guard let width = proposal.width, width > 0, width != .infinity else { return nil }
textField.preferredMaxLayoutWidth = width
guard let size = textField.cell?.cellSize(forBounds: NSRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)) else {
return nil
}
return CGSize(width: ceil(size.width), height: ceil(size.height))
}
}
extension NSAttributedString {
var trimmed: NSAttributedString {
let nonWhitespaces = CharacterSet.whitespacesAndNewlines.inverted
let startRange = string.rangeOfCharacter(from: nonWhitespaces)
let endRange = string.rangeOfCharacter(from: nonWhitespaces, options: .backwards)
guard let startLocation = startRange?.upperBound, let endLocation = endRange?.lowerBound else {
return self
}
let location = string.distance(from: string.startIndex, to: startLocation) - 1
let length = string.distance(from: startLocation, to: endLocation) + 2
let range = NSRange(location: location, length: length)
return attributedSubstring(from: range)
}
}