Why fullScreenCover always take just first index of an array?
This is some example of code:
struct TestView: View {
#State private var isFullScreen: Bool = false
var body: some View {
VStack{
ForEach(0..<5, id:\.self) { number in
VStack{
Text("\(number)")
.background(.red)
.foregroundColor(.white)
.padding(20)
.onTapGesture {
isFullScreen.toggle()
}
}
.fullScreenCover(isPresented: $isFullScreen) {
test2View(title: number)
}
}
}
}
}
This is the code of test2View:
struct test2View: View {
var title:Int
var body: some View {
Text("\(title)")
}
}
Whenever I click on any number it always show just 0, but when I make navigationLink instead of fullScreenCover, it works as expected, but navigationLink isn't a solution for my problem, I want that to be fullScreenCover.
It's because fullScreenCover is using a single isFullScreen for each number so only the first one works. Fix by adding a third intermediary View to hold an isFullScreen bool for each number, e.g.
struct TestView: View {
var body: some View {
VStack{
ForEach(0..<5) { number in
TestView2(number: number)
}
}
}
}
struct TestView2: View {
let number: Int
#State private var isFullScreen: Bool = false
var body: some View {
Text("\(number, format: .number)")
.background(.red)
.foregroundColor(.white)
.padding(20)
.onTapGesture {
isFullScreen.toggle()
}
.fullScreenCover(isPresented: $isFullScreen) {
TestView3(number: number)
}
}
}
struct TestView3: View {
let number: Int
var body: some View {
Text("\(number, format: .number)")
}
}
I found a solution using .fullScreenCover item parameter like this:
struct TestView: View {
#State private var isFullScreen: Int? = nil
var body: some View {
VStack{
ForEach(0..<5, id:\.self) { number in
VStack{
Text("\(number)")
.background(.red)
.foregroundColor(.white)
.padding(20)
.onTapGesture {
isFullScreen = number
}
}
.fullScreenCover(item: $isFullScreen) { item in
test2View(title: item)
}
}
}
}
}
Related
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()
}
}
})
}
}
This is my proper model for the variables and values that is to be stored in it from the api.
struct Items : Hashable,Codable {
var id,origin_station_code,destination_station_code : Int
var station_path : [Int]
var date,state,city,map_url : String
}
This is the View:
struct SheetView: View {
var items : Items
#EnvironmentObject var viewModel: ViewModel
#EnvironmentObject var personvm : PersonViewModel
var body: some View
{
VStack(spacing:30)
{
URLImage(urlstring: items.map_url)
VStack(spacing:20)
{
HStack
{
VStack
{
Text("Ride ID").font(.subheadline)
Text("\(items.id)").fontWeight(.bold)
}
Spacer()
VStack
{
Text("Origin Station").font(.subheadline)
Text("\(items.origin_station_code)").fontWeight(.bold)
}
}//First row
Divider()
HStack
{
VStack
{
Text("Date").font(.subheadline)
Text(items.date).fontWeight(.bold)
}
Spacer()
VStack
{
Text("Distance").font(.subheadline)
Text("100Km").fontWeight(.bold)
}
}//Second row
Divider()
HStack
{
VStack
{
Text("State").font(.subheadline)
Text(items.state).fontWeight(.bold)
}
Spacer()
VStack
{
Text("Distance").font(.subheadline)
Text(items.city)
}
}//third row
Divider()
VStack{
Text("Station Path").font(.subheadline)
Text("\(items.station_path)") //The error occurs here and I am unable to print the array fetched from the api call
}
}
}
}
For some reason, my selectedTask State is Empty when presenting the Sheet,
even if I set it on the onTapGesture.
What I'm I missing?
struct TasksTabView: View {
#State private var showComputedTaskSheet: Bool = false
#State var selectedTask: OrderTaskCheck?
var body: some View {
VStack(alignment: .leading) {
List {
ForEach(Array(tasks.enumerated()), id:\.1.title) { (index, task) in
VStack(alignment: .leading, spacing: 40) {
HStack(spacing: 20) {
PillForRow(index: index, task: task)
}.padding(.bottom, 30)
}.onTapGesture {
// Where I'm setting selectedTask
self.selectedTask = task
self.showComputedTaskSheet.toggle()
}
}
}
}.listStyle(SidebarListStyle())
}
.sheet(isPresented: $showComputedTaskSheet) {
// self.selectedTask is returns nil
showScreen(task: self.selectedTask!)
}
.onAppear {
UITableView.appearance().backgroundColor = .white
}
}
Since I have no access to your full project this example can help you to get the idea, you can use .sheet() with item initializer like aheze said.
The advantage is here you pass optional to input item and you receive unwrapped safe value to work!
struct ContentView: View {
#State private var customValue: CustomValue?
var body: some View {
Button("Show the Sheet View") { customValue = CustomValue(description: "Hello, World!") }
.sheet(item: $customValue){ item in sheetView(item: item) }
}
func sheetView(item: CustomValue) -> some View {
return VStack {
Text(item.description)
Button("Close the Sheet View") { customValue = nil }.padding()
}
}
}
struct CustomValue: Identifiable {
let id: UUID = UUID()
var description: String
}
I have 2 Views in my Swift Project and when I click on the Button on the secondView, I want to update the List in the First View. I don't know how to do it! If I use a static variable in my MainView and then edit this variable from the secondView, it works, but it won't update. And if I don't use static and instead use #State, it would update, but I can't access it from my secondView.
Here is the Code below:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
MainView()
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("MainView")
}
}.tag(0)
UpdateOtherViewFromHere()
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("SecondView")
}
}.tag(1)
}
}
}
struct MainView: View {
var arrayList: [CreateListItems] = []
init() {
let a = CreateListItems(name: "First Name!")
let b = CreateListItems(name: "Second Name!")
let c = CreateListItems(name: "Third Name!")
arrayList.append(a)
arrayList.append(b)
arrayList.append(c)
}
var body: some View {
return VStack {
ZStack {
NavigationView {
List {
ForEach(arrayList) { x in
Text("\(x.name)")
}
}.navigationBarTitle("Main View")
}
}
}
}
}
struct UpdateOtherViewFromHere: View {
func updateList() {
//Code that should remove "FirstName" from the List in MainView
}
var body: some View {
return VStack {
Button(action: {
updateList()
}) {
Image(systemName: "heart.slash")
.font(.largeTitle)
Text("Click Me!")
}
}
}
}
struct CreateListItems: Identifiable {
var id: UUID = UUID()
var name: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can share it using #State and #Binding if you put
struct ContentView: View {
#State var arrayList: [CreateListItems] = []
struct MainView: View {
#Binding var arrayList: [CreateListItems]
struct UpdateOtherViewFromHere: View {
#Binding var arrayList: [CreateListItems]
or you use the MVVM pattern and store the list in an ObservableObject and use #StateObject/#ObservedObject (source) and use #EnvironmentObject(connection) to share it between your Views.
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
class ParentViewModel: ObservableObject{
#Published var arrayList: [CreateListItems] = []
init(){
addSamples()
}
func addSamples() {
let a = CreateListItems(name: "First Name!")
let b = CreateListItems(name: "Second Name!")
let c = CreateListItems(name: "Third Name!")
arrayList.append(a)
arrayList.append(b)
arrayList.append(c)
}
func updateList() {
let a = CreateListItems(name: "\(arrayList.count + 1) Name!")
arrayList.append(a)
}
}
struct ParentView: View {
#StateObject var vm: ParentViewModel = ParentViewModel()
var body: some View {
TabView {
MainView().environmentObject(vm)
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("MainView")
}
}.tag(0)
UpdateOtherViewFromHere().environmentObject(vm)
.tabItem() {
VStack {
Image(systemName: "circle.fill")
Text("SecondView")
}
}.tag(1)
}
}
}
struct MainView: View {
#EnvironmentObject var vm: ParentViewModel
var body: some View {
return VStack {
ZStack {
NavigationView {
List {
ForEach(vm.arrayList) { x in
Text(x.name)
}
}.navigationBarTitle("Main View")
}
}
}
}
}
struct UpdateOtherViewFromHere: View {
#EnvironmentObject var vm: ParentViewModel
var body: some View {
return VStack {
Button(action: {
vm.updateList()
}) {
Image(systemName: "heart.slash")
.font(.largeTitle)
Text("Click Me!")
}
}
}
}
I am passing binding variable into other view:
struct PocketlistView: View {
#ObservedObject var pocket = Pocket()
#State var isSheetIsVisible = false
var body: some View {
NavigationView{
List{
ForEach(Array(pocket.pockets.enumerated()), id: \.element.id) { (index, pocketItem) in
VStack(alignment: .leading){
Text(pocketItem.name).font(.headline)
Text(pocketItem.type).font(.footnote)
}
.onTapGesture {
self.isSheetIsVisible.toggle()
}
.sheet(isPresented: self.$isSheetIsVisible){
PocketDetailsView(pocketItem: self.$pocket.pockets[index])
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Pockets")
}
}
}
the other view is:
struct PocketDetailsView: View {
#Binding var pocketItem: PocketItem
var body: some View {
Text("\(pocketItem.name)")
}
}
Why I see the first item when i open sheet for second or third row?
When I use NavigationLink instead of the .sheet it works perfect
You activate all sheets at once, try the following approach (I cannot test your code, but the idea should be clear)
struct PocketlistView: View {
#ObservedObject var pocket = Pocket()
#State var selectedItem: PocketItem? = nil
var body: some View {
NavigationView{
List{
ForEach(Array(pocket.pockets.enumerated()), id: \.element.id) { (index, pocketItem) in
VStack(alignment: .leading){
Text(pocketItem.name).font(.headline)
Text(pocketItem.type).font(.footnote)
}
.onTapGesture {
self.selectedItem = pocketItem
}
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Pockets")
.sheet(item: self.$selectedPocket) { item in
PocketDetailsView(pocketItem:
self.$pocket.pockets[self.pocket.pockets.firstIndex(of: item)!])
}
}
}
}