i want to put some fixed text inside TextField like "email: ....."
I tryed this:
HStack {
Image(systemName: "list.bullet").foregroundColor(.gray)
Text("email:")
TextField("", text: Binding(
get: { viewModel.email },
set: { viewModel.email = $0 }
))
.textFieldStyle(DefaultTextFieldStyle())
.frame(width: 60)
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(
viewModel.emailValid() ? Color.gray : Color.red, lineWidth: 1
))
The problem is that this implementation only allows to click in specific part (inside the textField space) but i want to make all clickable to be more friendly to edit.
I tryed putting TextField and Text inside ZStack but the text entered by the user in inside te "Email text".
Maybe its possible to move the textField cursor to the end of the "email" text, or another implementation?
Thanks!
So if its mainly about activating the TextField on tap/click on any area of the element, you can use programmatic focus for TextField:
struct ContentView: View {
#State var input = ""
#FocusState var focused: Bool
var body: some View {
HStack {
Image(systemName: "list.bullet").foregroundColor(.gray)
Text("email:")
TextField("", text: $input)
.frame(width: 80)
.focused($focused)
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(
true ? Color.gray : Color.red, lineWidth: 1
))
.contentShape(Rectangle()) // makes the whole element tappable
.onTapGesture {
focused = true // sets focus for textField
}
}
}
Related
I've got a TextField in SwiftUI that is centered on the screen. I want to add a pencil icon immediately. to the left of it to indicate that it is editable - how can I do this? I've tried embedding both the TextField and Image in an HStack like this:
HStack {
Spacer()
Image(systemName: "pencil")
TextField(...)
}
But that only yields something like this:
where the textfield is no longer centered and the pencil is aligned to the left of the screen.
Any guidance is appreciated.
this should do it:
TextField("", text: $input)
.overlay(alignment: .leading) {
Image(systemName: "pencil")
.offset(x: -24, y: 0)
}
struct CustomTextFieldView: View {
#State private var text: String = ""
var body: some View {
VStack(alignment: .leading) {
textfeild
.padding()
}
}
var textfeild: some View {
HStack {
Image(systemName: "pencil")
TextField("Edit me!", text: $text)
}
.textFieldStyle(DefaultTextFieldStyle())
}
}
struct CustomTextFieldView_Previews: PreviewProvider {
static var previews: some View {
CustomTextFieldView()
}
}
We have a custom textfield defined in SwiftUI. This is the basic code for it:
var body: some View {
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: Constants.cornerRadius, style: .continuous)
.stroke(isFocused ? Color.blue : hasWarning ? .red : .gray, lineWidth: Constants.lineWidth)
HStack {
Text(title)
.font(.body)
.foregroundColor(isFocused ? Color.blue : hasWarning ? .red : .gray)
.padding(.horizontal, text.isEmpty ? Constants.Padding.horizontalIsEmpty : Constants.Padding.horizontalIsNotEmpty)
.background(text.isEmpty ? Color.clear : background)
.padding(.leading, Constants.Padding.leading)
.offset(y: text.isEmpty ? Constants.Offset.isEmpty : -(frameHeight / Constants.Offset.isNotEmptyRatio))
.scaleEffect(text.isEmpty ? Constants.ScaleEffect.isEmpty : Constants.ScaleEffect.isNotEmpty, anchor: .leading)
}
HStack {
TextField("", text: $text, onEditingChanged: { inFocus in
self.isFocused = inFocus
})
.font(.body)
.padding(.horizontal, Constants.Padding.horizontal)
}
}
.frame(height: frameHeight)
.background(background)
.animation(.easeInOut(duration: Constants.Animation.easeInOut))
.padding(.top, Constants.Padding.top)
}
Currently there's no viewModel linked to this. Now we want to extend this component to be able to include one (or both of):
An internal button
An icon in the textfield itself
I want to avoid adding any logic into the view and so I initially created a viewModel that looks something like this:
struct InternalTextfieldButtonProperties {
let action: () -> Void
let labelText: String
}
class TextFieldFloatingWithBorderViewModel: ObservableObject {
let internalButtonProperties: InternalTextfieldButtonProperties?
let icon: Image?
var hasButton: Bool {
return internalButtonProperties != nil
}
var hasIcon: Bool {
return icon != nil
}
init(internalButtonProperties: InternalTextfieldButtonProperties? = nil, icon: Image? = nil) {
self.internalButtonProperties = internalButtonProperties
self.icon = icon
}
}
The idea being that you can initialise this and then use the viewModel properties within the view to display the a button and or icon. However, this all seems a bit counterproductive as the icon and button properties in the viewModel would still need to be optional, which means again unwrapping in the view, taking away some of the point of having the viewModel. Indeed, if no icon or button is required, you end up declaring a viewModel for no reason.
I wondered if there's a cleaner way of doing this?
I have a textfield on a form where user can type value, but I would also like to update the content of the textfield with a button.
Here is my code :
struct test: View {
#State private var amount: Double = 0.0
var body: some View {
Form {
VStack {
HStack {
Text("Amount EUR")
Spacer()
TextField("Type amount", value: $amount, format: .number)
.keyboardType(.numberPad)
.multilineTextAlignment(.trailing)
}
Text("Set MAX (999)")
.frame(maxWidth: .infinity, alignment: .leading)
.onTapGesture {
print("before tap \(amount )")
amount = 999
print("after tap \(amount)")
}
}
}
}
When I just launch the app, the first tap on the Text updates the textfield with 999, but after it does not work anymore.
The amount value is correctly updated but the textfield does not reflect the change.
Would you have an explanation ?
The answer is simply that TextFields don't update while they are in focus. To solve this problem, you need to incorporate a #FocusState in to the view, and cause the TextField to lose focus right before updating the variable. You can test it in your own view by tapping your Text prior to tapping in to the TextField. You will see it updates just fine.
struct ButtonUpdateTextField: View {
#State private var amount: Double = 0.0
#FocusState var isFocused: Bool // <-- add here
var body: some View {
Form {
VStack {
HStack {
Text("Amount EUR")
Spacer()
TextField("Type amount", value: $amount, format: .number)
.keyboardType(.numberPad)
.multilineTextAlignment(.trailing)
.focused($isFocused) // <-- add here
}
Text("Set MAX (999)")
.frame(maxWidth: .infinity, alignment: .leading)
.onTapGesture {
print("before tap \(amount )")
isFocused = false // <-- add here
amount = 999
print("after tap \(amount)")
}
}
}
}
}
I am trying to update the style of the HStack containing my text box when the Textbox is selected. In the example below, I want the text box to have a red border when selected, otherwise the border should be gray.
My issue is that the textbox seems to go through an intermediate transition that I don't want, which is the border is updated to red, but the keyboard doesn't pop up until I select the textbox again (The textbox moves up a bit and then goes back down). It seems that there is some issue with the ordering of how the view refresh happens.
#State private var text: String
#State private var textFieldSelected: Bool = false
var body: some View {
let stack = HStack {
TextField("Enter name", text: $text, onEditingChanged: {
(changed) in
textFieldSelected = changed
})
}
if (textFieldSelected) {
stack
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.red, lineWidth: 1))
} else {
stack
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
}
}
Here's a video example of the existing behavior:
Make it even simpler by using ternary condition for the border and the issue won't appear
struct ContentView : View {
#State private var text: String = ""
#State private var textFieldSelected: Bool = false
var body: some View {
HStack {
TextField("Enter name", text: $text, onEditingChanged: {
(changed) in
textFieldSelected = changed
})
}
.overlay(RoundedRectangle(cornerRadius: 10).stroke(textFieldSelected ? Color.red : Color.gray, lineWidth: 1))
}
}
Tested on iPhone 8 Plus iOS 14
I need to make an Alert in SwiftUI that has an editable TextField in it. Currently, this isn't supported by SwiftUI (as of Xcode 11.3), so I'm looking for a work-around.
I know I can implement by wrapping the normal UIKit bits in a UIHostingController, but really want to stick with an all-SwiftUI implementation.
I've got two VStacks in a ZStack, with the front one (the one with the TextView) being hidden and disabled until until you tap the button. Take a look at this:
import SwiftUI
struct ContentView: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
ZStack {
VStack {
Text("Value is \(self.text)")
Button(action: {
print("button")
self.isShowingEditField = true
}) {
Text("Tap To Test")
}
}
.disabled(self.isShowingEditField)
.opacity(self.isShowingEditField ? 0.25 : 1.00)
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
Divider()
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
.padding()
.background(Color.white)
.shadow(radius: CGFloat(1.0))
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)
}
}
}
This seems like it should work to me. Switching between the two VStacks works well, but the TextField is not editable.
It acts like it's disabled, but it's not. Explicitly .disabled(false) to the TextField doesn't help. Also, it should already be enabled anyway since 1) that's the default, 2) the VStack it's in is specifically being set as enabled, and 3) The OK button works normally.
Ideas/workarounds?
Thanks!
You need to force the TextField updating with some methods. Like the following:
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
.id(self.isShowingEditField)
That is really ridiculous, but the problem disappears if you comment just one line of code:
//.shadow(radius: CGFloat(1.0))
I commented it and everything works. I think you need to use ZStack somewhere in your custom alert view to avoid this.. hm, bug, maybe?
update
try some experiments. If you leave just this code in that View:
struct CustomAlertWithTextField: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
TextField("", text: $text)
.padding()
.shadow(radius: CGFloat(1.0))
}
}
it would not work again. only if you comment .padding() or .shadow(...)
BUT if you relaunch Xcode - this code begin to work (which made me crazy). Tried it at Xcode Version 11.2 (11B52)
update 2 the working code version:
struct CustomAlertWithTextField: View {
#State var isShowingEditField = false
#State var text: String = "12345"
var body: some View {
ZStack {
VStack {
Text("Value is \(self.text)")
Button(action: {
print("button")
self.isShowingEditField = true
}) {
Text("Tap To Test")
}
}
.disabled(self.isShowingEditField)
.opacity(self.isShowingEditField ? 0.25 : 1.00)
ZStack {
Rectangle()
.fill(Color.white)
.frame(width: 300, height: 100)
.shadow(radius: 1) // moved shadow here, so it doesn't affect TextField now
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
.frame(width: 298)
Divider().frame(width: 300)
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
}
.padding()
.background(Color.white)
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)
}
}
}
after some research i solved this problem by adding .clipped() to the VStack.
For your problem, it would look like this:
VStack(alignment: .center) {
Text("Edit the text")
TextField("", text: self.$text)
.multilineTextAlignment(.center)
.lineLimit(1)
Divider()
HStack {
Button(action: {
withAnimation {
self.isShowingEditField = false
print("completed... value is \(self.text)")
}
}) {
Text("OK")
}
}
}
.padding()
.clipped() // <- here
.background(Color.white)
.shadow(radius: CGFloat(1.0))
.disabled(!self.isShowingEditField)
.opacity(self.isShowingEditField ? 1.0 : 0.0)