Assistance with keyboard avoidance

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!

Why are you assigning frame sizes to your Spacer() elements. Leave them as just Spacer() and as the keyboard pushes the View up the spacing will adjust flexibly and your view elements should remain visible.

Thanks for your reply @Chris_Parker

I do this to make sure the elements of my view are in the right place, so they align perfectly with my design.

I’ve removed these frames, but it still didn’t work. After trying some things like padding to place the view back in its initial place, everything seems to be working fine!

Thanks for your help!