diff --git a/Sources/AttributedText/AttributedTextImpl+UIKit.swift b/Sources/AttributedText/AttributedTextImpl+iOS.swift similarity index 96% rename from Sources/AttributedText/AttributedTextImpl+UIKit.swift rename to Sources/AttributedText/AttributedTextImpl+iOS.swift index f7b59ae..dc7b281 100644 --- a/Sources/AttributedText/AttributedTextImpl+UIKit.swift +++ b/Sources/AttributedText/AttributedTextImpl+iOS.swift @@ -1,4 +1,4 @@ -#if canImport(UIKit) +#if os(iOS) import SwiftUI extension AttributedTextImpl: UIViewRepresentable { @@ -35,9 +35,7 @@ self.backgroundColor = .clear self.textContainerInset = .zero - #if !os(tvOS) - self.isEditable = false - #endif + self.isEditable = false self.isSelectable = false self.isScrollEnabled = false self.textContainer.lineFragmentPadding = 0 diff --git a/Sources/AttributedText/AttributedTextImpl+AppKit.swift b/Sources/AttributedText/AttributedTextImpl+macOS.swift similarity index 99% rename from Sources/AttributedText/AttributedTextImpl+AppKit.swift rename to Sources/AttributedText/AttributedTextImpl+macOS.swift index a087f2d..bb0f869 100644 --- a/Sources/AttributedText/AttributedTextImpl+AppKit.swift +++ b/Sources/AttributedText/AttributedTextImpl+macOS.swift @@ -25,7 +25,6 @@ nsView.textContainer?.lineBreakMode = NSLineBreakMode( truncationMode: context.environment.truncationMode ) - context.coordinator.openLink = onOpenLink ?? { context.environment.openURL($0) } textSizeViewModel.didUpdateTextView(nsView) } diff --git a/Sources/AttributedText/AttributedTextImpl+tvOS.swift b/Sources/AttributedText/AttributedTextImpl+tvOS.swift new file mode 100644 index 0000000..554f281 --- /dev/null +++ b/Sources/AttributedText/AttributedTextImpl+tvOS.swift @@ -0,0 +1,84 @@ +#if os(tvOS) + import SwiftUI + + extension AttributedTextImpl: UIViewRepresentable { + func makeUIView(context: Context) -> TextView { + let uiView = TextView() + + uiView.backgroundColor = .clear + uiView.textContainerInset = .zero + uiView.isScrollEnabled = false + uiView.textContainer.lineFragmentPadding = 0 + uiView.delegate = context.coordinator + + return uiView + } + + func updateUIView(_ uiView: TextView, context: Context) { + uiView.attributedText = attributedText + uiView.maxLayoutWidth = maxLayoutWidth + + uiView.textContainer.maximumNumberOfLines = context.environment.lineLimit ?? 0 + uiView.textContainer.lineBreakMode = NSLineBreakMode( + truncationMode: context.environment.truncationMode + ) + context.coordinator.openLink = onOpenLink ?? { context.environment.openURL($0) } + textSizeViewModel.didUpdateTextView(uiView) + } + + func makeCoordinator() -> Coordinator { + Coordinator() + } + } + + extension AttributedTextImpl { + final class TextView: UITextView { + var maxLayoutWidth: CGFloat = 0 { + didSet { + guard maxLayoutWidth != oldValue else { return } + invalidateIntrinsicContentSize() + } + } + + override var intrinsicContentSize: CGSize { + guard maxLayoutWidth > 0 else { + return super.intrinsicContentSize + } + + return sizeThatFits(CGSize(width: maxLayoutWidth, height: .greatestFiniteMagnitude)) + } + } + + final class Coordinator: NSObject, UITextViewDelegate { + var openLink: ((URL) -> Void)? + + func textView( + _: UITextView, + shouldInteractWith URL: URL, + in _: NSRange, + interaction: UITextItemInteraction + ) -> Bool { + guard case .invokeDefaultAction = interaction else { + return false + } + + if let openLink = self.openLink { + openLink(URL) + return false + } else { + return true + } + } + + func textView( + _: UITextView, + shouldInteractWith _: NSTextAttachment, + in _: NSRange, + interaction _: UITextItemInteraction + ) -> Bool { + // Disable text attachment interactions + false + } + } + } +#endif