Xcode: Button action between files/views - xcode

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

Related

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

SwiftUI Toggle onChange not called for macOS menu item

I am adding a sort menu to my macOS SwiftUI app. Some of the items can have checkmarks next to them. For each menu item I create a Toggle item bound to an #State Bool. When an item is clicked I would like to run additional code to update the sort, so I added an .onChange, but it is never called. The checkmark for the item appears and disappears as I would expect, but the .onChange never gets hit.
Here is an example of creating a macOS menu item with checkmark. The "change" is never printed and a breakpoint set there never gets hit.
import SwiftUI
struct ViewMenuCommands: Commands {
#State
var isAlphabetical = false
#CommandsBuilder var body: some Commands {
CommandMenu("ViewTest") {
Toggle("Alphabetical", isOn: $isAlphabetical)
.onChange(of: isAlphabetical) { value in
print( "change" )
}
}
}
}
And here's where I add the menu to the app:
import SwiftUI
#main
struct MenuToggleTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ViewMenuCommands()
}
}
}
The problem is a result of #State not notifying Commands of its change -- #State will only notify a View of a change.
The solution is to wrap your menu item in a view that can respond to changes (via either #State or an ObservableObject -- I've chosen then latter).
Example:
#main
struct MainApp: App {
#StateObject var menuState = MenuState()
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandMenu("ViewTest") {
AlphabeticalMenuItem(menuState: menuState)
}
}
}
}
class MenuState : ObservableObject {
#Published var isAlphabetical = false
}
struct AlphabeticalMenuItem : View {
#ObservedObject var menuState : MenuState
var body: some View {
Toggle("Alphabetical", isOn: $menuState.isAlphabetical)
.onChange(of: menuState.isAlphabetical) { value in
print( "change" )
}
}
}

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

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.

Highlight Navigation View At Start

When I start my app, the start page is "Kunde" but the whole thing is not highlighted in blue in the navigation. It just turns blue (system color) when I click on it.
I want it to be highlighted blue when I open the app.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: ListView()) {
Text("Kunde")
}
}
ListView()
}
}
}
struct ListView: View {
var body: some View {
Text("Hello.")
}
}
you could try something like this approach:
struct ContentView: View {
#State var selection: String?
#State var listData = ["Kunde", "xxxx", "zzzz"]
var body: some View {
NavigationView {
List(listData, id: \.self) { item in
NavigationLink(destination: ListView()) {
Text(item)
}
.listRowBackground(selection == item ? Color.blue : Color.clear)
}
}
.onAppear {
selection = "Kunde"
}
}
}

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