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

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
}
}

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)

How to transfer an Image from one screen to another screen in SwiftUI?

I am creating a feature where a user can take a picture of an item and that image can be displayed on their home screen. I am currently allowing them to take a picture in the AddProductView screen and I want the picture that they took/choose to be shown on the HomeScreenView. I tried to put a #Binding var productImage = UIImage() in the HomeScreenView but that is giving me a
No exact matches in a call to initializer
Here is the HomeScreenView:
struct HomeScreenView: View {
#Binding var loggedin: Bool
#State var showSheet: Bool = false
#Binding var productImage = UIImage()
var body: some View {
NavigationView {
VStack {
Text("Authorized.... You are in!")
.font(.largeTitle)
}
.padding()
.navigationTitle("Home")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing){
Button () {
showSheet.toggle()
} label: {
Text("Add Item")
}
.sheet(isPresented: $showSheet, content: {
AddProductView()
})
}
}
}
.environment(\.colorScheme, .light)
.navigationBarBackButtonHidden(true)
}
}
Here is the AddProductView:
struct AddProductView: View {
#Environment(\.presentationMode) var presentationMode
#State var itemImage = false
#State var openCameraRoll = false
#State var camera = false
#State var photoLib = false
#State private var productimage = UIImage()
#State var productname = ""
#State var productcategory = ""
#State var otherproductcategory = ""
let category = ["Living Room", "Bedroom", "Kitchen", "Garage", "Office", "Electronics", "Clothes"]
var body: some View {
NavigationView {
VStack{
Form{
Section(header: Text("Add An Image")) {
if itemImage {
Image(uiImage: productimage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(alignment: .center)
} else {
Image("ImageButton")
.frame(width: 300, alignment: .center)
}
HStack{
Button(action: {
itemImage = true
openCameraRoll = true
camera = true
}, label: {
Text("Camera")
.frame(maxWidth: .infinity, alignment: .center)
})
.buttonStyle(BorderlessButtonStyle())
Divider()
Button(action: {
itemImage = true
openCameraRoll = true
camera = false
photoLib = true
}, label: {
Text("Photos")
.frame(maxWidth: .infinity, alignment: .center)
})
.buttonStyle(BorderlessButtonStyle())
}
}
Section(header: Text("Item Information")) {
TextField("Item Name", text: $productname)
Picker("Category", selection: $productcategory) {
ForEach(category, id: \.self){
Text("\($0)")
}
Text("Other").tag("other")
}
if productcategory == "other" {
TextField("Other Provider", text: $otherproductcategory)
}
}
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: {
Text("Submit")
.frame(maxWidth: .infinity, alignment: .center)
})
}
}
.navigationTitle("Add An Item")
.sheet(isPresented: $openCameraRoll) {
if camera {
ImagePicker(selectedImage: $productimage, sourceType: .camera)
} else {
ImagePicker(selectedImage: $productimage, sourceType: .photoLibrary)
}
}
}
}
Here is where the Image is being set to a variable:
.sheet(isPresented: $openCameraRoll) {
if camera {
ImagePicker(selectedImage: $productimage, sourceType: .camera)
} else {
ImagePicker(selectedImage: $productimage, sourceType: .photoLibrary)
}
}
I tried to use #Binding var productImage = UIImage() but I get a "No exact matches in a call to initializer"

Can't select Picker

I am trying to make a stock control system in SwiftUI where users can add an ingredient and then ad an amount and then select a unit of measurement(kg, g, l ,ml).
The app allows them to click a button which allows them to add an ingredient to a text-box which is created and then their input is added to a list.
I am having trouble allowing the user to also type in a number in a text-box next to the ingredient text-box and making the picker clickable
Here is my code
import SwiftUI
struct UploadView2: View {
#State var ingredients = [String]()
#State var amount = [String]()
#State var choices = ["g", "kg", "ml", "l"]
#State var choosen: String = ""
#EnvironmentObject var viewRouter: ViewRouter
func getBinding(forIndex index: Int) -> Binding<String> {
return Binding<String>(get: { ingredients[index] },
set: { ingredients[index] = $0 })
}
var body: some View {
VStack{
HStack{
Button {
print("Going Back")
viewRouter.currentPage = .UploadView
} label: {
Image(systemName: "arrow.left")
.font(.system(size: 30))
.foregroundColor(.black)
}
.padding(.horizontal)
Spacer()
Text("Add Ingredients")
.font(.system(size: 30))
.fontWeight(.bold)
Spacer()
Button {
print("Saved")
} label: {
Image(systemName: "bookmark")
.font(.system(size: 30))
.foregroundColor(.black)
}
.padding()
}
Form {
ForEach(0..<ingredients.count, id: \.self) { index in
HStack {
Button(action: { ingredients.remove(at: index) }) {
Image(systemName: "minus.circle.fill")
.foregroundColor(.red)
.padding(.horizontal)
}
TextField("Ingredient", text: getBinding(forIndex: index))
Picker("", selection: $choosen){
ForEach(choices, id: \.self) { i in
Text("\(i)").tag(i)
}
}
}
}
Button(action: { ingredients.append("") }) {
HStack {
Image(systemName: "plus")
.foregroundColor(.black)
.padding(.horizontal)
Text("add an ingredient")
.foregroundColor(.black)
}
}
}
Button {
//change view router
//add data to data class
viewRouter.currentPage = .UploadView3
} label: {
Label("next", systemImage: "arrow.right")
}
.padding()
.frame(width: 100)
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(8)
}
}
}
struct UploadView2_Previews: PreviewProvider {
static var previews: some View {
UploadView2()
.previewDevice(PreviewDevice(rawValue: "iPhone 13"))
.previewInterfaceOrientation(.portrait)
UploadView2()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
}
}
When i click on my click it removes the text-box like the dismiss button
My overall goal is to have a text-box to enter an ingredient and then a text-box to enter an amount and then a picker to select a unit of measurement.
How can I achieve this using my current code as much as I can?
to be able to "select" your Picker, you could try this approach, as shown in this example code:
struct ContentView: View {
#State var ingredient = ""
#State var amount = ""
#State var choosen = ""
#State var choices = ["g", "kg", "ml", "l"]
var body: some View {
HStack {
TextField("Ingredient", text: $ingredient) // <-- or your getBinding thing
TextField("Amount", text: $amount)
Picker("", selection: $choosen){
ForEach(choices, id: \.self) { i in
Text("\(i)").tag(i)
}
}
.pickerStyle(.inline)
.frame(width: 55)
.clipped() // <-- here
}
}
}

Passing down #FocusState to another view

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)
}
}
}
}

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)
}
}