Learn Courses My Dashboard

Swiftui textfield unresponsive on clicks and holds

When I tap on the textfield it does not respond.
Here is a clip of the weird behaviour…
https://drive.google.com/file/d/16HwOhCsTRzVpg8INMrvxmG4FofnUcr6G/view?usp=sharing

Any ideas on how to make it responsive?

Please show us some code to help us diagnose (and hopefully solve!) the problem.

This is the code for the name field (It is imbedded in a Vstack then a Vstack then a GeoReader)

    var nameField : some View
    {
        TextField(editMode == .inactive ? $message.wrappedValue: "Click 'Done' button to continue", text: self.$enteredName)
                .onSubmit
                {
                    self.addEnteredName()
                }
        
    }

here is all the states and environment objects…

    @EnvironmentObject var gameSettings:GameSetting
    @State var enteredName : String = ""
    @State private var editMode: EditMode = .inactive
    @State private var message:String = "You must have 4 players! Enter here"
    @State private var previousMessage:String = ""
    @FocusState private var isTextFieldFocused: Bool

Unfortunately, that’s really not enough code to help. Please provide a complete View, preferably stripped of as much as possible except what causes the issue. You want a minimal example that can reproduce the issue.

Like here, I can see that you have a nameField computed property that generates a TextField, but how is it put into the greater View scope? You say it’s in a VStack then a VStack then a GeometryReader but that can encompass a wide variety of layouts. Are there any modifiers attached to these elements? Is the nameField the first item in its VStack? The second? What else is in the stack? etc.

import SwiftUI
import Combine
import Foundation
import Neumorphic




struct CreateNames: View
{

    @EnvironmentObject var gameSettings:GameSetting
    @State var enteredName : String = ""
    @State private var editMode: EditMode = .inactive
    @State private var message:String = "You must have 4 players! Enter here"
    @State private var previousMessage:String = ""
    @FocusState private var isTextFieldFocused: Bool



//--functions--\\

    func updatePlayers()
    {
        $gameSettings.players.wrappedValue = $gameSettings.namesInOrder.wrappedValue.count
    }
    
    func canPlay() -> Bool
    {
        if $gameSettings.namesInOrder.wrappedValue.count >= gameSettings.minimumPlayers && $gameSettings.namesInOrder.wrappedValue.count <= gameSettings.maximimPlayers && $editMode.wrappedValue == .inactive
        {
            return true
        }
        else
        {
            return false
        }
    }
    
    func updateMessageForPlayerCount()
    {
        if $gameSettings.namesInOrder.wrappedValue.count >= gameSettings.minimumPlayers && $gameSettings.namesInOrder.wrappedValue.count < gameSettings.maximimPlayers
        {
            $message.wrappedValue = "You can now play!"
        }
        else if $gameSettings.namesInOrder.wrappedValue.count != 10
        {
            $message.wrappedValue = "You need \(gameSettings.minimumPlayers-$gameSettings.namesInOrder.wrappedValue.count) more player\(gameSettings.minimumPlayers-$gameSettings.namesInOrder.wrappedValue.count==1 ? "":"s")"
        }
    }
    
    func startMessage() -> String
    {
        if $gameSettings.namesInOrder.wrappedValue.count >= gameSettings.minimumPlayers && $gameSettings.namesInOrder.wrappedValue.count < gameSettings.maximimPlayers
        {
            return "You can now play!"
        }
        else if $gameSettings.namesInOrder.wrappedValue.count != 10
        {
            return "You need \(gameSettings.minimumPlayers-$gameSettings.namesInOrder.wrappedValue.count) more player\(gameSettings.minimumPlayers-$gameSettings.namesInOrder.wrappedValue.count==1 ? "":"s"). Enter here!"
        }
        else
        {
            return "You can play!"
        }
    }
    
    
    func nameIsNotUsed() -> Bool
    {
        var counter = 0
        for _ in $gameSettings.namesInOrder.wrappedValue
        {
            if $enteredName.wrappedValue.capitalized.filter({$0 != " "}) == $gameSettings.namesInOrder.wrappedValue[counter].capitalized.filter({$0 != " "})
            {
                return false
            }
            else
            {
                counter+=1
            }
        }
        return true
    }
    

