Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Refactor to remove the preference, which is not needed
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalezreal committed Jan 25, 2021
1 parent 8d66e40 commit bf076de
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 83 deletions.
32 changes: 10 additions & 22 deletions Sources/AttributedText/AttributedText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)
public struct AttributedText: View {
@StateObject private var store = TextViewStore()
@StateObject private var textViewStore = TextViewStore()

private let attributedText: NSAttributedString

Expand All @@ -14,36 +14,24 @@

public var body: some View {
GeometryReader { geometry in
TextViewWrapper(attributedText: attributedText, store: store)
.preference(key: ContainerSizePreference.self, value: geometry.maxSize)
}
.onPreferenceChange(ContainerSizePreference.self) { value in
store.onContainerSizeChange(value)
TextViewWrapper(
attributedText: attributedText,
maxLayoutWidth: geometry.maxWidth,
textViewStore: textViewStore
)
}
.frame(
idealWidth: store.intrinsicContentSize?.width,
idealHeight: store.intrinsicContentSize?.height
idealWidth: textViewStore.intrinsicContentSize?.width,
idealHeight: textViewStore.intrinsicContentSize?.height
)
.fixedSize(horizontal: false, vertical: true)
}
}

@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)
private extension GeometryProxy {
var maxSize: CGSize {
CGSize(
width: size.width - safeAreaInsets.leading - safeAreaInsets.trailing,
height: size.height - safeAreaInsets.top - safeAreaInsets.bottom
)
}
}

@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)
private struct ContainerSizePreference: PreferenceKey {
static var defaultValue: CGSize?

static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
value = nextValue()
var maxWidth: CGFloat {
size.width - safeAreaInsets.leading - safeAreaInsets.trailing
}
}

Expand Down
15 changes: 2 additions & 13 deletions Sources/AttributedText/TextViewStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,8 @@
final class TextViewStore: ObservableObject {
@Published var intrinsicContentSize: CGSize?

weak var view: TextViewWrapper.View?

func onContainerSizeChange(_ containerSize: CGSize?) {
guard let containerSize = containerSize,
let view = self.view else { return }

view.maxWidth = containerSize.width
}

func didInvalidateIntrinsicContentSize() {
guard let view = self.view else { return }

intrinsicContentSize = view.intrinsicContentSize
func didUpdateTextView(_ textView: TextViewWrapper.View) {
intrinsicContentSize = textView.intrinsicContentSize
}
}

Expand Down
32 changes: 9 additions & 23 deletions Sources/AttributedText/TextViewWrapper+NSTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
@available(macOS 11.0, *)
struct TextViewWrapper: NSViewRepresentable {
final class View: NSTextView {
weak var store: TextViewStore?

var maxWidth: CGFloat {
var maxLayoutWidth: CGFloat {
get { textContainer?.containerSize.width ?? 0 }
set {
guard textContainer?.containerSize.width != newValue else { return }
Expand All @@ -17,7 +15,7 @@
}

override var intrinsicContentSize: NSSize {
guard maxWidth > 0,
guard maxLayoutWidth > 0,
let textContainer = self.textContainer,
let layoutManager = self.layoutManager
else {
Expand All @@ -27,19 +25,6 @@
layoutManager.ensureLayout(for: textContainer)
return layoutManager.usedRect(for: textContainer).size
}

override func invalidateIntrinsicContentSize() {
super.invalidateIntrinsicContentSize()
store?.didInvalidateIntrinsicContentSize()
}

func setAttributedText(_ attributedText: NSAttributedString) {
// Avoid notifiying the store while the text storage is processing edits
let store = self.store
self.store = nil
textStorage?.setAttributedString(attributedText)
self.store = store
}
}

final class Coordinator: NSObject, NSTextViewDelegate {
Expand All @@ -56,7 +41,8 @@
}

let attributedText: NSAttributedString
let store: TextViewStore
let maxLayoutWidth: CGFloat
let textViewStore: TextViewStore

func makeNSView(context: Context) -> View {
let nsView = View(frame: .zero)
Expand All @@ -70,19 +56,19 @@
nsView.textContainer?.widthTracksTextView = false
nsView.delegate = context.coordinator

nsView.store = store
store.view = nsView

return nsView
}

func updateNSView(_ nsView: View, context: Context) {
nsView.setAttributedText(attributedText)
nsView.textStorage?.setAttributedString(attributedText)
nsView.maxLayoutWidth = maxLayoutWidth

nsView.textContainer?.maximumNumberOfLines = context.environment.lineLimit ?? 0
nsView.textContainer?.lineBreakMode = NSLineBreakMode(truncationMode: context.environment.truncationMode)
nsView.invalidateIntrinsicContentSize()

context.coordinator.openURL = context.environment.openURL

textViewStore.didUpdateTextView(nsView)
}

func makeCoordinator() -> Coordinator {
Expand Down
36 changes: 11 additions & 25 deletions Sources/AttributedText/TextViewWrapper+UITextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,22 @@
@available(iOS 14.0, tvOS 14.0, macCatalyst 14.0, *)
struct TextViewWrapper: UIViewRepresentable {
final class View: UITextView {
weak var store: TextViewStore?

var maxWidth: CGFloat = 0 {
var maxLayoutWidth: CGFloat = 0 {
didSet {
guard maxWidth != oldValue else { return }
guard maxLayoutWidth != oldValue else { return }
invalidateIntrinsicContentSize()
}
}

override var intrinsicContentSize: CGSize {
guard maxWidth > 0 else {
guard maxLayoutWidth > 0 else {
return super.intrinsicContentSize
}

return sizeThatFits(
CGSize(width: maxWidth, height: .greatestFiniteMagnitude)
CGSize(width: maxLayoutWidth, height: .greatestFiniteMagnitude)
)
}

override func invalidateIntrinsicContentSize() {
super.invalidateIntrinsicContentSize()
store?.didInvalidateIntrinsicContentSize()
}

func setAttributedText(_ attributedText: NSAttributedString) {
// Avoid notifiying the store while the text storage is processing edits
let store = self.store
self.store = nil
self.attributedText = attributedText
self.store = store
}
}

final class Coordinator: NSObject, UITextViewDelegate {
Expand All @@ -48,7 +33,8 @@
}

let attributedText: NSAttributedString
let store: TextViewStore
let maxLayoutWidth: CGFloat
let textViewStore: TextViewStore

func makeUIView(context: Context) -> View {
let uiView = View()
Expand All @@ -62,19 +48,19 @@
uiView.textContainer.lineFragmentPadding = 0
uiView.delegate = context.coordinator

uiView.store = store
store.view = uiView

return uiView
}

func updateUIView(_ uiView: View, context: Context) {
uiView.setAttributedText(attributedText)
uiView.attributedText = attributedText
uiView.maxLayoutWidth = maxLayoutWidth

uiView.textContainer.maximumNumberOfLines = context.environment.lineLimit ?? 0
uiView.textContainer.lineBreakMode = NSLineBreakMode(truncationMode: context.environment.truncationMode)
uiView.invalidateIntrinsicContentSize()

context.coordinator.openURL = context.environment.openURL

textViewStore.didUpdateTextView(uiView)
}

func makeCoordinator() -> Coordinator {
Expand Down

0 comments on commit bf076de

Please sign in to comment.