I have a TextField in SwiftUI. When I apply padding to it as
TextField(text, text: $value)
.padding()
the padding is outside of the tap area of the TextField, i.e. tapping on the padding does not bring the text field into focus.
I would like to be able to focus the TextField even if the user taps on the padding.
You can try this. At least worked for me. Hope that helps someone:
TextField("", text: $textfieldValueBinding)
.frame(height: 48)
.padding(EdgeInsets(top: 0, leading: 6, bottom: 0, trailing: 6))
.cornerRadius(5)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(lineWidth: 1.0)
)
you can check this. for now i only did with top and bottom padding. you can do the same with the leading and trailing(or with horizontal and vertical).
as asked in the question this would do this
"I would like to be able to focus the TextField even if the user taps on the padding."
struct ContentView: View {
#State var name = ""
#State var isTextFieldFocused = false
var body: some View {
ZStack {
HStack{
Text(name)
.font(.system(size: 50 , weight: .black))
.foregroundColor(isTextFieldFocused ? Color.clear : Color.black)
Spacer()
}
TextField(name, text: $name , onEditingChanged: { editingChanged in
isTextFieldFocused = editingChanged
})
.font(.system(size: isTextFieldFocused ? 50 : 100 , weight: .black))
.foregroundColor(isTextFieldFocused ? Color.black : Color.clear)
.frame(width: 300, height: isTextFieldFocused ? 50 : 100 , alignment: .center)
.padding(.leading, isTextFieldFocused ? 25 : 0 )
.padding(.trailing, isTextFieldFocused ? 25 : 0 )
.padding(.top,isTextFieldFocused ? 25 : 0 )
.padding(.bottom,isTextFieldFocused ? 25 : 0 )
}.frame(width: 300)
.background(Color.red.opacity(0.2))
}
}
Yes, but you have to create your own TextFieldStyle. Here's an example:
struct CustomTextField: View {
public struct CustomTextFieldStyle : TextFieldStyle {
public func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.font(.largeTitle) // set the inner Text Field Font
.padding(10) // Set the inner Text Field Padding
//Give it some style
.background(
RoundedRectangle(cornerRadius: 5)
.strokeBorder(Color.primary.opacity(0.5), lineWidth: 3))
}
}
#State private var password = ""
#State private var username = ""
var body: some View {
VStack {
TextField("Test", text: $username)
.textFieldStyle(CustomTextFieldStyle()) // call the CustomTextField
SecureField("Password", text: $password)
.textFieldStyle(CustomTextFieldStyle())
}.padding()
}
}
For iOS 15 and above you can use this:
var body: some View {
TextField("placeholder", text: $text).focusablePadding()
}
extension View {
func focusablePadding(_ edges: Edge.Set = .all, _ size: CGFloat? = nil) -> some View {
modifier(FocusablePadding(edges, size))
}
}
private struct FocusablePadding : ViewModifier {
private let edges: Edge.Set
private let size: CGFloat?
#FocusState private var focused: Bool
init(_ edges: Edge.Set, _ size: CGFloat?) {
self.edges = edges
self.size = size
self.focused = false
}
func body(content: Content) -> some View {
content
.focused($focused)
.padding(edges, size)
.contentShape(Rectangle())
.onTapGesture { focused = true }
}
}
TextField(
"TextFiled",
text: $teamNames,
prompt: Text("Text Field")
.foregroundColor(.gray)
)
.padding(5)
.frame(width: 250, height: 50, alignment: .center)
.background(
RoundedRectangle(cornerRadius: 25, style: .continuous)
.foregroundColor(.white)
.padding(.horizontal, -30)
)
I don't know if you can give padding inside of a TextField, but you can pad it from outside environment and give it a background of a shape with the same color of your TextField background.
Try this;
TextField("Username", text: $value)
.background(Color.yellow)
.padding(50)
.background(Capsule().fill(Color.white))
Related
I want to set the text as header when the field enter But it does not
work as I want. This is my code section It works but whenever I select
the texts and then write again it shows clear foreground text.
struct PasswordField: View{
#State var isTapped = true
let textFieldHeight: CGFloat = 50
private var placeHolderText: String = ""
#State var leadingImage: String = "lock.fill"
#Binding var text: String
#State private var isEditing = false
public init(placeHolder: String,
text: Binding<String>) {
self._text = text
self.placeHolderText = placeHolder
self.leadingImage = leadingImage
}
var shouldPlaceHolderMove: Bool {
isEditing || (text.count != 0)
}
var body: some View {
ZStack(alignment: .leading) {
ZStack {
if isTapped{
// SecureField("",text: $text)
ZStack {
SecureField("", text: $text)
.font(.system(size: 22))
TextField("", text: $text, onEditingChanged: { (edit) in
isEditing = edit
})
.foregroundColor(.clear)
.disableAutocorrection(true)
.font(.system(size: 22))
}
.padding(.leading,40)
.padding(.trailing,50)
.foregroundColor(Color.white)
.accentColor(Color.teal)
.font(.system(size: 22))
.animation(.linear, value: true)
} else{
TextField("", text: $text, onEditingChanged: { (edit) in
isEditing = edit
})
.padding(.leading,40)
.padding(.trailing,50)
.foregroundColor(Color.white)
.accentColor(Color.teal)
.font(.system(size: 22))
.animation(.linear, value: true)
}
}.overlay(alignment: .trailing){
Image(systemName: isTapped ? "eye.slash.fill": "eye.fill").font(.system(size: 28))
.foregroundColor(.gray).padding(4).onTapGesture{
isTapped.toggle()
}
}
//Floating Placeholder
Text(placeHolderText)
.foregroundColor(.white)
.padding(shouldPlaceHolderMove ?
EdgeInsets(top: 2, leading:40, bottom: textFieldHeight, trailing: 0) :
EdgeInsets(top: 0, leading:50, bottom: 0, trailing: 0))
.scaleEffect(shouldPlaceHolderMove ? 1.2 : 1.3)
.animation(.easeInOut, value: 91)
.overlay(alignment: .leading){
Image(systemName: leadingImage)
.foregroundColor(Color.gray)
.font(.system(size: 28)).padding(4)
}
}
.underlineTextField()
.foregroundColor(shouldPlaceHolderMove ? Color.teal: Color.white)
.frame( height: 90)
}
}
it does not work properly. let me know where I am getting wrong? in my
code Textfield used for isEditting attribute Use only. is there any
other way for getting this functionality in the SecuredField.
As the title says, I'm trying to add some leading padding if the user provides a symbol for the icon. This works just fine in the TextField View but a SecureField doesn't have this as an option. Is there an alternative that I'm missing?
struct PasswordInputView: View {
#Binding var password: String
let placeholder: String
let sfSymbol: String?
var body: some View {
SecureField(placeholder, text: $password)
.frame(maxWidth: .infinity,
minHeight: 44)
.padding(.leading, sfSymbol == nil ? textFieldLeader / 2 : textFieldLeader) <---- ERROR: Cannot find textFieldLeader
.background(
ZStack {
if let systemName = sfSymbol {
Image(systemName: systemName)
.font(.system(size: 16, weight: .semibold))
.padding(.leading, 5)
.foregroundColor(Color.gray.opacity(0.5))
}
RoundedRectangle(cornerRadius: 10, style: .continuous)
.stroke(Color.gray.opacity(0.25))
}
)
}
}
When i have TextField inside ScrollView and tap on it the keyboard is shown as expected. But it seems that the TextField is moved up just enough to show the input area but i want to be moved enough so that is visible in its whole. Otherwise it looks cropped. I couldn't find a way to change this behaviour.
struct ContentView: View {
#State var text:String = ""
var body: some View {
ScrollView {
VStack(spacing: 10) {
ForEach(1...12, id: \.self) {
Text("\($0)…")
.frame(height:50)
}
TextField("Label..", text: self.$text)
.padding(10)
.background(.white)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(.blue, lineWidth: 1)
)
}
.padding()
.background(.red)
}
}
}
Use a .safeAreaInset modifier.
#State var text:String = ""
var body: some View {
ScrollView {
VStack(spacing: 10) {
ForEach(1...12, id: \.self) {
Text("\($0)…")
.frame(height:50)
}
TextField("Label..", text: self.$text)
.padding(10)
.background(.white)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(.blue, lineWidth: 1)
)
}
.padding()
.background(.red)
}.safeAreaInset(edge: .bottom) { //this will push the view when the keyboad is shown
Color.clear.frame(height: 30)
}
}
You can provide additional padding to the view (and it works even in iOS 13 and 14):
struct ContentView: View {
#State var text:String = ""
var body: some View {
ScrollView {
VStack(spacing: 10) {
ForEach(1...12, id: \.self) {
Text("\($0)…")
.frame(height:50)
}
TextField("Label..", text: self.$text)
.padding(10)
.background(.white)
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(.blue, lineWidth: 1)
)
.padding(.bottom, 32) //here, set as much pasding as you want
}
.padding()
.background(.red)
}
}
}
I have a swift view that consists of a HStack with a rectangle and a Vstack of text inside. I want to make the height of the rectangle the same as the height of the Vstack. I have already tried looking through many other questions here on StackOverflow but didn't find an answer. Can anyone help me do that?
Here is my code:
struct TodoView: View {
#State var todos = ["feed the dog", "take the dog out for a walk", "make coffee"]
#State var height: CGFloat = 45
var body: some View {
HStack{
RoundedRectangle(cornerRadius: 2)
.frame(width: 1)
.foregroundColor(Color("lightGray"))
.padding()
VStack{
Text("Todo")
.font(.title)
ForEach(todos, id: \.self){ todo in
Text(todo)
}
}
Spacer()
}
}
}
You need to know the GeometryReader and PreferenceKey to make this possible.
struct SiblingHeightKey: PreferenceKey {
static var defaultValue: CGSize? {
nil
}
static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
value = value ?? nextValue()
}
}
struct TodoView: View {
#State var vStackSize: CGSize? = nil
#State var todos = ["feed the dog", "take the dog out for a walk", "make coffee"]
#State var height: CGFloat = 45
var body: some View {
HStack{
RoundedRectangle(cornerRadius: 2)
.foregroundColor(.gray)
.frame(width: self.vStackSize?.width, height: self.vStackSize?.height)
VStack{
Text("Todo")
.font(.title)
ForEach(todos, id: \.self){ todo in
Text(todo)
}
}.background(
GeometryReader { proxy in
Color.clear.preference(key: SiblingHeightKey.self, value: proxy.size)
}
)
Spacer()
}.onPreferenceChange(SiblingHeightKey.self) {
self.vStackSize = $0
}
}
}
You can use .frame modifier:
HStack{
RoundedRectangle(cornerRadius: 2)
.frame(width: 1, height: 50)
.foregroundColor(Color("lightGray"))
.padding()
VStack {
Text("Todo")
.font(.title)
ForEach(todos, id: \.self){ todo in
Text(todo)
}
.frame(height: 50)
}
Spacer()
}
If you want to have them fill the whole View:
.frame(minWidth: 0, maxWidth: .infinity)
Alternatively, you can use GeometryReader as proposed here: Make a grid of buttons of same width and height in SwiftUI.
Xcode 11.2.1, Swift 5
I have a custom view in SwiftUI and I have been fiddling around with it for a while to try and get rid of the extra space being added. I cannot tell if this is a bug in SwiftUI itself or if I am doing something wrong.
View:
struct Field: View {
#Binding var text: String
var title: String
var placeholderText: String
var leftIcon: Image?
var rightIcon: Image?
var onEditingChanged: (Bool) -> Void = { _ in }
var onCommit: () -> Void = { }
private let height: CGFloat = 47
private let iconWidth: CGFloat = 16
private let iconHeight: CGFloat = 16
init(text: Binding<String>, title: String, placeholder: String, leftIcon: Image? = nil, rightIcon: Image? = nil, onEditingChanged: #escaping (Bool) -> Void = { _ in }, onCommit: #escaping () -> Void = { }) {
self._text = text
self.title = title
self.placeholderText = placeholder
self.leftIcon = leftIcon
self.rightIcon = rightIcon
self.onEditingChanged = onEditingChanged
self.onCommit = onCommit
}
var body: some View {
VStack(alignment: .leading, spacing: 3) {
Text(title.uppercased())
.font(.caption)
.fontWeight(.medium)
.foregroundColor(.primary)
.blendMode(.overlay)
Rectangle()
.fill(Color(red: 0.173, green: 0.173, blue: 0.180))
.blendMode(.overlay)
.cornerRadius(9)
.frame(height: height)
.overlay(
HStack(spacing: 16) {
if leftIcon != nil {
leftIcon!
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: iconWidth, height: iconHeight)
.foregroundColor(.secondary)
}
ZStack(alignment: .leading) {
if text.isEmpty {
Text(placeholderText)
.foregroundColor(.secondary)
.lineLimit(1)
.truncationMode(.tail)
}
TextField("", text: $text, onEditingChanged: onEditingChanged, onCommit: onCommit)
.foregroundColor(.white)
.lineLimit(1)
.truncationMode(.tail)
}
Spacer()
if rightIcon != nil {
rightIcon!
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: iconWidth, height: iconHeight)
.foregroundColor(.secondary)
}
}
.padding(.horizontal, 16)
)
}
}
}
When I create an instance of it like so:
struct ContentView: View {
#State var text = ""
let backgroundGradient = Gradient(colors: [
Color(red: 0.082, green: 0.133, blue: 0.255),
Color(red: 0.227, green: 0.110, blue: 0.357)
])
var body: some View {
ZStack {
LinearGradient(gradient: backgroundGradient, startPoint: .top, endPoint: .bottom)
.edgesIgnoringSafeArea(.all)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
Field(text: $text, title: "field", placeholder: "Required", leftIcon: Image(systemName: "square.and.pencil"), rightIcon: Image(systemName: "info.circle"))
.padding(.horizontal, 30)
}
.onAppear {
UIApplication.shared.windows.forEach { window in
window.overrideUserInterfaceStyle = .dark
}
}
}
}
At first glance, it seems to behave as expected:
The problem appears after the user tries typing in the field. There is too much spacing between the right icon and the text, causing it to be truncated earlier than necessary.
As you can see, there is a much larger amount of spacing between the text field and the right icon (excess spacing) than there is between the text field and the left icon (correct spacing).
All elements of Field should have a spacing of 16 on all sides. For some reason, the TextField is not taking up enough space and thus its spacing from the right icon is less than the desired 16.
How can I fix this spacing?
You have the following:
Spacer()
before the following section of code:
if rightIcon != nil {
rightIcon!
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: iconWidth, height: iconHeight)
.foregroundColor(.secondary)
}
Removing the Spacer() will give you the same pixels between the left icon and the right icon.
*SwiftUI
///For vertical Custom Spaces
.padding(.vertical, 6)
//For Horizontal
.padding(. horizontal, 6)
//For uniform Spacting
Spacer()
///