    func addEnteredName()
    {
        if $gameSettings.namesInOrder.wrappedValue.count < gameSettings.maximimPlayers
        {
            if $enteredName.wrappedValue.isEmptyOrWhitespace() == false
            {
                if $enteredName.wrappedValue.count<30
                {
                    if nameIsNotUsed()
                    {
                        $gameSettings.namesInOrder.wrappedValue.append(enteredName)
                        self.enteredName = ""
                        updateMessageForPlayerCount()

                    }
                    else
                    {
                        self.enteredName = ""
                        $message.wrappedValue = "You have already entered that name"
                    }
                }
                else
                {
                    self.enteredName = ""
                    $message.wrappedValue = "That name is too big!"
                }

            }
            else
            {
                self.enteredName = ""
                $message.wrappedValue = "That name was blank!"
            }
        }
        else
        {
            self.enteredName = ""
            $message.wrappedValue = "You have reached max players"
        }
        
        print($gameSettings.namesInOrder.wrappedValue)


    }

    func move(from source : IndexSet, to destination : Int) {
        $gameSettings.namesInOrder.wrappedValue.move(fromOffsets: source, toOffset: destination)
    }

    func delete(at offsets : IndexSet)
    {
        $gameSettings.namesInOrder.wrappedValue.remove(atOffsets: offsets)
        
        updateMessageForPlayerCount()
    }

    
    
    
    
    
    
    
//--custom Views--\\


    var nameField : some View
    {
        TextField(editMode == .inactive ? $message.wrappedValue: "Click 'Done' button to continue", text: self.$enteredName)
                .onSubmit
                {
                    self.addEnteredName()
                }
        
    }
    
    
    var addNameBtn : some View
    {
    
            Button(action: self.addEnteredName, label: {
                Text("Add")

            })
        
    }



    private var editButton: some View {
        return Button {
            if editMode == .inactive
            {
                editMode = .active
                $previousMessage.wrappedValue = $message.wrappedValue
                $message.wrappedValue = "Click the 'Done' button to continue"
            }
            else
            {
            editMode = .inactive
                updateMessageForPlayerCount()
                
            }
        }
        label: {
            Text(editMode == .inactive ? "Edit" : "Done")
                .frame( alignment: .trailing)
        }
    }


//--BODY--\\
    
    var body: some View
    {
        GeometryReader
        { geo in

                ZStack
                    {
                        VStack
                        {
                            nameField
//                                .font(.system(size: geo.size.width*0.05))
//                                .frame(width: geo.size.width, height: geo.size.height*0.1)
//                                .border($isTextFieldFocused.wrappedValue==true ? .cyan:.gray, width:$isTextFieldFocused.wrappedValue==true ? 2:0)
//                                .padding(.leading, 15)
//                                .font(.system(size: geo.size.width*0.05))
//                                .textFieldStyle(.roundedBorder)
//                                .focused($isTextFieldFocused)
                            HStack
                            {
                                Spacer()
                                addNameBtn
                                Spacer()
                                editButton
                                Spacer()
                            }
                            
                            
                            List
                            {
                                ForEach(self.$gameSettings.namesInOrder, id: \.self)
                                { name in
                                    Text(name.wrappedValue)
                                }
                                .onMove(perform: self.move)
                                .onDelete(perform: self.delete)
                                
                            }
                            .frame(height: geo.size.height*0.7)
                            .environment(\.editMode, $editMode)
                            
                        }
                        .offset(y:-geo.size.height*0.05)
                        
                        NavigationLink(destination: RolePage())
                        {
                            Text("Set Roles")
                                .font(.system(size: geo.size.width*0.14, weight: .heavy, design: .rounded))
                                .frame(width: geo.size.width*0.7, height: geo.size.height*0.06)
                                .padding()
                                .background(Color.blue)
                                .foregroundColor(.white)
                                .clipShape(RoundedRectangle(cornerRadius: 18))
                        }
                        .disabled(canPlay() ? false:true)
                        .opacity(canPlay() ? 1:0.3)
                        .offset(y: geo.size.height*0.5)
                        .padding(.bottom, geo.size.height*0.05)

                        


                        
                }
                .onDisappear
                    {
                        updatePlayers()
                    }
        }
    }






}

struct CreateNames_Previews: PreviewProvider {
    static var previews: some View {
        CreateNames()
            .environmentObject(GameSetting())
    }
}

It’s this line that is preventing you from editing the TextField:

.offset(y:-geo.size.height*0.05)

That line is shoving your VStack up under the NavigationView and blocking taps on the field. You can see this by assigning a background color to the NavigationView's header.

with negative y offset:
VStack with padding

without negative y offset:
VStack without padding

Side note: You don’t need to do this all the time:

$message.wrappedValue = "whatever"

Just assign to message normally:

message = "whatever"

Thank you! Removing that line of code solved the problem, just one more thing, is there a way to make the three items (the name field, add button and edit button) be a navigation bar item?

I tried to do this by embedding the Zstack in a “NavigationView” but then there is two toolbars, one with the back button and then underneath is the three items and it pushes the three items down.