Passing down #FocusState to another view - swiftui

I was wondering how would u be able to pass down a #FocusState to another view. Here is some example code.
struct View1: View {
enum Field {
case username, password
}
#State var passwordText: String = ""
#FocusState var focusedField: Field?
var body: some View {
// How would I be able to pass the focusedField here?
View2(text: $passwordText, placeholder: "Password")
//TextField("Password", text: $passwordText)
//.frame(minHeight: 44)
//.padding(.leading, 8)
//.focused($focusedField, equals: .password)
// How would I be able to add the commented code above to View2
}
}
struct View2: View {
#Binding var text: String
let placeholder: String
var body: some View {
HStack {
TextField(placeholder, text: $text)
.frame(minHeight: 44)
.padding(.leading, 8)
// How would I be able to add this
//.focused(binding: , equals: )
if text.count > 0 {
Image(systemName: "xmark.circle.fill")
.font(.headline)
.foregroundColor(.secondary)
.padding(.trailing, 8)
}
}
}
}
How would I be able to pass it down to View2. Or is there a better way to reuse a custom textfield? Would appreciate any help.

You can pass its binding as argument, like
struct View1: View {
enum Field {
case username, password
}
#State var passwordText: String = ""
#FocusState var focusedField: Field?
var body: some View {
View2(text: $passwordText, placeholder: "Password", focused: $focusedField)
}
}
struct View2: View {
#Binding var text: String
let placeholder: String
var focused: FocusState<View1.Field?>.Binding // << here !!
var body: some View {
HStack {
TextField(placeholder, text: $text)
.frame(minHeight: 44)
.padding(.leading, 8)
.focused(focused, equals: .password) // << here !!
if text.count > 0 {
Image(systemName: "xmark.circle.fill")
.font(.headline)
.foregroundColor(.secondary)
.padding(.trailing, 8)
}
}
}
}

Related

How to generate a preview with a .binding as variable type?

struct View1: View {
enum Field {
case username, password
}
#State var passwordText: String = ""
#FocusState var focusedField: Field?
var body: some View {
View2(text: $passwordText, placeholder: "Password", focused: $focusedField)
}
}
struct View2: View {
#Binding var text: String
let placeholder: String
var focused: FocusState<View1.Field?>.Binding // << here !!
var body: some View {
HStack {
TextField(placeholder, text: $text)
.frame(minHeight: 44)
.padding(.leading, 8)
.focused(focused, equals: .password) // << here !!
if text.count > 0 {
Image(systemName: "xmark.circle.fill")
.font(.headline)
.foregroundColor(.secondary)
.padding(.trailing, 8)
}
}
}
}
In View2, I can't find a way to be able to generate a preview view. What should I pass into focusedField?
This compiles, although whether it actually works…?
struct View2_Previews: PreviewProvider {
static let focusedState = FocusState<View1.Field?>()
static var previews: some View {
View2(text: .constant("text"), placeholder: "placeholder", focused: focusedState.projectedValue)
}
}
Do you actually need the FocussedState in View2? Perhaps you'd be better off to move that back up to View1
View2(text: $passwordText, placeholder: "Password")
.focused($focusedField, equals: .password)

destination:isActive:label deprecated in iOS 16 - I need help migrating

