Created
July 1, 2022 05:38
-
-
Save samrshi/9befeb91e187dae2c9629fac41452e9b to your computer and use it in GitHub Desktop.
SwiftUI - Snapshot the view inside a UIViewRepresentable (barely) using Combine
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 SwiftUI | |
import Combine | |
struct ContentView: View { | |
@State private var text = "A\nB\nC\nD\nE\nF\nG\nH" | |
@State private var image: Image? | |
private let snapshotRequester = PassthroughSubject<Void, Never>() | |
var body: some View { | |
VStack { | |
GroupBox { | |
image | |
} label: { | |
Label("Resuting Image", systemImage: "photo") | |
} | |
Button { | |
snapshotRequester.send() | |
} label: { | |
Label("Take Snapshot", systemImage: "camera") | |
} | |
GroupBox { | |
TextView(text: $text, snapshotRequester: snapshotRequester, didTakeSnapshot: didTakeSnapshot) | |
.frame(height: 88) | |
} label: { | |
Label("Text View", systemImage: "square.and.pencil") | |
} | |
Spacer() | |
} | |
.padding() | |
} | |
func didTakeSnapshot(snapshot: UIImage) { | |
self.image = Image(uiImage: snapshot) | |
} | |
} |
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 SwiftUI | |
import Combine | |
struct TextView: UIViewRepresentable { | |
@Binding var text: String | |
let snapshotRequester: PassthroughSubject<Void, Never> | |
let didTakeSnapshot: (UIImage) -> Void | |
func makeCoordinator() -> Coordinator { | |
return Coordinator(text: $text, snapshotRequester: snapshotRequester, didTakeSnapshot: didTakeSnapshot) | |
} | |
func makeUIView(context: Context) -> UITextView { | |
let textView = UITextView() | |
textView.text = text | |
textView.delegate = context.coordinator | |
context.coordinator.textView = textView | |
return textView | |
} | |
func updateUIView(_ textView: UITextView, context: Context) { | |
textView.text = text | |
} | |
} | |
extension TextView { | |
class Coordinator: NSObject, UITextViewDelegate { | |
@Binding private var text: String | |
var cancellable: AnyCancellable? | |
var textView: UITextView! | |
init(text: Binding<String>, snapshotRequester: PassthroughSubject<Void, Never>, didTakeSnapshot: @escaping (UIImage) -> Void) { | |
_text = text | |
super.init() | |
cancellable = snapshotRequester | |
.sink { [weak self] _ in | |
guard let snapshot = self?.takeSnapshot() else { return } | |
didTakeSnapshot(snapshot) | |
} | |
} | |
func textViewDidChange(_ textView: UITextView) { | |
text = textView.text | |
} | |
func takeSnapshot() -> UIImage { | |
// replace this with whatever snapshotting implementation works best for you | |
let renderer = UIGraphicsImageRenderer(size: textView.contentSize) | |
let rect = CGRect(origin: .zero, size: textView.contentSize) | |
return renderer.image { _ in | |
textView.attributedText.draw(in: rect) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If I were doing this in a real application, I would likely create a separate object to communicate between the Coordinator and parent view that encapsulates the bridging logic (containing all the parameters I pass in,
@Published
properties, etc)