Keyboard in a sheet's - ios

I have a basic window with an input field (page 1), on top of it appears a popup window (page 2), inside of which there is also an input fields and buttons, which, when clicked, will bring up a small window with an input field (page 3). If there is no "Done" on the keyboard, the interface functions normally. If you add a "Done" button, it turns out that its color changes from system color blue to gray when moving from page 2 to page 3. Experimenting and wondering why this is so, I found that the toolbar on page 1 is responsible for the color of the button on page 3... If you change the color of the button on the toolbar on page 1 - it will change on the toolbar on page 3, and page 2 will not be affected. Also, adding buttons causes error: "[LayoutConstraints] Unable to simultaneously satisfy constraints." I want to understand why setting the button on the keyboard for Page 1, I also get a button when I type on Page 3? And why is it grayed out and not working? Why if I change the color for the button on Page 1, does it also change for that gray button on Page 3?
A small representative sample:
ContentView
import SwiftUI
struct ContentView: View {
#State private var bloodClucoseLvl: String = ""
#State private var isSheetShown: Bool = false
#FocusState private var focusField: Bool
var body: some View {
NavigationView {
List {
Section("Add your current blood glucose lvl") {
TextField("5,0 mmol/l", text: $bloodClucoseLvl)
.focused($focusField)
}
Section("Add food or drink") {
Button(action:{
isSheetShown.toggle()
}, label:{
HStack{
Text("Add")
Image(systemName: "folder.badge.plus")
}
})
.sheet(isPresented: $isSheetShown) {
addFoodButton()
}
}
}
.navigationTitle("Page 1 - General")
.toolbar{
ToolbarItem(placement: .keyboard) {
HStack {
Spacer()
Button(action: {
focusField = false
}) {
Text("Done")
}
}
}
}
.ignoresSafeArea(.keyboard)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
addFoodButton
import SwiftUI
struct addFoodButton: View {
#State private var selectedFood: String = ""
#State public var addScreen: Bool = false
var body: some View {
ZStack {
NavigationView {
List {
Section("or choose from category"){
NavigationLink(destination: Alcohol(addScreen: $addScreen)){
Text("Alcohol")
}
}
}
.listStyle(.insetGrouped)
.searchable(text: $selectedFood, prompt: "Search by word")
.navigationTitle("Page 2 - Search in DB")
}
if addScreen{
addScreenView(addScreen: $addScreen)
}
}
}
}
struct Alcohol: View {
#State private var searchInsideCategory: String = ""
#Binding var addScreen: Bool
var body: some View {
List {
Button(action: {addScreen.toggle()}){
Text("Light beer")
}
}
.navigationTitle("Page 2 - Choose beer")
.searchable(text: $searchInsideCategory, prompt: "Search inside a category")
}
}
struct addFoodButton_Previews: PreviewProvider {
static var previews: some View {
addFoodButton()
}
}
addScreenView
import SwiftUI
struct addScreenView: View {
#Binding var addScreen: Bool
#State private var gram: String = ""
var body: some View {
ZStack{
Color.black.opacity(0.2).ignoresSafeArea()
VStack(spacing:0){
Text("Page 3 - Add an item")
.bold()
.padding()
Divider()
VStack(){
TextField("gram", text: $gram)
.padding(.leading, 16)
.padding(.trailing, 16)
.keyboardType(.numberPad)
Rectangle()
.frame(height: 1)
.padding(.leading, 16)
.padding(.trailing, 16)
}.padding()
Divider()
HStack(){
Button(action: {
addScreen.toggle()
}){
Text("Cancel").frame(minWidth:0 , maxWidth: .infinity)
}
Divider()
Button(action: {
addScreen.toggle()
}){
Text("Save").frame(minWidth:0 , maxWidth: .infinity)
}
}.frame(height: 50)
}
.background(Color.white.cornerRadius(10))
.padding([.leading, .trailing], 15)
}
}
}
struct addScreenView_Previews: PreviewProvider {
static var previews: some View {
addScreenView(addScreen: .constant(true))
}
}

Related

How to prevent keyboard dismiss when bottomsheet is dismiss using swipe gesture (Interactive dismiss) iOS

I have a requirement where we need to keep close active unless bottom sheet is not closed. I have seen similar implementations in iOS maps where search keyboard is opened until the view is fully dismissed.
What I have currently :-
What I want :- https://drive.google.com/file/d/1SmXniFp0ZTF5igMzk6gk3eflCeP1xjn6/view?usp=share_link
This is code which i use to to present iOS native sheet :-
struct ContentView: View {
#State var isPresented = false
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.onTapGesture {
isPresented.toggle()
}
.sheet(isPresented: $isPresented) {
BottomSheetViewRepresentable(content: {
NavigationView {
DemoView()
}
.navigationTitle("Hey")
}, detents: [.large()])
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct DemoView: View {
var body: some View{
if #available(iOS 16.0, *) {
ZStack {
TextField("", text: .constant("Hey"))
}
.scrollDismissesKeyboard(.never)
} else {
// Fallback on earlier versions
Color.yellow
}
}
}
I see you have added .scrollDismissesKeyboard(.never) on the ZStack which seems not working.
Try wrapping content in a List or ScrollView, then set scrollDismissesKeyboard to never.
Examples -
var body: some View {
ScrollView {
VStack {
TextField("Name", text: $username)
.textFieldStyle(.roundedBorder)
TextEditor(text: $bio)
.frame(height: 400)
.border(.quaternary, width: 1)
}
.padding(.horizontal)
}
.scrollDismissesKeyboard(.never)
}
List example -
struct KeyboardDismissExample: View {
#State private var text = ""
var body: some View {
List {
ForEach(0..<100) { i in
TextField(("Item \(i)"), text: .constant(""))
}
}
.scrollDismissesKeyboard(.never)
}
}

SwiftUI increasing sheet size after NavigationLink with the "selection" in "presentationDetents" cuts off view inside the sheet

Since iOS 16 there is a new feature for the ".sheet" modifier called ".presentationDetents". ".presentationDetents" has a parameter called "selection" where you can pass a Binding. You can programmatically resize the sheet with the "selection" parameter. As soon as you change the sheet size for example from PresentationDetent.medium to PresentationDetent.large right after changed the page with a "NavigationLink" the View below gets cut off:
But if I slightly move (resize) the sheet afterwards the cut off below is going to disappear:
The view hierarchy is also strange:
If you add a delay by 0.6s for resizing the sheet, the cut off won't happen.
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
currentSelection = .large
}
}
You can find the code below:
import SwiftUI
struct ContentView: View {
#State private var sheetIsOpened = false
#State private var currentSelection = PresentationDetent.medium
var body: some View {
Text("Click to open a sheet")
.padding()
.onTapGesture {
sheetIsOpened = true
}
.sheet(isPresented: $sheetIsOpened) {
NavigationStack {
List {
ScrollView {
ForEach(0..<100) { index in
VStack {
NavigationLink(destination: NavigatedView(currentSelection: $currentSelection)) {
Text("I have the index: \(index)")
.foregroundColor(.green)
}
}
.frame(maxWidth: .infinity)
}
}
.padding()
}
}
.presentationDetents([.medium, .large], selection: $currentSelection)
}
}
}
struct NavigatedView: View {
#Binding fileprivate var currentSelection: PresentationDetent
var body: some View {
ScrollView {
ForEach(0..<100) { index in
VStack {
Text("I'm a child and I have the index: \(index)")
.onAppear {
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
currentSelection = .large
// }
}
}
.frame(maxWidth: .infinity)
}
}
.background(.red)
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Cannot add something to an Array

i have made an app in SwiftUI where you can create different classes. The app saves this in an array. I have a textfield and a button in the same view as the scrollview that displays the array. This works perfectly fine and I can easily add new classes. Now I made a new view with a text field and a button. This view can be viewed by pressing a button in the nav bar. It uses the exact same function as the other view, but in the other view adding a item to the array works, in this view it doesn't work. I hope you understand my problem and can help me.
Thank You.
This is the file where I store the array:
import Foundation
import SwiftUI
import Combine
struct Class: Identifiable {
var name: String
var id = UUID()
}
class ClassStore: ObservableObject {
#Published var classes = [Class]()
}
This is the view with the button + textfield that works and the scrollview that displays the array:
import SwiftUI
struct ContentView: View {
#State var showNewClass = false
#ObservedObject var classStore = ClassStore()
#State var newClass: String = ""
#State var toDoColor: Color = Color.pink
func addNewClass() {
classStore.classes.append(
Class(name: newClass)
)
newClass = ""
}
var body: some View {
NavigationView {
VStack {
HStack {
TextField("New Todo", text: $newClass)
Image(systemName: "app.fill")
.foregroundColor(Color.pink)
.padding(.horizontal, 3)
Image(systemName: "books.vertical")
.padding(.horizontal, 3)
if newClass == "" {
Text("Add!")
.foregroundColor(Color.gray)
} else {
Button(action: {
addNewClass()
}) {
Text("Add!")
}
}
}.padding()
ScrollView {
ForEach(self.classStore.classes) { name in
Text(name.name)
}
}
.navigationBarTitle(Text("Schulnoten"))
.navigationBarItems(trailing:
Button(action: {
self.showNewClass.toggle()
}) {
Text("New Class")
}
.sheet(isPresented: $showNewClass) {
NewClass(isPresented: $showNewClass)
})
}
}
}
}
And this is the new view I created, the button and the textfield have the exact same code, but somehow this doesn't work:
import SwiftUI
struct NewClass: View {
#Binding var isPresented: Bool
#State var className: String = ""
#ObservedObject var classStore = ClassStore()
func addNewClass() {
classStore.classes.append(
Class(name: className)
)
}
var body: some View {
VStack {
HStack {
Text("New Class")
.font(.title)
.fontWeight(.semibold)
Spacer()
}
TextField("Name of the class", text: $className)
.padding()
.background(Color.gray)
.cornerRadius(5)
.padding(.vertical)
Button(action: {
addNewClass()
self.isPresented.toggle()
}) {
HStack {
Text("Safe")
.foregroundColor(Color.white)
.fontWeight(.bold)
.font(.system(size: 20))
}
.padding()
.frame(width: 380, height: 60)
.background(Color.blue)
.cornerRadius(5)
}
Spacer()
}.padding()
}
}
Sorry if my English is not that good. I'm not a native speaker.
I assume you should pass same class store from parent view into sheet, ie
struct NewClass: View {
#Binding var isPresented: Bool
#State var className: String = ""
#ObservedObject var classStore: ClassStore // << expect external
and in ContentView
.sheet(isPresented: $showNewClass) {
NewClass(isPresented: $showNewClass, classStore: self.classStore) // << here !!
})

SwiftUI toggle active / inactive line color on custom TextField

I'm trying to create a TextField with a line below it, for that I used a Divide as suggested in this answer and I added a .onTapGesture() to it to change the Divider color, however that's a component that's embedded in another view.
In my ContentView I have a Button and my UnderscoredTextField and they are contained inside a HStack and it's contained inside a Background component (taken from this answer) to be able to dismiss the keyboard programmatically, but I'd like to be able to change the #State var isActive variable inside the UnderscoredTextField when that happens, but as I'm new to SwiftUI I'm not quite sure how to achieve this.
import SwiftUI
struct ContentView: View {
var body: some View {
Background {
UnderscoredTextField(phoneNumber: "")
}.onTapGesture {
self.hideKeyboard()
//How to tell my UnderscoreTextField's isActive variable to change
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
struct UnderscoredTextField: View {
#State var phoneNumber: String
#State var isActive: Bool = false
var body: some View {
VStack {
TextField("12345678", text: $phoneNumber)
.keyboardType(.phonePad)
.onTapGesture {
self.isActive = true
}
Divider()
.padding(.horizontal)
.frame(height: 1)
.background(isActive ? Color.red : Color.gray)
}
}
}
struct UnderscoredTextField_Previews: PreviewProvider {
static var previews: some View {
UnderscoredTextField(phoneNumber: "12345678")
}
}
This is what it looks like when I hide the keyboard, but I'd like to switch it back to gray
If I understood correctly it is enough to use onEditingChanged, like below
var body: some View {
VStack {
TextField("12345678", text: $phoneNumber, onEditingChanged: {
self.isActive = $0 // << here !!
}).keyboardType(.phonePad)
Divider()
.padding(.horizontal)
.frame(height: 1)
.background(isActive ? Color.red : Color.gray)
}
}

SwiftUI How to push to next screen when tapping on Button

I can navigate to next screen by using NavigationButton (push) or present with PresentationButton (present) but i want to push when i tap on Buttton()
Button(action: {
// move to next screen
}) {
Text("See More")
}
is there a way to do it?
You can do using NavigationLink
Note: Please try in real device. in simulator sometimes not work properly.
struct MasterView: View {
#State var selection: Int? = nil
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailsView(), tag: 1, selection: $selection) {
Button("Press me") {
self.selection = 1
}
}
}
}
}
}
struct DetailsView: View {
#Environment(\.presentationMode) var presentation
var body: some View {
Group {
Button("Go Back") {
self.presentation.wrappedValue.dismiss()
}
}
}
}
As you can see to display the new view, add the NavigationLink with isActive: $pushView using <.hidden()> to hide the navigation "arrow".
Next add Text("See More") with tapGesture to make the text respond to taps. The variable pushView will change (false => true) when you click "See More" text.
import SwiftUI
struct ContentView: View {
#State var pushView = false
var body: some View {
NavigationView {
List {
HStack{
Text("test")
Spacer()
NavigationLink(destination: NewView(), isActive: $pushView) {
Text("")
}.hidden()
.navigationBarTitle(self.pushView ? "New view" : "default view")
Text("See More")
.padding(.trailing)
.foregroundColor(Color.blue)
.onTapGesture {
self.pushView.toggle()
}
}
}
}
}
}
struct NewView: View {
var body: some View {
Text("New View")
}
}
ContentView picture
NewView picture
To tap on button and navigate to next screen,You can use NavigationLink like below
NavigationView{
NavigationLink(destination: SecondView()) {
Text("Login")
.padding(.all, 5)
.frame(minWidth: 0, maxWidth: .infinity,maxHeight: 45, alignment: .center)
.foregroundColor(Color.white)
}
}
You can use NavigationLink to implement this:
struct DetailsView: View {
var body: some View {
VStack {
Text("Hello world")
}
}
}
struct ContentView: View {
#State var selection: Int? = nil
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailsView(), tag: 1, selection: $selection) {
Button("Press me") {
self.selection = 1
}
}
}
}
}
}

Resources