#ObservedObject var taskModel = TaskViewModel()
var body: some View {
ScrollView(.vertical , showsIndicators:false){
VStack{
CustomSegmentedBar()
if taskModel.currentTab == "Done"{
taskModel.getTasks()
}else if taskModel.currentTab == "Today"{
TaskView()
}
}.padding()
}
i am trying to filter my task object depending on the segmented control and then display it in a list but xcode is giving me a error > Type() cannot confirm view. I am really confused
maybe you could try something like this:
#StateObject var taskModel = TaskViewModel()
var body: some View {
ScrollView(.vertical , showsIndicators:false){
VStack {
CustomSegmentedBar()
if taskModel.currentTab == "Today" {
TaskView()
}
}.padding()
.onAppear {
if taskModel.currentTab == "Done" {
taskModel.getTasks()
}
}
}
}
Related
I have multiple views in a swift project I am trying to change Views My idea is to make an enum, and change state. I do not want to use navigationLinks.
Here is my code:
struct NightOutApp: App {
var body: some Scene {
WindowGroup {
ViewNavigator()
}
}
}
enum ViewState{
case LoginView
case UserProfileView
}
struct ViewNavigator: View {
var body: some View {
#State var ViewState = ViewState.LoginView
return Group{
switch ViewState{
case .LoginView:
LoginView()
case .UserProfileView:
UserProfileView()
}
}
}
}
I have a variable
#Binding var ViewState: ViewState at the top of the LoginView
some logic on the LoginView that would change ViewState from LoginView to UserProfileView:
self.ViewState = .UserProfileView
I tried using binding variables. this gave me a warning: Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
Edit-
Here is what happens when I run it. I press a button to login, It takes me to this breakpoint. The code seems to process, but the view does not change.
Code
Ok I've edited my answer in an attempt to understand what you're looking for. It seems you are needing a login flow but I don't think I can understand your problem fully without seeing more of your code. Here is an example that you should be able to copy and paste and play around with while you figure out exactly what you're needing.
import SwiftUI
struct ViewNavigator: View {
#State var viewState = 0
#State var withEmail: String = ""
#State var withPassword: String = ""
#State var showAlert: Bool = false
#State var alertTitle: String = ""
#State var alertMessage: String = ""
var body: some View {
ZStack {
switch viewState {
case 0:
loginView
case 1:
profileView
default:
RoundedRectangle(cornerRadius: 25)
.foregroundColor(.red)
}
VStack {
Spacer()
bottomButton
}
.padding()
}
.alert(alertTitle, isPresented: $showAlert) { } message: {
Text(alertMessage)
}
}
func handleBottomButtonPressed() {
switch viewState {
case 0:
guard !withEmail.isEmpty else {
showAlert(title: "Wait!", message: "Your email is required.")
return
}
guard withPassword == "password" else {
showAlert(title: "Hold Up!", message: "Your password is incorrect.")
return
}
default:
break
}
if viewState == 0 {
viewState += 1
withPassword = ""
} else {
viewState -= 1
}
}
func showAlert(title: String, message: String) {
alertTitle = title
alertMessage = message
showAlert.toggle()
}
}
struct ViewNavigator_Previews: PreviewProvider {
static var previews: some View {
ViewNavigator()
}
}
extension ViewNavigator {
private var bottomButton: some View {
Button {
handleBottomButtonPressed()
} label: {
Text(viewState == 0 ? "LOGIN" : "LOGOUT")
.font(.headline)
.foregroundColor(.white)
.frame(height: 55)
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(15)
.shadow(radius: 10)
}
}
private var loginView: some View {
ZStack {
Color.green.ignoresSafeArea()
VStack {
Image(systemName: "1.square")
.font(.largeTitle)
Text("LOGIN")
.font(.largeTitle)
VStack {
TextField("email...", text: $withEmail)
.padding()
.background(Color(UIColor.systemGray5))
.cornerRadius(10)
.textInputAutocapitalization(.never)
SecureField("password...", text: $withPassword)
.padding()
.background(Color(UIColor.systemGray5))
.cornerRadius(10)
.textInputAutocapitalization(.never)
}
.padding()
}
}
}
private var profileView: some View {
ZStack {
Color.orange.ignoresSafeArea()
VStack {
Image(systemName: "2.square")
.font(.largeTitle)
Text("Profile View")
.font(.largeTitle)
}
}
}
}
So I am trying to change between views when someone clicks on the image, but the action after the ontapgesture is always "expression is unused". I tried changing it to a navigation view as well as other things but I feel like nothing seems to be working.
Code below:
struct MenuView2: View {
#State private var menuu2 = menu2()
var body: some View {
ScrollView(){
VStack {
ZStack {
Rectangle().frame(height:40).opacity(0.25).blur(radius: 10).onTapGesture {
print("breakfast tapped ")
}
HStack {
VStack(alignment: .leading, spacing: 8, content: {
Text("Breakfast").font(.largeTitle)
})
}
}
Image("breakfast").resizable().scaledToFill().onTapGesture {
menuu2
}
}
}
}
}
Thank you.
The error you get is "correct" in that menuu2 does not do anything, it is just there.
There are a number of ways to change view on tap, this is just one way:
struct MenuView2: View {
#State private var menuu2 = menuu2()
#State private var changeView = false
var body: some View {
changeView ? AnyView(theOtherView) : AnyView(theScrollView)
}
var theOtherView: some View {
// menuu2 presumably
// just for testing
Text("theOtherView").onTapGesture {
self.changeView = false
}
}
var theScrollView: some View {
ScrollView() {
VStack {
ZStack {
Rectangle().frame(height:40).opacity(0.25).blur(radius: 10).onTapGesture {
print("breakfast tapped ")
}
HStack {
VStack(alignment: .leading, spacing: 8, content: {
Text("Breakfast").font(.largeTitle)
})
}
}
Image("breakfast").resizable().scaledToFill().onTapGesture {
self.changeView = true
}
}
}
}
}
I have a sign out button on a modal sheet that takes the user back to the login screen. To accomplish this I first dismiss the sheet and then, using asyncAfter(deadline:) I set an environment variable that causes the login page to appear. Everything works fine, but once the sheet is dismissed, the transition from the view under the sheet to the login page is pretty jarring. Mostly because there isn't one. The top view just disappears, revealing the login view. I know I can create custom transitions, but I can't figure out where to attach it. Say, for example, I want to fade out the view underneath the sheet. (Although, I'm open to any kind of transition!)
This is the struct that directs the traffic:
struct ConductorView: View {
#EnvironmentObject var tower: Tower
let onboardingCompleted = UserDefaults.standard.bool(forKey: "FirstVisit")
var body: some View {
VStack {
if tower.currentPage == .onboarding {
Onboarding1View()
} else if tower.currentPage == .login {
LoginView()
} else if tower.currentPage == .idle {
LoginView()
}
}.onAppear{
if self.onboardingCompleted {
self.tower.currentPage = .login
} else {
self.tower.currentPage = .onboarding
}
}
}
}
And this is the sign out button on the sheet:
Button(action: {
self.presentationMode.wrappedValue.dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.tower.currentPage = .login
}
}) {
Text("Sign Out")
}
Here is a simplified demo on your replicated code (and I made some longer delay to make it mode visible). Of course you will need to tune it for your needs by changing type of transition or animation, etc. Tested with Xcode 12 / iOS 14
class Tower: ObservableObject {
enum PageType {
case onboarding, login, idle
}
#Published var currentPage: PageType = .onboarding
}
struct ConductorView: View {
#EnvironmentObject var tower: Tower
let onboardingCompleted = false
var body: some View {
VStack {
if tower.currentPage == .onboarding {
Onboarding1View()
} else if tower.currentPage == .login {
Text("LoginView")
.transition(.move(edge: .trailing)) // << here !!
} else if tower.currentPage == .idle {
Text("IdleView")
}
}
.animation(.default, value: tower.currentPage) // << here !!
.onAppear{
if self.onboardingCompleted {
self.tower.currentPage = .login
} else {
self.tower.currentPage = .onboarding
}
}
}
}
struct Onboarding1View: View {
#EnvironmentObject var tower: Tower
#Environment(\.presentationMode) var presentationMode
#State private var isPresented = true
var body: some View {
Text("Login")
.sheet(isPresented: $isPresented) {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.tower.currentPage = .login
}
}) {
Text("Sign Out")
}
}
}
}
I have a button in my code and I have a file called LogindView.swift
I cannot get the code to open another view file when clicking on the button.
Can anybody give me an example on how to do it.
In my button action I have tried to write LogindView() but i just gives me a warning.
"Result of 'LogindView' initializer is unused"
Button(action: {
// Do action
LogindView()
}, label: {
//** Label text
Text("Logind")
.font(.headline)
.padding(.all)
.foregroundColor(Color.white)
})
.background(Color.blue)
You essentially have 3 options to transition between views depending on your needs.
First, you can use a NavigationView. This will provide a back button and will allow the user to go back. Note that there are some bugs currently when you don't put the NavigationLink inside of a List as per https://stackoverflow.com/a/57122621/3179416
import SwiftUI
struct MasterView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: LoginView()) {
Text("Login")
}
}
.navigationBarTitle(Text("Master"))
}
}
}
struct LoginView: View {
var body: some View {
Text("Login View")
}
}
Second, you can present a modal using .sheet. This will present a modal that appears on top of the current view but it can be dismissed by the user by dragging it down.
import SwiftUI
struct MasterView: View {
#State var isModal: Bool = false
var body: some View {
Button("Login") {
self.isModal = true
}.sheet(isPresented: $isModal, content: {
LoginView()
})
}
}
struct LoginView: View {
var body: some View {
Text("Login View")
}
}
Third, you can just use an if statement to change the current view to your Login View like so
import SwiftUI
struct MasterView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
LoginView()
} else {
Button("Login") {
self.showLoginView = true
}
}
}
}
}
struct LoginView: View {
var body: some View {
Text("Login View")
}
}
If you would like to animate this, so that the transition doesn't appear so abruptly, you can also do this:
import SwiftUI
struct MasterView: View {
#State var showLoginView: Bool = false
var body: some View {
VStack {
if showLoginView {
LoginView()
.animation(.spring())
.transition(.slide)
} else {
Button("Login") {
withAnimation {
self.showLoginView = true
}
}.animation(.none)
}
}
}
}
struct LoginView: View {
var body: some View {
Text("Login View")
}
}
You can use navigation link instead button
var body: some View {
VStack {
Text("Title")
.font(.headline)
Image("myimage").clipShape(Circle())
Text("mytext").font(.title)
NavigationLink(destination: AnotherView()) {
Image(systemName: "person.circle").imageScale(.large)
}
}
}
I would like to be able to show a new view when a button is pressed on one of my views.
From the tutorials I have looked at and other answered questions here it seems like everyone is using navigation button within a navigation view, unless im mistaken navigation view is the one that gives me a menu bar right arrows the top of my app so I don't want that. when I put the navigation button in my view that wasn't a child of NavigationView it was just disabled on the UI and I couldn't click it, so I guess I cant use that.
The other examples I have seen seem to use presentation links / buttons which seem to show a sort of pop over view.
Im just looking for how to click a regular button and show another a view full screen just like performing a segue used to in the old way of doing things.
Possible solutions
1.if you want to present on top of current view(ex: presentation style in UIKit)
struct ContentView: View {
#State var showingDetail = false
var body: some View {
Button(action: {
self.showingDetail.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $showingDetail) {
DetailView()
}
}
}
2.if you want to reset current window scene stack(ex:after login show home screen)
Button(action: goHome) {
HStack(alignment: .center) {
Spacer()
Text("Login").foregroundColor(Color.white).bold()
Spacer()
}
}
func goHome() {
if let window = UIApplication.shared.windows.first {
window.rootViewController = UIHostingController(rootView: HomeScreen())
window.makeKeyAndVisible()
}
}
3.push new view (ex: list->detail, navigation controller of UIKit)
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Show Detail View")
}.navigationBarTitle("Navigation")
}
}
}
}
4.update the current view based on #state property, (ex:show error message on login failure)
struct ContentView: View {
#State var error = true
var body: some View {
...
... //login email
.. //login password
if error {
Text("Failed to login")
}
}
}
For simple example you can use something like below
import SwiftUI
struct ExampleFlag : View {
#State var flag = true
var body: some View {
ZStack {
if flag {
ExampleView().tapAction {
self.flag.toggle()
}
} else {
OtherExampleView().tapAction {
self.flag.toggle()
}
}
}
}
}
struct ExampleView: View {
var body: some View {
Text("some text")
}
}
struct OtherExampleView: View {
var body: some View {
Text("other text")
}
}
but if you want to present more view this way looks nasty
You can use stack to control view state without NavigationView
For Example:
class NavigationStack: BindableObject {
let didChange = PassthroughSubject<Void, Never>()
var list: [AuthState] = []
public func push(state: AuthState) {
list.append(state)
didChange.send()
}
public func pop() {
list.removeLast()
didChange.send()
}
}
enum AuthState {
case mainScreenState
case userNameScreen
case logginScreen
case emailScreen
case passwordScreen
}
struct NavigationRoot : View {
#EnvironmentObject var state: NavigationStack
#State private var aligment = Alignment.leading
fileprivate func CurrentView() -> some View {
switch state.list.last {
case .mainScreenState:
return AnyView(GalleryState())
case .none:
return AnyView(LoginScreen().environmentObject(state))
default:
return AnyView(AuthenticationView().environmentObject(state))
}
}
var body: some View {
GeometryReader { geometry in
self.CurrentView()
.background(Image("background")
.animation(.fluidSpring())
.edgesIgnoringSafeArea(.all)
.frame(width: geometry.size.width, height: geometry.size.height,
alignment: self.aligment))
.edgesIgnoringSafeArea(.all)
.onAppear {
withAnimation() {
switch self.state.list.last {
case .none:
self.aligment = Alignment.leading
case .passwordScreen:
self.aligment = Alignment.trailing
default:
self.aligment = Alignment.center
}
}
}
}
.background(Color.black)
}
}
struct ExampleOfAddingNewView: View {
#EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.push(state: .emailScreen) }){
Text("Tap me")
}
}
}
}
struct ExampleOfRemovingView: View {
#EnvironmentObject var state: NavigationStack
var body: some View {
VStack {
Button(action:{ self.state.pop() }){
Text("Tap me")
}
}
}
}
In my opinion this bad way, but navigation in SwiftUI much worse