Whenever I click on button the view won't open and give me this warnings, what am I doing wrong in this code - xcode

The code below gives me warning in the button part and if I run the view won't open.
If I try using NavigationLink a arrow comes at end and I don't want to use navigation in any ways.
I tried the button view putting in NavigationLink link but when I click It the whole form has the click event effect, as if ram clicking the whole form.
import SwiftUI
//login view
struct login: View {
#State private var usern:String=""
#State var navigated = false
#State private var pass:String=""
#State private var secured:Bool=true
var body: some View {
NavigationView{
//textfield and password field
Form{
VStack{
HStack{
Text("UserName:")
TextField("", text: $usern)
}
HStack{
Text("Password:")
if secured{
SecureField("",text:$pass)
}
else{
TextField("",text: $pass)
}
Button(action: {self.secured.toggle()}){
//password seen or unseen
if secured{
Image(systemName:"eye.slash")
}
else{
Image(systemName:"eye")
}
}.buttonStyle(BorderlessButtonStyle())
}
Button("Login",action: {signup()}).buttonStyle(BorderlessButtonStyle())
//button view not working
Button("Not Yet signed? Create new account",action: {signup()}).buttonStyle(BorderlessButtonStyle())
//button view not working
}
}
}
}
}
struct login_Previews: PreviewProvider {
static var previews: some View {
login()
}
}
//SIGNUP VIEW
import SwiftUI
struct signup: View {
var body: some View {
Text("Hello, World!")
}
}
struct signup_Previews: PreviewProvider {
static var previews: some View {
signup()
}
}

The purpose of the button is to call an action, to toggle something.
To solve your problem you have to:
Add a State variable to login View
struct login: View {
#State private var usern:String=""
#State var navigated = false
#State private var pass:String=""
#State private var secured:Bool=true
#State var showSignUp:Bool = false -- this one
}
Change the button definition FROM
Button("Not Yet signed? Create new account",action: {signup()}).buttonStyle(BorderlessButtonStyle())
TO
Button("Not Yet signed? Create new account",action: {showSignUp.toggle()}).buttonStyle(BorderlessButtonStyle())
.sheet(isPresented: $showSignUp) {
signup()
}
Please notice that, for this case I've used .sheet(isPresented) to call the view.

Related

SwiftUI, How to publish data from view to a viewModel then to a second view?

