Tusker/Tusker/Screens/Compose/ComposeTextView.swift

126 lines
3.5 KiB
Swift

//
// ComposeTextView.swift
// Tusker
//
// Created by Shadowfacts on 8/18/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import SwiftUI
struct ComposeTextView: View {
@Binding private var text: String
private let placeholder: Text?
private let minHeight: CGFloat
private var heightDidChange: ((CGFloat) -> Void)?
private var backgroundColor = UIColor.secondarySystemBackground
private var fontSize: CGFloat = 20
@State private var height: CGFloat?
init(text: Binding<String>, placeholder: Text?, minHeight: CGFloat = 150) {
self._text = text
self.placeholder = placeholder
self.minHeight = minHeight
}
var body: some View {
ZStack(alignment: .topLeading) {
Color(backgroundColor)
if text.isEmpty, let placeholder = placeholder {
placeholder
.font(.system(size: fontSize))
.foregroundColor(.secondary)
.offset(x: 4, y: 8)
}
WrappedTextView(
text: $text,
textDidChange: self.textDidChange,
font: .systemFont(ofSize: fontSize)
)
.frame(height: height ?? minHeight)
}
}
private func textDidChange(textView: UITextView) {
height = max(minHeight, textView.contentSize.height)
heightDidChange?(height!)
}
func heightDidChange(_ callback: @escaping (CGFloat) -> Void) -> Self {
var copy = self
copy.heightDidChange = callback
return copy
}
func backgroundColor(_ color: UIColor) -> Self {
var copy = self
copy.backgroundColor = color
return copy
}
func fontSize(_ size: CGFloat) -> Self {
var copy = self
copy.fontSize = size
return copy
}
}
struct WrappedTextView: UIViewRepresentable {
typealias UIViewType = UITextView
@Binding var text: String
var textDidChange: ((UITextView) -> Void)?
var font = UIFont.systemFont(ofSize: 20)
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.delegate = context.coordinator
textView.isEditable = true
textView.backgroundColor = .clear
textView.font = font
textView.textContainer.lineBreakMode = .byWordWrapping
return textView
}
func updateUIView(_ uiView: UITextView, context: Context) {
uiView.text = text
context.coordinator.text = $text
context.coordinator.didChange = textDidChange
// wait until the next runloop iteration so that SwiftUI view updates have finished and
// the text view knows its new content size
DispatchQueue.main.async {
self.textDidChange?(uiView)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(text: $text, didChange: textDidChange)
}
class Coordinator: NSObject, UITextViewDelegate {
var text: Binding<String>
var didChange: ((UITextView) -> Void)?
init(text: Binding<String>, didChange: ((UITextView) -> Void)?) {
self.text = text
self.didChange = didChange
}
func textViewDidChange(_ textView: UITextView) {
text.wrappedValue = textView.text
didChange?(textView)
}
}
}
//struct ComposeTextView_Previews: PreviewProvider {
// static var previews: some View {
// ComposeTextView()
// }
//}