I need help migrating my code for iOS 16 support:
//
// RegistrationView.swift
// Recosia
//
// Created by Alex Slater on 16/11/2022.
//
import SwiftUI
struct RegistrationView: View {
#State private var email = ""
#State private var username = ""
#State private var fullname = ""
#State private var password = ""
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var viewModel: AuthViewModel
#State var root = [Root]()
var body: some View {
VStack {
NavigationStack(root: $root) {
//content
.onChange(of: viewModel.didAuthenticateUser) { newValue in
guard newValue else {return}
root.append(.profile)
}.navigationDestination(for: Root.self) { navigation in
switch navigation {
case .profile:
ProfilePhotoSelectorView()
}
}
}
}
// NavigationLink(destination: ProfilePhotoSelectorView(),
// isActive: $viewModel.didAuthenticateUser,
// label: { })
AuthHeaderView(title1: "Sign up", title2: "to Recosia")
VStack(spacing: 40) {
CustomInputField(imageName: "at.circle.fill", placeholderText: "Username", text: $username)
CustomInputField(imageName: "person", placeholderText: "Full Name", text: $fullname)
CustomInputField(imageName: "envelope", placeholderText: "Email", text: $email)
CustomInputField(imageName: "lock",
placeholderText: "Password",
isSecureField: true,
text: $password)
}
.padding(32)
Button {
viewModel.register(withEmail: email,
password: password,
fullname: fullname,
username: username)
} label: {
Text("Create Account")
.font(.headline)
.foregroundColor(.white)
.frame(width: 340, height: 50)
.background(Color(.systemPink))
.clipShape(Capsule())
.padding()
}
.shadow(color: .gray.opacity(0.5), radius: 10, x: 0, y: 0)
Spacer()
Button {
presentationMode.wrappedValue.dismiss()
} label: {
HStack {
Text("Already have an account?")
.font(.caption)
.foregroundColor(Color(.black))
Text("Sign In")
.font(.footnote)
.fontWeight(.semibold)
.foregroundColor(Color(.systemPink))
}
.padding(.bottom, 32)
}
}
.ignoresSafeArea()
}
}
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
RegistrationView()
}
}
enum Root {
case profile
}
with the error:
init(destination:isActive:label:)' was deprecated in iOS 16.0: use
NavigationLink(value:label:) inside a NavigationStack or
NavigationSplitView
I have tried re-arranging my code but I'm getting confused, can someone help!
Much appreciated :)
I tried using a NavigationStack with a .navigationDestination, but it says that it expects other arguments but the code presented above is all I have.
The new NavigationLink no longer requires a destination View. However, it needs some kind of an identifier, so navigationDestination expects the type of the identifier used in order to identify what View should be presented.
In your case, you don't need a NavigationLink anymore since you are navigating based on viewModel.didAuthenticateUser. You can listen to didAuthenticateUser and directly append the identifier to an array that you provide the NavigationStack with. Check init(path:root:):
struct RegistrationView: View {
#State private var email = ""
#State private var username = ""
#State private var fullname = ""
#State private var password = ""
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var viewModel: AuthViewModel
#State var root = [Root]()
var body: some View {
NavigationStack(path: $root) {
VStack {
AuthHeaderView(title1: "Sign up", title2: "to Recosia")
VStack(spacing: 40) {
CustomInputField(imageName: "at.circle.fill", placeholderText: "Username", text: $username)
CustomInputField(imageName: "person", placeholderText: "Full Name", text: $fullname)
CustomInputField(imageName: "envelope", placeholderText: "Email", text: $email)
CustomInputField(imageName: "lock",
placeholderText: "Password",
isSecureField: true,
text: $password)
}
.padding(32)
Button {
viewModel.register(withEmail: email,
password: password,
fullname: fullname,
username: username)
} label: {
Text("Create Account")
.font(.headline)
.foregroundColor(.white)
.frame(width: 340, height: 50)
.background(Color(.systemPink))
.clipShape(Capsule())
.padding()
}
.shadow(color: .gray.opacity(0.5), radius: 10, x: 0, y: 0)
Spacer()
Button {
presentationMode.wrappedValue.dismiss()
} label: {
HStack {
Text("Already have an account?")
.font(.caption)
.foregroundColor(Color(.black))
Text("Sign In")
.font(.footnote)
.fontWeight(.semibold)
.foregroundColor(Color(.systemPink))
}
.padding(.bottom, 32)
}
}
.ignoresSafeArea()
.onChange(of: viewModel.didAuthenticateUser) { newValue in
guard newValue else {return}
root.append(.profile)
}.navigationDestination(for: Root.self) { navigation in
switch navigation {
case .profile:
ProfilePhotoSelectorView()
}
}
}
}
enum Root {
case profile
}
}

