IOS App Crash when show keyboard after change binding inside sheet - ios

this is my code with swiftui, when I change tradingMode inside sheet by click button, after show keyboard from ContentView => app crash. Pease help me and thanks!
enum TradingMode {
case Derivatives, Equities
}
struct ContentView: View {
#State var tradingMode : TradingMode = TradingMode.Equities
#State var isShowSecondView = false
var body: some View {
VStack(content: {
Button("show second view") {
isShowSecondView.toggle()
}
TabView {
switch tradingMode {
case .Equities:
VStack(content: {
Text("Tab 1 Un")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 1")
}.tag(0)
case .Derivatives:
VStack(content: {
Text("Tab 1 Der")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 1")
}.tag(0)
}
switch tradingMode {
case .Equities:
VStack(content: {
Text("Tab 2 Un")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 2")
}.tag(1)
case .Derivatives:
VStack(content: {
Text("Tab 2 Der")
.padding()
TextField("ple", text: .constant(""))
})
.tabItem {
Text("tab 2")
}.tag(1)
}
}
})
.sheet(isPresented: $isShowSecondView, content: {
SecondView(tradingMode: $tradingMode)
})
}
}
struct SecondView: View {
#Environment(\.presentationMode) var presentation
#Binding var tradingMode : TradingMode
var body: some View {
VStack(content: {
Button("Change state") {
if tradingMode == .Derivatives {
tradingMode = .Equities
} else {
tradingMode = .Derivatives
}
self.presentation.wrappedValue.dismiss()
}
})
}
}
This is my bug: https://drive.google.com/file/d/18eXCmlByGqJylE_hCZbtJ4Rn0wmI0bb_/view?usp=sharing

Your code work well.
You can try to clean buildĀ folder CMD + shift + K or clear derived data in ~/Library/Developer/Xcode/DerivedData

Related

Animation not working inside sheet for swiftui

I am using a sheet to present a list of options and on click of the option I want to change the view with the animation of sliding from trailing. As per my understanding and what I have read on various sites I have written this code but I am not sure why it is not working the way intended. I just want to know where exactly this code went wrong.
struct XYZ: App {
let persistenceController = PersistenceController.shared
#State var isPresented : Bool = false
#State var isSwiped : Bool = false
var body: some Scene {
WindowGroup {
optionList(isPresented: $isPresented)
.sheet(isPresented: $isPresented, content: {
Text("This is from modal view!")
.onTapGesture {
withAnimation(Animation.easeIn(duration: 10)){
isSwiped.toggle()
}
}
if isSwiped {
checkedList()
.transition(.move(edge: .trailing))
}
})
}
}
}
struct optionList : View {
#Binding var isPresented : Bool
var body: some View {
Text("This is a testView")
.onTapGesture {
withAnimation{
isPresented.toggle()
}
}
}
}
struct checkedList : View {
#State var name : String = "WatchList"
var arr = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh"]
#State var mp : [Int:Int] = [:]
var body: some View {
VStack{
HStack{
TextField("WatchlistName", text: $name)
.padding(.all)
Image(systemName: "trash.fill")
.padding(.all)
.onTapGesture {
print("Delete watchList!!")
}
}
ScrollView{
ForEach(arr.indices) { item in
HStack (spacing: 0) {
Image(systemName: mp.keys.contains(item) ? "checkmark.square" : "square")
.padding(.horizontal)
Text(arr[item])
}
.padding(.bottom)
.frame(width: UIScreen.main.bounds.width, alignment: .leading)
.onTapGesture {
if mp.keys.contains(item) {
mp[item] = nil
} else {
mp[item] = 1
}
}
}
}
Button {
print("Remove Ticked Elements!")
deleteWatchListItem(arr: Array(mp.keys))
} label: {
Text("Save")
}
}
}
func deleteWatchList(ind: Int){
print(ind)
}
func deleteWatchListItem(arr : [Int]) {
print(arr)
}
}
I tried to create a view and with the animation using withanimation with a bool variable tried to change the view.
It sounds like what you want is to push the checkedList on to a NavigationStackā€¦
struct ContentView: View {
#State var isPresented : Bool = false
var body: some View {
Text("This is a testView")
.onTapGesture {
isPresented.toggle()
}
.sheet(isPresented: $isPresented, content: {
NavigationStack {
NavigationLink("This is from modal view!") {
checkedList()
}
}
})
}
}

SwiftUI | Dynamic Navigation Bar Title

Hello,
My app has a TabView inside the only navigation view in the whole app. To change the navigationTitle I use an extension that has a variable navigationBarTitle, that returnes different titles for every tab bar selection. Unfortunately, the whole "changing the title thing" stopped working a while back and I can't get it to update.
The switch inside is always called and returns correctly, but the navigation title doesn't change either way.
What am I doing wrong?
Here's the code
ContentView.swift
NavigationView {
TabView(selection: self.$viewModel.tabBarSelection) {
ListView(viewModel: self.toProcessViewModel)
.tabItem {
R.image.tabBar.toProcess
Text("tabbar.title.approval".localized)
}
.tag(0)
ListView(viewModel: self.myRequestsViewModel)
.tabItem {
R.image.tabBar.myRequests
Text("tabbar.title.myRequests".localized)
}
.tag(1)
PasswordView()
.tabItem {
R.image.tabBar.password
Text("tabbar.title.password".localized)
}
.tag(2)
.commonRedimBackground()
SettingsView(viewModel: self.settingsViewModel)
.tabItem {
R.image.tabBar.settings
Text("tabbar.title.settings".localized)
}
.tag(3)
}
.navigationBarTitle(Text(navigationBarTitle), displayMode: .inline)
.navigationBarHidden(navigationBarHidden)
.navigationBarTitleDisplayMode(.inline)
.accentColor(R.color.blue)
}
ContentViewModel.swift
final class ContentViewModel: ObservableObject {
#Published var tabBarSelection: Int = 0
}
ContentView-Extended
private extension ContentView { // For navigation
var navigationBarTitle: String {
switch viewModel.tabBarSelection {
case 0:
return "tabbar.title.approval".localized
case 1:
return "tabbar.title.myRequests".localized
case 2:
return "" //"tabbar.title.password".localized - UIKit View - remove after switching to SwiftUI
case 3:
return "tabbar.title.settings".localized
default:
return "SELECTION_ERROR"
}
}
var navigationBarHidden: Bool {
switch self.viewModel.tabBarSelection {
case 2:
return true
default:
return false
}
}
}
Edit after Yrb's comment - Unfortunately still not working
struct ContentView: View {
#ObservedObject var viewModel: ContentViewModel
#ObservedObject var toProcessViewModel: ListViewModel = ListViewModel(listType: .toProcess)
#ObservedObject var myRequestsViewModel: ListViewModel = ListViewModel(listType: .myRequests)
#ObservedObject var settingsViewModel: SettingsViewModel = SettingsViewModel()
#State var navTitle: String = ""
var body: some View {
NavigationView {
TabView(selection: self.$viewModel.tabBarSelection) {
ListView(viewModel: self.toProcessViewModel)
.tabItem {
R.image.tabBar.toProcess
Text("tabbar.title.approval".localized)
}
.tag(0)
ListView(viewModel: self.myRequestsViewModel)
.tabItem {
R.image.tabBar.myRequests
Text("tabbar.title.myRequests".localized)
}
.tag(1)
PasswordView()
.tabItem {
R.image.tabBar.password
Text("tabbar.title.password".localized)
}
.tag(2)
.commonRedimBackground()
SettingsView(viewModel: self.settingsViewModel)
.tabItem {
R.image.tabBar.settings
Text("tabbar.title.settings".localized)
}
.tag(3)
}
.navigationBarTitle(Text(navTitle), displayMode: .inline)
.navigationBarHidden(navigationBarHidden)
.navigationBarTitleDisplayMode(.inline)
.accentColor(R.color.redimBlue)
}
.toast(isPresented: self.$viewModel.showingToast, text: self.viewModel.toastTitle, image: self.viewModel.toastImage, backgroundColor: self.viewModel.toastBackgroundColor)
.bottomSheet(isPresented: self.$viewModel.showingBottomSheet, view: self.viewModel.bottomSheetView)
.alert(isPresented: self.$viewModel.showingAlert) {
Alert(title: Text(self.viewModel.alertTitle), message: Text(self.viewModel.alertMessage), dismissButton: .default(Text("general.understand".localized)))
}
.onReceive(self.viewModel.$tabBarSelection) { tag in
switch tag {
case 0:
navTitle = "tabbar.title.approval".localized
case 1:
navTitle = "tabbar.title.myRequests".localized
case 2:
navTitle = "" //"tabbar.title.password".localized - UIKit View - remove after switching to SwiftUI
case 3:
navTitle = "tabbar.title.settings".localized
default:
navTitle = "SELECTION_ERROR"
}
}
}
}
Edit: The only thing that worked so far, but I'm not really happy with that solution
struct ContentView: View {
#ObservedObject var viewModel: ContentViewModel
#ObservedObject var toProcessViewModel: ListViewModel = ListViewModel(listType: .toProcess)
#ObservedObject var myRequestsViewModel: ListViewModel = ListViewModel(listType: .myRequests)
#ObservedObject var settingsViewModel: SettingsViewModel = SettingsViewModel()
#State var uuid: UUID = UUID()
var body: some View {
NavigationView {
TabView(selection: self.$viewModel.tabBarSelection) {
ListView(viewModel: self.toProcessViewModel)
.tabItem {
R.image.tabBar.toProcess
Text("tabbar.title.approval".localized)
}
.tag(0)
ListView(viewModel: self.myRequestsViewModel)
.tabItem {
R.image.tabBar.myRequests
Text("tabbar.title.myRequests".localized)
}
.tag(1)
PasswordView()
.tabItem {
R.image.tabBar.password
Text("tabbar.title.password".localized)
}
.tag(2)
.commonRedimBackground()
SettingsView(viewModel: self.settingsViewModel)
.tabItem {
R.image.tabBar.settings
Text("tabbar.title.settings".localized)
}
.tag(3)
}
.navigationBarTitle(Text(navigationBarTitle), displayMode: .inline)
.navigationBarHidden(navigationBarHidden)
.navigationBarTitleDisplayMode(.inline)
.accentColor(R.color.redimBlue)
}
.id(uuid)
.toast(isPresented: self.$viewModel.showingToast, text: self.viewModel.toastTitle, image: self.viewModel.toastImage, backgroundColor: self.viewModel.toastBackgroundColor)
.bottomSheet(isPresented: self.$viewModel.showingBottomSheet, view: self.viewModel.bottomSheetView)
.alert(isPresented: self.$viewModel.showingAlert) {
Alert(title: Text(self.viewModel.alertTitle), message: Text(self.viewModel.alertMessage), dismissButton: .default(Text("general.understand".localized)))
}
.onReceive(self.viewModel.$tabBarSelection) { _ in
self.uuid = UUID()
}
}
}
private extension ContentView { // For navigation
var navigationBarTitle: String {
switch viewModel.tabBarSelection {
case 0:
return "tabbar.title.approval".localized
case 1:
return "tabbar.title.myRequests".localized
case 2:
return "" //"tabbar.title.password".localized - UIKit View - remove after switching to SwiftUI
case 3:
return "tabbar.title.settings".localized
default:
return "SELECTION_ERROR"
}
}
var navigationBarHidden: Bool {
switch self.viewModel.tabBarSelection {
case 2:
return true
default:
return false
}
}
}
Minimal, Reproducible Example that strangely works
ContentView
import SwiftUI
struct ContentView: View {
#ObservedObject var viewModel: ContentViewModel
var body: some View {
NavigationView {
TabView(selection: self.$viewModel.tabBarSelection) {
Text("Tag0")
.tabItem {
Image(systemName: "xmark")
Text("tabbar.title.settings")
}
.tag(0)
Text("Tag1")
.tabItem {
Image(systemName: "xmark")
Text("tabbar.title.settings")
}
.tag(1)
Text("Tag2")
.tabItem {
Image(systemName: "xmark")
Text("tabbar.title.settings")
}
.tag(2)
Text("Tag3")
.tabItem {
Image(systemName: "xmark")
Text("tabbar.title.settings")
}
.tag(3)
}
.navigationBarTitle(Text(navigationBarTitle))
.navigationBarTitleDisplayMode(.inline)
}
}
}
ContentViewModel
final class ContentViewModel: ObservableObject {
#Published var tabBarSelection: Int = 0
}
ContentView-Extension
private extension ContentView { // For navigation
var navigationBarTitle: String {
switch viewModel.tabBarSelection {
case 0:
return "title.approval"
case 1:
return "title.myRequests"
case 2:
return "title.password"
case 3:
return "title.settings"
default:
return "SELECTION_ERROR"
}
}
}

SwiftUI: How to display the second Sheet when first sheet is closed

I want to make a side menu with .fullScreenCover, when a user is logged in and press MyGarage button. the .fullScreenCover will dismiss and the main view will navigate to MyGarage View. But if user is not logged in the .fullScreenCover will dismiss and a loginView with .fullScreenCover will appear. My problem is, the .fullScreenCover will not work if I put 2 same .fullScreenCover inside the main view. Is there any way to solve this? I'm sorry it's a little bit difficult for me to explain.
Here's the code
SideMenuView
struct SideMenuView: View {
#Environment(\.presentationMode) var presentationMode
#Binding var showMyGarage: Bool
#Binding var showSignIn: Bool
var user = 0 //If user is 1, it is logged in
var body: some View {
NavigationView{
VStack{
Button(action: {
presentationMode.wrappedValue.dismiss()
if user == 1 {
self.showMyGarage = true
}else{
self.showSignIn = true
}
}, label: {
Text("My Garage")
})
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:
HStack(spacing: 20){
Button(action: {
presentationMode.wrappedValue.dismiss()
}, label: {
Text("X")
})
Text("Main Menu")
}
)
}.navigationViewStyle(StackNavigationViewStyle())
}
}
MainView
struct HomeView: View {
#State var showSideMenu = false
#State private var showMyGarage = false
#State var showSignIn = false
var body: some View {
VStack{
Text("Home")
NavigationLink(destination: MyGarageView(showMyGarage: $showMyGarage), isActive: $showMyGarage){
EmptyView()
}
}
.navigationBarItems(leading:
Button(action: {
self.showSideMenu.toggle()
}, label: {
Text("Menu")
})
)
.fullScreenCover(isPresented: $showSideMenu, content: {
SideMenuView(showMyGarage: $showMyGarage, showSignIn: $showSignIn)
})
.fullScreenCover(isPresented: $showSignIn, content: {
SignInView()
})
}
}
struct MyGarageView: View {
#Binding var showMyGarage: Bool
var body: some View {
Text("MyGarage")
}
}
struct SignInView: View {
var body: some View {
Text("Sign In")
}
}
Try to attach them to different views, like
var body: some View {
VStack{
Text("Home")
.fullScreenCover(isPresented: $showSideMenu, content: {
SideMenuView(showMyGarage: $showMyGarage, showSignIn: $showSignIn)
})
NavigationLink(destination: MyGarageView(showMyGarage: $showMyGarage), isActive: $showMyGarage){
EmptyView()
}
.fullScreenCover(isPresented: $showSignIn, content: {
SignInView()
})
}
.navigationBarItems(leading:
Button(action: {
self.showSideMenu.toggle()
}, label: {
Text("Menu")
})
)
}

Swiftui how to add a view above a tabview?

The requirement is to display a banner above the tabbar. So that the banner does not disappear when the tabs are changed? How can I achieve it?
I can think of starting points, to help you see ways to approach this, but I don't think either idea really meets your requirements. It will depend on the details of whether the banner is permanent, where its content comes from, etc.
The first idea:
struct BannerView : View {
var text : String
var body: some View {
VStack {
Spacer()
HStack {
Spacer()
Text(text)
Spacer()
}.background(Color.orange)
}
}
}
Then you can include this in a ZStack along with your tabs:
TabView {
ZStack {
Text("Tab 1")
BannerView("BANNER")
}.tabItem { Text("Home") }
ZStack {
Text("Tab 2")
BannerView("BANNER")
}.tabItem { Text("History") }
}
The second idea uses the BannerView from the first idea, but in a slightly cleaner way, still not great:
struct TabWrapperWithOptionalBanner<Content> : View where Content : View {
var showBanner : Bool
var content : Content
init(showBanner : Bool, #ViewBuilder content: () -> Content) {
self.showBanner = showBanner
self.content = content()
}
var body: some View {
ZStack {
content
if showBanner {
BannerView(text: "BANNER")
}
}
}
}
then your ContentView looks like this:
TabView {
TabWrapperWithOptionalBanner(showBanner: showBanner) {
Text("Tab 1")
}.tabItem { Text("Home") }
TabWrapperWithOptionalBanner(showBanner: showBanner) {
Text("Tab 2")
}.tabItem { Text("History") }
}
Update Try a pair of TabViews bound to the same State:
struct BanneredTabView: View {
#State private var selected = panels.one
var body: some View {
VStack {
TabView(selection: $selected) {
panels.one.label.tag(panels.one)
panels.two.label.tag(panels.two)
panels.three.label.tag(panels.three)
}
.tabViewStyle(PageTabViewStyle())
Text("Banner")
.frame(height: 40, alignment: .top)
TabView(selection: $selected) {
ForEach(panels.allCases) { panel in
Text("").tabItem {
panel.label
}
.tag(panel)
}
}
.frame(height: 30)
}
}
enum panels : Int, CaseIterable, Identifiable {
case one = 1
case two = 2
case three = 3
var label : some View {
switch self {
case .one:
return Label("Tab One", systemImage: "1.circle")
case .two:
return Label("Tab Two", systemImage: "2.square")
case .three:
return Label("Tab Three", systemImage: "asterisk.circle")
}
}
// so the enum can be identified when enumerated
var id : Int { self.rawValue }
}
}

How to use NavigationView and NavigationLink in SwiftUI?

Piece of code that I made for this question that runs in Playground:
import SwiftUI
struct Player {
let name: String
let surname: String
}
struct DetailView2: View {
var player: Player
var body: some View {
Text("\(player.name) \(player.surname)")
}
}
struct PlaygroundView: View {
// MARK: - Propertiers
#State private var selection = 0
private var players = [
Player(name: "Lionel", surname: "Messi"),
Player(name: "Diogo", surname: "Jota"),
]
// MARK: - View
var body: some View {
TabView(selection: $selection) {
NavigationView {
VStack {
Text("Settings").font(.title)
List(0..<players.count, id: \.self) { index in
NavigationLink(destination: DetailView2(player: players[index])) {
Text("\(index)")
}
}
}
.navigationTitle("Players")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
.background(Color.white)
.tabItem {
Image(systemName: "house.fill")
Text("Players")
}
.tag(0)
VStack {
Text("Settings").font(.title)
List(0..<2, id: \.self) { index in
Text("#\(index)")
}
}
.tabItem {
Image(systemName: "book.fill")
Text("Foo")
}
.tag(1)
}
}
}
struct Playground_Previews: PreviewProvider {
static var previews: some View {
PlaygroundView()
}
}
Current Players bar:
Current Settings bar:
How can I fix the code such that "Players" bar will look like Settings bar (it terms of the styling of the title). It seems like I've got the tab item and navigation working already.
It seems you're looking something like below (prepared & tested with Xcode 12.1 / iOS 14.1)
var body: some View {
TabView(selection: $selection) {
VStack {
Text("Settings").font(.title)
NavigationView {
List(0..<players.count, id: \.self) { index in
NavigationLink(destination: DetailView2(player: players[index])) {
Text("\(index)")
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
.background(Color.white)
.tabItem {
Image(systemName: "house.fill")
Text("Players")
}
.tag(0)
VStack {
Text("Settings").font(.title)
List(0..<2, id: \.self) { index in
Text("#\(index)")
}
}
.tabItem {
Image(systemName: "book.fill")
Text("Foo")
}
.tag(1)
}
}

Resources