Hello everyone, I’ve taken on a challenge to make my own app, so I’ve began programming and I encountered a problem.
It has to do with the Keyboard Avoidance feature from SwiftUI, now I don’t want it to not work because I’ve tried to make my own keyboard avoidance without the one from SwiftUI interrupting it, but I couldn’t make it work. However, my problem would be solved If I could just adjust the feature.
Let me explain:
If I run the view without any adjustments regarding to the keyboard avoidance, I get this result:
code:
struct WelcomeView: View {
@Binding var step: Step
@Binding var username: String
@Environment(\.colorScheme) private var colorScheme
@State private var showConfirmationDialog = false
// Properties to coordinate the view
private var isBeingViewed: Bool { self.step == .welcome }
@FocusState private var focused: Bool
var body: some View {
// Content
VStack(spacing: 0) {
Spacer()
.frame(height: 157)
// Titles
self.titles
Spacer()
.frame(height: 165)
// Collecting the username
self.usernameCollection
// Log in button
self.loginButton
Spacer()
.frame(height: 330)
}
.draggable(onEnded: { value in
// Change the focus state if the height of the translation is big enough
if value.translation.height > 30 {
self.focused = false
}
})
.confirmationDialog("Username confirmation", isPresented: $showConfirmationDialog) {
Button {
step = .profile
} label: {
Text("Continue as '\(self.username)'")
}
}
}
// MARK: - Components
private var titles: some View {
// Titles
VStack(spacing: 10) {
Text("Welcome to")
.foregroundColor(Color("Text"))
.font(.title)
.bold()
.adaptiveShadow(color: Color("Shadow").opacity(0.2), radius: 10)
ZStack {
if colorScheme == .light {
Ellipse()
.fill(.radialGradient(
colors: [.black, .clear],
center: .center,
startRadius: 15, endRadius: 200))
.frame(height: 63)
.offset(y: 27)
.blur(radius: 11)
.opacity(0.2)
}
Text("Chataboo")
.font(.chatabooFont(64))
.customGradientFill(width: 260, height: 56)
.shadow(color: Color("Shadow").opacity(0.2), radius: 7, x: 0, y: 0)
}
}
}
private var usernameCollection: some View {
// Collecting the username
VStack(spacing: 20) {
Text("How should we call you?")
.foregroundColor(Color("Text"))
.font(.title2)
.bold()
.adaptiveShadow(color: Color("Shadow").opacity(0.2), radius: 10)
TextField("Username", text: $username)
.focused($focused)
.textFieldStyle(Custom(input: username))
.onChange(of: self.showConfirmationDialog) { _ in
if !showConfirmationDialog && isBeingViewed {
focused = true
}
}
.toolbar() {
if isBeingViewed {
ToolbarItem(placement: .keyboard) {
Button {
// Dismiss the keyboard
focused = false
// Show the confirmation dialog
if username != "" && isBeingViewed {
showConfirmationDialog = true
}
} label: {
Text("Done")
.bold()
.tint(Color("Accent"))
}
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
}
}
}
private var loginButton: some View {
// Log in button
HStack {
Spacer()
Button("**Log in**") {}
.foregroundColor(Color("Accent"))
}
.padding(.horizontal, 20)
.padding(.top, 10)
}
}
(if you are wondering what .draggable
is, it is just an extension I’ve made to implement the drag down to dismiss keyboard feature faster:)
// Drag Gesture
struct Draggable: ViewModifier {
var onChanged: ((DragGesture.Value) -> Void)?
var onEnded: ((DragGesture.Value) -> Void)?
func body(content: Content) -> some View {
content
.gesture(dragDownToCloseKeyboard)
}
var dragDownToCloseKeyboard: some Gesture {
DragGesture()
.onChanged({ value in
if onChanged != nil {
self.onChanged!(value)
}
})
.onEnded { value in
if onEnded != nil {
self.onEnded!(value)
}
}
}
}
/// A drag gesture applied on the view
extension View {
func draggable(onChanged: ((DragGesture.Value) -> Void)? = nil, onEnded: ((DragGesture.Value) -> Void)? = nil) -> some View {
// Switch depending on the parameters
if onChanged != nil && onEnded != nil {
return self.modifier(Draggable(onChanged: { value in
onChanged!(value)
}, onEnded: { value in
onEnded!(value)
}))
}
else if onChanged != nil && onEnded == nil {
return self.modifier(Draggable(onChanged: { value in
onChanged!(value)
}))
}
else if onChanged == nil && onEnded != nil {
return self.modifier(Draggable(onEnded: { value in
onEnded!(value)
}))
}
else {
return self.modifier(Draggable())
}
}
}
As you can see, the view elements are pushed up beyond the screen bounds, and that is what I want to avoid. I think this has something to do with the safe area’s but I’m not sure…
If you know a way to do this, please share it with me, any help or opinion would be much appreciated!