value of type 'some View' has no member 'onSubmit'

I cannot use .onSubmit function in my simple TextField. I want to perform the following function when I tap return on my keyboard. Is there any function without using a button?
struct Model: View {
#EnvironmentObject var tappingSwitches: TapToggle
#AppStorage("scrollToIndex") var scrollToIndex: Int?
#State private var numNum: String = ""
var body: some View {
VStack(spacing: 0) {
HStack {
TextField("#", text: $numNum)
.padding(.leading)
.foregroundColor(.primary)
.font(.title2)
.frame(width: 70, height: 35)
.background(Color("cTabColor"))
.cornerRadius(20)
.keyboardType(.numberPad)
.onSubmit(of: .text) { // The error of value of type 'some View' has no member 'onSubmit' is appeared here
withAnimation(.spring()) {
if let songNum = Int(hymnNumber) {
scrollToIndex = songNum
}
}
}
}
.foregroundColor(Color("cTextColor"))
.background(Color("cColor"))
.clipShape(Capsule())
.padding(.horizontal)
ViewModel()
}
}
}

How to Add multi text into the list in SwiftUI?(Data Flow)

I'm trying to build an demo app by swiftUI that get multi text from user and add them to the list, below , there is an image of app every time user press plus button the AddListView show to the user and there user can add multi text to the List.I have a problem to add them to the list by new switUI data Flow I don't know how to pass data.(I comment more information)
Thanks 🙏
here is my code for AddListView:
import SwiftUI
struct AddListView: View {
#State var numberOfTextFiled = 1
#Binding var showAddListView : Bool
var body: some View {
ZStack {
Title(numberOfTextFiled: $numberOfTextFiled)
VStack {
ScrollView {
ForEach(0 ..< numberOfTextFiled, id: \.self) { item in
PreAddTextField()
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView)
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
AddListView(showAddListView: .constant(false))
}
}
struct PreAddTextField: View {
// I made this standalone struct and use #State to every TextField text be independent
// if i use #Binding to pass data all Texfield have the same text value
#State var textInTextField = ""
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
// What should happen here to add Text to List???
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
#Binding var numberOfTextFiled : Int
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
numberOfTextFiled += 1
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
and for DataModel:
import SwiftUI
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
var textData = [
Text1(text: "SwiftUI"),
Text1(text: "Data flow?"),
]
and finally:
import SwiftUI
struct ListView: View {
#State var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
Because of the multiple-items part of the question, this becomes a lot less trivial. However, using a combination of ObservableObjects and callback functions, definitely doable. Look at the inline comments in the code for explanations about what is going on:
struct Text1 : Identifiable , Hashable{
var id = UUID()
var text : String
}
//Store the items in an ObservableObject instead of just in #State
class AppState : ObservableObject {
#Published var textData : [Text1] = [.init(text: "Item 1"),.init(text: "Item 2")]
}
//This view model stores data about all of the new items that are going to be added
class AddListViewViewModel : ObservableObject {
#Published var textItemsToAdd : [Text1] = [.init(text: "")] //start with one empty item
//save all of the new items -- don't save anything that is empty
func saveToAppState(appState: AppState) {
appState.textData.append(contentsOf: textItemsToAdd.filter { !$0.text.isEmpty })
}
//these Bindings get used for the TextFields -- they're attached to the item IDs
func bindingForId(id: UUID) -> Binding<String> {
.init { () -> String in
self.textItemsToAdd.first(where: { $0.id == id })?.text ?? ""
} set: { (newValue) in
self.textItemsToAdd = self.textItemsToAdd.map {
guard $0.id == id else {
return $0
}
return .init(id: id, text: newValue)
}
}
}
}
struct AddListView: View {
#Binding var showAddListView : Bool
#ObservedObject var appState : AppState
#StateObject private var viewModel = AddListViewViewModel()
var body: some View {
ZStack {
Title(addItem: { viewModel.textItemsToAdd.append(.init(text: "")) })
VStack {
ScrollView {
ForEach(viewModel.textItemsToAdd, id: \.id) { item in //note this is id: \.id and not \.self
PreAddTextField(textInTextField: viewModel.bindingForId(id: item.id))
}
}
}
.padding()
.offset(y: 40)
Buttons(showAddListView: $showAddListView, save: {
viewModel.saveToAppState(appState: appState)
})
}
.frame(width: 300, height: 200)
.background(Color.white)
.shadow(color: Color.black.opacity(0.3), radius: 10, x: 0, y: 10)
}
}
struct PreAddTextField: View {
#Binding var textInTextField : String //this takes a binding to the view model now
var body: some View {
VStack {
TextField("Enter text", text: $textInTextField)
}
}
}
struct Buttons: View {
#Binding var showAddListView : Bool
var save : () -> Void //callback function for what happens when "Add" gets pressed
var body: some View {
VStack {
HStack(spacing:100) {
Button(action: {
showAddListView = false}) {
Text("Cancel")
}
Button(action: {
showAddListView = false
save()
}) {
Text("Add")
}
}
}
.offset(y: 70)
}
}
struct Title: View {
var addItem : () -> Void //callback function for what happens when the plus button is hit
var body: some View {
VStack {
HStack {
Text("Add Text to list")
.font(.title2)
Spacer()
Button(action: {
addItem()
}) {
Image(systemName: "plus")
.font(.title2)
}
}
.padding()
Spacer()
}
}
}
struct ListView: View {
#StateObject var appState = AppState() //store the AppState here
#State private var showAddListView = false
var body: some View {
NavigationView {
VStack {
ZStack {
List(appState.textData, id : \.self){ text in
Text(text.text)
}
if showAddListView {
AddListView(showAddListView: $showAddListView, appState: appState)
.offset(y:-100)
}
}
}
.navigationTitle("List")
.navigationBarItems(trailing:
Button(action: {showAddListView = true}) {
Image(systemName: "plus")
.font(.title2)
}
)
}
}
}

Textfield not enabled in SwiftUI

I am trying to create a TextField in SwiftUI, Its successfully created but not enabled. I don't understand why its not enabled. please see the blow code.
import SwiftUI
struct About: View {
static var about:String = ""//some very very very long description string to be initially wider than screen"
static var testBinding = Binding<String>(get: { about }, set: {
// print("New value: \($0)")
about = $0 } )
var body: some View {
ScrollView{
GeometryReader{geometry in
ZStack{
VStack{
Username()
aboutu()
profission()
youtubeurl()
website()
submitbutton()
}
}.padding(.all,10)
}
}
}
}
struct About_Previews: PreviewProvider {
static var previews: some View {
About()
}
}
struct Username: View {
#State var name: String = ""
var body: some View {
TextField("Your Name", text: $name)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
}
}
struct aboutu: View {
var body: some View {
MultilineTextField("About You", text: About.testBinding)
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.gray))
.padding(.bottom,10)
}
}
struct profission: View {
#State var profession: String = ""
var body: some View {
TextField("Your Profession", text: $profession)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
}
}
struct youtubeurl: View {
#State var youtube: String = ""
var body: some View {
TextField("Your Youtube URL", text: $youtube)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
}
}
struct website: View {
#State var website: String = ""
var body: some View {
TextField("Your WebSite", text: $website)
.padding()
.background(lightGreyColor)
.cornerRadius(5.0)
.padding(.bottom, 10)
}
}
struct submitbutton: View {
var body: some View {
Button(action: {
}){
Text("Submit")
.fontWeight(.bold)
.font(.system(size:14))
.padding()
.padding(5)
}.frame(width: 300, height: 50, alignment: .center)
.background(Color.blue)
.cornerRadius(20)
.foregroundColor(.white)
}
}