My first ever question here at Stack Overflow.
I'm writing a small application for iOS and macOS. Text entry is done via (now) a TextField
and a Button
. The issue with the TextField is that it's a single line and doesn't allow for multiline text entry. So, I tried using TextEditor
instead, but I can either set it up to not grow as more text is added, or it shows up very big to begin with.
What I'm saying is that ideally, it would mimic the behavior that the text entry in iMessage has: starts as the same size of a TextField
but grows if needed to accommodate a multiline text like a TextEditor
.
Here's the code I am using for this view:
var inputView: some View {
HStack {
ZStack {
//tried this here...
//TextEditor(text: $taskText)
TextField("New entry...", text: $taskText, onCommit: { didTapAddTask() })
.frame(maxHeight: 35)
.padding(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 0))
.clipped()
.accentColor(.black)
.cornerRadius(8)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text(taskText).opacity(0).padding(.all, 8)
}
Button(action: AddNewEntry, label: { Image(systemName: "plus.circle")
.imageScale(.large)
.foregroundColor(.primary)
.font(.title) }).padding(15).foregroundColor(.primary)
}
}
Any way of doing this? I tried different approaches found in different questions from other users, but I can't quite figure out.
Here's how it looks:
I also have tried something like this and played with different values for the .frame:
TextEditor(text: $taskText)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 200)
.border(Color.primary, width: 1)
.padding(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 0))
Any help is appreciated.
Thanks.
Oh, and I'm using Xcode 12.5.1 and target is iOS 14.x and macOS Big Sur for now.
EDIT to answer jnpdx. When I add the code from Dynamic TextEditor overlapping with other views this is how it looks, and it does not change dynamically.
Here's an example, using your original code with the Button
next to the TextEditor
. The TextEditor
grows until it hits the limit, defined by maxHeight
. It also has a view for the messages (since you mentioned iMessage), but you could easily remove that.
struct ContentView: View {
@State private var textEditorHeight : CGFloat = 100
@State private var text = "Testing text. Hit a few returns to see what happens"
private var maxHeight : CGFloat = 250
var body: some View {
VStack {
VStack {
Text("Messages")
Spacer()
}
Divider()
HStack {
ZStack(alignment: .leading) {
Text(text)
.font(.system(.body))
.foregroundColor(.clear)
.padding(14)
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height)
})
TextEditor(text: $text)
.font(.system(.body))
.padding(6)
.frame(height: min(textEditorHeight, maxHeight))
.background(Color.black)
}
.padding(20)
Button(action: {}) {
Image(systemName: "plus.circle")
.imageScale(.large)
.foregroundColor(.primary)
.font(.title)
}.padding(15).foregroundColor(.primary)
}.onPreferenceChange(ViewHeightKey.self) { textEditorHeight = $0 }
}
}
}
struct ViewHeightKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}