I have one view (with a Form), a viewModel, and a second view that I hope to display inputs in the Form of the first view. I thought property wrapping birthdate with #Published in the viewModel would pull the Form input, but so far I can't get the second view to read the birthdate user selects in the Form of the first view.
Here is my code for my first view:
struct ProfileFormView: View {
#EnvironmentObject var appViewModel: AppViewModel
#State var birthdate = Date()
var body: some View {
NavigationView {
Form {
Section(header: Text("Personal Information")) {
DatePicker("Birthdate", selection: $birthdate, displayedComponents: .date)
}
}
}
Here is my viewModel code:
class AppViewModel: ObservableObject {
#Published var birthdate = Date()
func calcAge(birthdate: String) -> Int {
let dateFormater = DateFormatter()
dateFormater.dateFormat = "MM/dd/yyyy"
let birthdayDate = dateFormater.date(from: birthdate)
let calendar: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
let now = Date()
let calcAge = calendar.components(.year, from: birthdayDate!, to: now, options: [])
let age = calcAge.year
return age!
and here is my second view code:
struct UserDataView: View {
#EnvironmentObject var viewModel: AppViewModel
#StateObject var vm = AppViewModel()
var body: some View {
VStack {
Text("\(vm.birthdate)")
Text("You are signed in")
Button(action: {
viewModel.signOut()
}, label: {
Text("Sign Out")
.frame(width: 200, height: 50)
.foregroundColor(Color.blue)
})
}
}
And it may not matter, but here is my contentView where I can tab between the two views:
struct ContentView: View {
#EnvironmentObject var viewModel: AppViewModel
var body: some View {
NavigationView {
ZStack {
if viewModel.signedIn {
ZStack {
Color.blue.ignoresSafeArea()
.navigationBarHidden(true)
TabView {
ProfileFormView()
.tabItem {
Image(systemName: "square.and.pencil")
Text("Profile")
}
UserDataView()
.tabItem {
Image(systemName: "house")
Text("Home")
}
}
}
}
else
{
SignInView()
}
}
}
.onAppear {
viewModel.signedIn = viewModel.isSignedIn
}
}
One last note, I've got a second project that requires this functionality (view to viewmodel to view) so skipping the viewmodel and going direct from view to view will not help.
Thank you so much!!
Using a class AppViewModel: ObservableObject like you do is the appropriate way to "pass" the data around your app views. However, there are a few glitches in your code.
In your first view (ProfileFormView), remove #State var birthdate = Date() and use
DatePicker("Birthdate", selection: $appViewModel.birthdate, ....
Also remove #StateObject var vm = AppViewModel() in your second view (UserDataView),
you already have a #EnvironmentObject var viewModel: AppViewModel, no need for 2 of them.
Put #StateObject var vm = AppViewModel() up in your hierarchy of views,
and pass it down (as you do) using the #EnvironmentObject with
.environmentObject(vm)
Read this info to understand how to manage your data: https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

SwiftUI dismiss() does not work on preview

I am making a TODO app on Xcode (ver 13.4) on mac.
When TodoEditor's "Add" button is tapped, the preview screen should show a ContentView and the ContentView should show updated Todo contents but it shows the ContentView and the updated Todo contents are not in it, then just it stops.
If I use simulator, it works perfectly. So the problem happens only on Xcode' preview screen.
If I delete
data.todos.append(newTodo)
from TodoEditor.swift, dismiss() works, the preview shows a ContentView and the preview does not stops. Of course todo content is not updated.
What can I do for this?
My source codes are followed.
Thank you.
MyApp.swift
import SwiftUI
#main
struct MyApp: App {
#StateObject var data = Todo()
var body: some Scene {
WindowGroup {
NavigationView{
ContentView()
.navigationTitle("My Todo")
}.environmentObject(data)
}
}
}
ContentView.swift
import SwiftUI
struct ContentView: View {
#EnvironmentObject var data: Todo
var body: some View {
List{
ForEach(data.todos){todo in
NavigationLink{
VStack{
Text(todo.taskContent)
}
}label: {
HStack{
Text(todo.taskContent)
Text(todo.isCompleted ? "done" : "not yet")
}
}
}
}.toolbar{
ToolbarItem{
NavigationLink("Add"){
TodoEditor()
.navigationTitle("Add Todo")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView{
ContentView()
.environmentObject(Todo())
}
}
}
TodoEditor.swift
import SwiftUI
struct TodoEditor: View {
#State var newTodo:Task = Task(taskContent: "", isCompleted: false)
#EnvironmentObject var data:Todo
#Environment(\.dismiss) var dismiss
var body: some View {
VStack{
Form{
Section("Task Content"){
TextField("Task Content",text: $newTodo.taskContent)
}
Section("Complete?"){
Toggle("Complete?",isOn: $newTodo.isCompleted)
}
}
}.toolbar{
ToolbarItem{
Button("Add"){
data.todos.append(newTodo) //<- New todo is added here
dismiss() //<- Here is dismiss()
}
}
}
}
}
struct TodoEditor_Previews: PreviewProvider {
#Environment(\.dismiss) var dismiss
static var previews: some View {
NavigationView{
TodoEditor(newTodo: Task(taskContent: "Hello", isCompleted:false))
.environmentObject(Todo())
}
}
}
Todo.swift
import SwiftUI
class Todo: ObservableObject{
#Published var todos:[Task] = []
}
struct Task: Identifiable{
var taskContent: String
var isCompleted: Bool
var id = UUID()
}

Xcode: Button action between files/views

Xcode complete beginner
Trying to make searchbar appear in different view than where the button is placed. When pressing magnifying-glass (button placed in ContentView), I want the text to appear in FeedView. How? #Published/ObservedObject/State...?? I have no knowledge just like to play around and learn. So 1. press magnifying-glass 2. makes the text appear (needs to be in different views)
ContentView:
import SwiftUI
struct ContentView: View {
#State private var doSearch = false
var body: some View {
Button {
doSearch.toggle()
} label: {
Image(systemName: "magnifyingglass")
.foregroundColor(Color(.black))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
FeedView:
import SwiftUI
struct FeedView: View {
#State private var doSearch = false
var body: some View {
HStack {
if doSearch {
Text("search bar here")
}
}
}
}
Images:
ContentView
FeedView

How do I store the Input of a Textfield and display it in another View in Swift UI?

I am just learning to code and I have a question. How do I store the Input data of a Textfield and display it in another View? I tried it with Binding but it doesn't work that way. I appreciate your help
import SwiftUI
struct SelectUserName: View {
#Binding var name: String
var body: some View {
TextField("Name", text: self.$name)
}
}
struct DisplayUserName: View {
#State private var name = ""
var body: some View {
// the name should be diplayed here!
Text(name)
}
}
struct DisplayUserName_Previews: PreviewProvider {
static var previews: some View {
DisplayUserName()
}
}
State should always be stored in a parent and passed down to the children. Right now, you're not showing the connection between the two views (neither reference the other), so it's a little unclear how they relate, but there are basically two scenarios:
Your current code would work if DisplayUserName was the parent of SelectUserName:
struct DisplayUserName: View {
#State private var name = ""
var body: some View {
Text(name)
SelectUserName(name: $name)
}
}
struct SelectUserName: View {
#Binding var name: String
var body: some View {
TextField("Name", text: self.$name)
}
}
Or, if they are sibling views, the state should be stored by a common parent:
struct ContentView : View {
#State private var name = ""
var body: some View {
SelectUserName(name: $name)
DisplayUserName(name: name)
}
}
struct SelectUserName: View {
#Binding var name: String
var body: some View {
TextField("Name", text: self.$name)
}
}
struct DisplayUserName: View {
var name : String //<-- Note that #State isn't needed here because nothing in this view modifies the value
var body: some View {
Text(name)
}
}

Updating SwiftUI View based on Observable in Preview

Trying to implement a Login screen in SwiftUI. Based on other similar questions, I'm going the approach of using an Observable EnvironmentObject and a ViewBuilder in the main ContentView that reacts to that and displays the appropriate screen.
However, even though the property is updating as expecting the view never changes in Preview. Everything works fine when built and run in the Simulator but in Preview the change never happens.
Below is the code reduced to the smallest possible example in a single file (only missing passing the environment object in SceneDelegate, which doesn't affect Preview anyway).
import SwiftUI
import Combine
struct ContentView: View {
#EnvironmentObject var userAuth: UserAuth
#ViewBuilder
var body: some View {
if !userAuth.person.isLoggedin {
FirstView()
} else {
SecondView()
} }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(UserAuth())
}
}
struct Person {
var isLoggedin: Bool
init() {
self.isLoggedin = false
}
}
class UserAuth: ObservableObject {
#Published var person: Person
init(){
self.person = Person()
}
let didChange = PassthroughSubject<UserAuth,Never>()
// required to conform to protocol 'ObservableObject'
let willChange = PassthroughSubject<UserAuth,Never>()
func login() {
// login request... on success:
willChange.send(self)
self.person.isLoggedin = true
didChange.send(self)
}
}
struct SecondView: View {
var body: some View {
Text("Second View!")
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView().environmentObject(UserAuth())
}
}
struct FirstView: View {
#EnvironmentObject var userAuth: UserAuth
var body: some View {
VStack {
Button(action: {
self.userAuth.login()
}) {
Text("Login")
}
Text("Logged in: " + String(self.userAuth.person.isLoggedin))
}
}
}
struct FirstView_Previews: PreviewProvider {
static var previews: some View {
FirstView().environmentObject(UserAuth())
}
}
EDIT: Based on the answer below, I've added the environment object to the interior views, but unfortunately the view still doesn't change in Preview mode.
struct FirstView_Previews: PreviewProvider {
static var previews: some View {
FirstView().environmentObject(UserAuth())
}
}
environment object must be set in PreviewProvider as well
UPDATE
struct ContentView: View {
#ObservedObject var userAuth = UserAuth () // #ObservedObject
var body: some View {
NavigationView{ // Navigation
}.environmentObject(UserAuth) //.environmentObject
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(UserAuth())
}
}
struct SecondView: View {
#EnvironmentObject var userAuth: UserAuth // only EnvironmentObject
var body: some View {
Text("Second View!")
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView().environmentObject(UserAuth())
}
}
The issue I was having with Canvas not giving previews is that my ObservableObject was reading from User Defaults
#Published var fName: String = Foundation.UserDefaults.standard.string(forKey: "fName") !
{ didSet {
Foundation.UserDefaults.standard.set(self.fName, forKey: "fName")
}
}
So works in simulator and on device but no Canvas Previews. I tried many ways to give Preview data to use since Preview can't read from UserDefaults (not a device), and realized I can put an initial/ default value if the UserDefault is not there:
#Published var fName: String = Foundation.UserDefaults.standard.string(forKey: "fName") ?? "Sean"
{ didSet {
Foundation.UserDefaults.standard.set(self.fName, forKey: "fName")
}
}
Now Preview/ Canvas is showing my view and I can continue coding with my Observable Object. The aim was to put in the Observable Object some default code to use.

Resources