For Each view pushing content away - ios

I'm working on a profile page and it has to render a list of previous runs and so I'm using a for each to do that. But for some reason, this view pushes all the other information out of view and I'm not sure why. Screenshot of the issue:
Here's the code:
struct AccountView: View {
#EnvironmentObject var session: SessionStore
#State var isShowingAccountDetails = false
#ObservedObject var tracks = RunsService()
var body: some View {
VStack(alignment: .leading) {
AccountHeader(tracks: tracks)
Text("This Week")
.font(.title)
.bold()
.padding(.leading)
PersonalWeeklyCard()
.frame(height: 150)
.padding(.horizontal)
Text("Previous runs")
.font(.title)
.bold()
.padding(.leading)
Spacer()
VStack{
ForEach(tracks.myRuns, id: \.id) { track in
RunCard(track: track)
}
}
}
.onAppear(perform: tracks.fetchRuns)
.navigationBarHidden(true)
.navigationBarItems(trailing: Button(action: {
isShowingAccountDetails = true
}, label: {Text("Details")}))
.sheet(isPresented: $isShowingAccountDetails) {
AccountDetails(session: session)
}
}
}

Related

sheet inside ForEach doesn't loop over id's

Each link has an id, the output I get when I click on the button is the wrong id, it repeats the first id on the first 4 buttons and then the others are random.
struct PricesList: View {
#ObservedObject var viewModel = ViewModel()
#State var isSheetPresented = false
var body: some View{
NavigationView{
ScrollView {
LazyVGrid(columns: gridLayout, spacing: 15, content: {
ForEach(viewModel.items, id: \.id) { item in
VStack(alignment: .leading, spacing: 5){
Button(action: {
self.isSheetPresented.toggle()
}, label: {
Image(item.imageUrl)
.resizable()
.scaledToFit()
.padding(10)
}).sheet(isPresented: $isSheetPresented, content: {
WebView(url: item.link)
})
}//:VSTACK
.scaledToFit()
.padding()
.background(Color.white.cornerRadius(12))
.background(RoundedRectangle(cornerRadius: 12).stroke(Color.gray, lineWidth: 1))
}//: LOOP FOR EACH
}).padding(5)
.onAppear(perform: {
viewModel.loadData()
viewModel.postData()
})
}
.navigationBarHidden(true)
}//: NAVIGATION VIEW
} //: BODY
}
You have sheet for each item in your list, and all of them are getting isSheetPresented value. Which one will be displayed is undefined
Instead you need to store selectedItem and pass it to single sheet, like this:
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
#State var selectedItem: Item?
var body: some View{
NavigationView{
ScrollView {
LazyVGrid(columns: gridLayout, spacing: 15, content: {
ForEach(viewModel.items, id: \.id) { item in
VStack(alignment: .leading, spacing: 5){
Button(action: {
selectedItem = item
}, label: {
Image(item.imageUrl)
.resizable()
.scaledToFit()
.padding(10)
})
}//:VSTACK
.scaledToFit()
.padding()
.background(Color.white.cornerRadius(12))
.background(RoundedRectangle(cornerRadius: 12).stroke(Color.gray, lineWidth: 1))
}//: LOOP FOR EACH
}).padding(5)
.onAppear(perform: {
viewModel.loadData()
viewModel.postData()
})
.sheet(item: $selectedItem, content: { selectedItem in
WebView(url: selectedItem.link)
})
}
.navigationBarHidden(true)
}//: NAVIGATION VIEW
} //: BODY
}

How do you make a tab bar where it will not mess up your views

I have tried multiple different ways to make a tab bar but for some reason, it messes up my views either preventing them from scrolling or their functionality completely corrupts here are two of the ways I tried why is this happening? They both allow me to switch between views but they bot alter the functionality or the performance of my views and it is confusing as to why it should just go from one view to the other no problem.
import SwiftUI
struct Home: View {
#State var selectedTab = "Global"
var edges = UIApplication.shared.windows.first?.safeAreaInsets
var body: some View {
GeometryReader { geometry in
ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom)) {
ZStack{
SettingsView()
.opacity(selectedTab == "Settings" ? 1 : 0)
Ether()
.opacity(selectedTab == "Ether" ? 1 : 0)
PostView()
.opacity(selectedTab == "Global" ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.frame(width: geometry.size.width, height: geometry.size.height)
CustomTabbar(selectedTab: $selectedTab)
.padding(.bottom,edges!.bottom == 0 ? 15 : 0)
}
.background(Color(.black).ignoresSafeArea(.all, edges: .all))
.ignoresSafeArea(.all, edges: .bottom)
.navigationBarHidden(true)
}
}
}
import SwiftUI
struct Home: View {
// #State var selectedTab = "Global"
var edges = UIApplication.shared.windows.first?.safeAreaInsets
var body: some View {
TabView {
PostView()
.tabItem {
Image(systemName: "flame")
Text("Global")
}
Ether()
.tabItem {
Image(systemName: "flame.fill")
Text("Ether")
}
SettingsView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
}
}
}
import SwiftUI
struct CustomTabbar: View {
#Binding var selectedTab : String
var body: some View {
HStack(spacing: 0){
TabButton(title: "Global", selectedTab: $selectedTab)
TabButton(title: "Ether", selectedTab: $selectedTab)
TabButton(title: "Settings", selectedTab: $selectedTab)
}
.padding(.horizontal)
.background(Color.white)
.clipShape(Capsule())
}
}
struct TabButton : View {
var title : String
#Binding var selectedTab : String
var body: some View{
Button(action: {selectedTab = title}) {
VStack(spacing: 5){
Image(title)
.renderingMode(.template)
Text(title)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(selectedTab == title ? Color(.blue) : .gray)
.padding(.horizontal)
.padding(.vertical,10)
}
}
}
}

I use navigationlink and I also want to use side menu

I have a NavigationView component in my iOS app that's built with SwiftUI.
I use navigationlink to change my page.In the second screen, I want to show side menu.But now I havetwo layers NavigationView in my iOS app, it's not my want to show.I want the second screen only have side menu.
is it possible or am i missing something about the way navigation is being managed in Swift?
How should I change my code?
here's my ContentView.swift:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
VStack {
NavigationLink(
destination: SwiftUIView_map(),
label: {
Text("Skip")
.fontWeight(.bold)
.font(.title)
.padding()
.background(Color.purple)
.cornerRadius(20)
.foregroundColor(.white)
})
HeaderView()
.offset(y:-60)
NavigationLink(
destination: Text("Login"),
label: {
Text("Login")
.fontWeight(.bold)
.font(.title)
.padding()
.background(Color.purple)
.cornerRadius(20)
.foregroundColor(.white)
})
NavigationLink(
destination: Text("Sign up"),
label: {
Text("Sign up")
.fontWeight(.bold)
.font(.title)
.padding()
.background(Color.purple)
.cornerRadius(20)
.foregroundColor(.white)
})
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct HeaderView: View {
var body: some View {
HStack{
VStack(alignment: .leading, spacing: 2){
Text("Just")
.font(.system(size: 60,weight:.heavy,design:.rounded))
.fontWeight(.black)
.multilineTextAlignment(.leading)
Text("test")
.font(.system(size: 60,weight:.heavy,design:.rounded))
.fontWeight(.black)
}
Spacer()
}
.padding()
}
}
The navigation sidebar:
import SwiftUI
struct SwiftUIView_map: View {
#State private var isShowing = false
var body: some View {
NavigationView{
ZStack{
if isShowing{
SideMenuView(isShowing: $isShowing)
}
HomeView()
.cornerRadius(isShowing ? 20 : 10 )
.offset(x: isShowing ? 300 : 0,y:isShowing ? 44 :0)
.scaleEffect(isShowing ? 0.8 : 1)
.navigationBarItems(leading: Button(action: {
withAnimation(.spring()){
isShowing.toggle()
}
}, label: {
Image(systemName: "list.bullet")
.foregroundColor(.black)
}))
.navigationTitle("home")
}
}
}
}
struct SwiftUIView_map_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView_map()
}
}
struct HomeView: View {
var body: some View {
ZStack{
Color(.white)
Text(/*#START_MENU_TOKEN#*/"Hello, World!"/*#END_MENU_TOKEN#*/)
.padding()
}
}
}
If you want more detailed code, please click the link
https://github.com/simpson0826/swifttest.git
You need to use the NavigationView only in ContentView(root view).
struct SwiftUIView_map: View {
#State private var isShowing = false
var body: some View {
// NavigationView{
ZStack{
if isShowing{
SideMenuView(isShowing: $isShowing)
}
HomeView()
.cornerRadius(isShowing ? 20 : 10 )
.offset(x: isShowing ? 300 : 0,y:isShowing ? 44 :0)
.scaleEffect(isShowing ? 0.8 : 1)
.navigationBarItems(trailing: Button(action: {
withAnimation(.spring()){
isShowing.toggle()
}
}, label: {
Image(systemName: "list.bullet")
.foregroundColor(.black)
}))
.navigationTitle("home")
}
// }
}
}

Presenting a sheet pops views from NavigationView

I have a view A that contains a NavigationView. A pushes View B by activating a NavigationLink. B pushes View C by activating a NavigationLink. C pushes View D by tapping a NavigationLink. D then presents a View as a sheet. If i dismiss the sheet the current top view is B. For some reason presenting the sheet pops 2 screens.
Can someone explain this behaviour and how i can fix it?
I'm guessing somehow the navigation link in view B (that pushed view C) get deActivated but I cant figure out why
From what i tested it seems when the sheet gets presented ViewB gets reinitialized which will reinit my vm and so deactivate the link. How to i stop the view from being reinitialized?
struct ViewA: View {
#SwiftUI.Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
#ObservedObject private var viewModel: ViewAVM
var body: some View {
NavigationView {
GeometryReader { fullView in
ScrollView(.vertical, showsIndicators: false) {
if !self.viewModel.loading {
VStack(alignment: .leading, spacing: 0) {
DetailsView(car: self.$viewModel.data)
Spacer(minLength: 20)
NavigationLink(destination: ViewB(rootActive: self.$viewModel.showB), isActive: self.$viewModel.showB) {
Button(action: {
self.viewModel.showB = true
}) {
SecondaryButtonView(enabled: true, title: "Go")
}
}.isDetailLink(false).padding([.leading, .trailing], 20)
}.frame(minHeight: fullView.size.height)
}
}
}
.navigationBarBackButtonHidden(true)
.navigationBarTitle("View A")
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image("LeftArrow").renderingMode(.original)
})
.onAppear {
self.viewModel.update()
}
}
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
struct ViewBUI: View {
#SwiftUI.Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
#ObservedObject private var viewModel: ViewBVM
private let rootActive: Binding<Bool>
var body: some View {
GeometryReader { fullView in
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading, spacing: 0) {
Text("").font(.regularParagraph).padding(.top, 15).padding(.bottom, 10)
CustomEntryView()
Spacer(minLength: 20)
NavigationLink(destination: ViewC(rootActive: self.rootActive), isActive: self.$viewModel.showC) {
Button(action: {
self.hideKeyboard()
self.viewModel.validate()
}) {
PrimaryButtonView(enabled: self.viewModel.valid, title: "Continue")
}.disabled(!self.viewModel.valid)
}.isDetailLink(false).padding(.bottom, 16)
}.padding([.leading, .trailing], 20).frame(minHeight: fullView.size.height)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarTitle(viewModel.new ? "New" : "Old")
.navigationBarItems(leading: Button(action: {
self.viewModel.rootActive.wrappedValue = false
}) {
Image("LeftArrow").renderingMode(.original)
})
}
}
struct ViewDUI: View {
#SwiftUI.Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
let isDriver: Bool
let car: Binding<Car?>
let rootActive: Binding<Bool>
var body: some View {
GeometryReader { fullView in
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading, spacing: 0) {
Text("").font(.regularParagraph).foregroundColor(Color.black).padding([.leading, .trailing], 20).padding(.top, 15)
CarDetailsView(car: self.car)
Spacer(minLength: 20)
if self.car.wrappedValue?.mot == true && self.car.wrappedValue?.taxed == true {
if self.isDriver {
Button(action: {
self.rootActive.wrappedValue = false
}) {
PrimaryButtonView(enabled: true, title: "Save")
}.padding(.bottom, 16).padding([.leading, .trailing], 20)
} else {
NavigationLink(destination: ViewE(rootActive: self.$viewModel.showB)) {
PrimaryButtonView(enabled: true, title: "Continue")
}.isDetailLink(false).padding(.bottom, 16).padding([.leading, .trailing], 20)
}
} else {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
PrimaryButtonView(enabled: true, title: "")
}.padding(.bottom, 16).padding([.leading, .trailing], 20)
}
}.frame(minHeight: fullView.size.height)
}
}
.navigationBarBackButtonHidden(true)
.navigationBarTitle("")
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image("LeftArrow").renderingMode(.original)
})
}
}
struct ViewEUI: View {
#SwiftUI.Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
private let rootActive: Binding<Bool>
private let isDriver: Bool
#State private var showingScanner = false
#State private var scanHandler: LicenseScanHandler!
#State private var scanCompleted = false
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text("")
.font(.regularParagraph)
.padding(.top, 15)
.padding([.leading, .trailing], 20)
ZStack(alignment: .topLeading) {
VStack(alignment: .leading, spacing: 0) {
Image("sample")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(20)
}
.overlay(RoundedRectangle(cornerRadius: 3).stroke(Color.azure, lineWidth: 1))
.background(Color.white.edgesIgnoringSafeArea(.all))
.padding(.top, 12)
Text("")
.font(.tinyParagraph)
.foregroundColor(.azure)
.frame(width: 80, height: 24)
.background(Color.white.edgesIgnoringSafeArea(.all))
.cornerRadius(8)
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.azure, lineWidth: 1))
.padding(.leading, 20)
}
.padding([.leading, .trailing], 40)
.padding(.top, 16)
Spacer(minLength: 20)
Button(action: {
self.showingScanner = true
}) {
PrimaryButtonView(enabled: true, title: "Scan")
}.padding(.bottom, 16).padding([.leading, .trailing], 20)
.sheet(isPresented: $showingScanner) {
ScannerWrapper(handler: self.scanHandler)
}
NavigationLink(destination: InfoUI(), isActive: $scanCompleted) {
Text("")
}.isDetailLink(false)
}
.background(Color.offWhite.edgesIgnoringSafeArea(.all))
.navigationBarBackButtonHidden(true)
.navigationBarTitle("")
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image("LeftArrow").renderingMode(.original)
})
.onAppear {
self.scanHandler = LicenseScanHandler(delegate: self)
}
}
}
Although it is not documented, I realised that presenting a .sheet() while the view itself is set to a "no detail view" via .isDetailLink(false) causes this issue.
I can see why, as if the view is not a detail view it cannot present a sheet, hence the sheet is actually presented by the first detail view and that is why it goes back to that view right after the sheet is presented.
Try removing .isDetailLink(false) and finding a different workaround if you really need it to be not a detail link.

SwiftUI list showing an array is not updated

In my app in SwiftUI, there is a list showing all items in an array. When I click on one item, its details are shown and can be modified. Those changes are stored in the array, but when I go back to the list view, the changes are only reflected after I made a change to that list array, like adding or moving an item. Can I make this list refresh when it re-appears?
My main view looks like this:
import SwiftUI
struct ContentView: View {
#State var items: [Item]
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: ItemDetailView(items: self.$items, index: self.items.firstIndex(of: item)!)) {
HStack {
VStack(alignment: .leading) {
Text(item.name).font(.title)
if item.serialNumber != nil {
Text(item.serialNumber!)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
Spacer()
Text("\(item.valueInDollars)$").font(.title)
}
}
}
.onDelete(perform: delete)
.onMove(perform: move)
Text("No more items!")
}
.navigationBarTitle(Text("Homepwner"), displayMode: .inline)
.navigationBarItems(leading: EditButton(), trailing: Button(action: addItem) { Text("Add") })
}
}
//... functions
}
The detail view looks like this:
import SwiftUI
struct ItemDetailView: View {
#Binding var items: [Item]
let index: Int
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Text("Name: ")
TextField("Item Name", text: $items[index].name)
}
//... more TextFields
}
.padding(.all, 8.0)
VStack(alignment: .center) {
//... button
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}.navigationBarTitle(items[index].name)
}
}
The class Item is Identifiable and Equatable and only holds the necessary members, the class ItemStore only holds an array of Items.
i just tried this (had to enhance code so it was compilable at all) and it works:
import SwiftUI
struct Item : Equatable, Identifiable, Hashable {
var id = UUID()
var name: String
var serialNumber : String?
var valueInDollars : Int = 5
}
struct ContentView: View {
#State var items: [Item] = [Item(name: "hi"), Item(name: "ho")]
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
NavigationLink(destination: ItemDetailView(items: self.$items, index: self.items.firstIndex(of: item)!)) {
HStack {
VStack(alignment: .leading) {
Text(item.name).font(.title)
if item.serialNumber != nil {
Text(item.serialNumber!)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
Spacer()
Text("\(item.valueInDollars)$").font(.title)
}
}
}
// .onDelete(perform: delete)
// .onMove(perform: move)
Text("No more items!")
}
.navigationBarTitle(Text("Homepwner"), displayMode: .inline)
// .navigationBarItems(leading: EditButton(), trailing: Button(action: addItem) { Text("Add") })
}
}
//... functions
}
struct ItemDetailView: View {
#Binding var items: [Item]
let index: Int
var body: some View {
VStack {
VStack(alignment: .leading) {
HStack {
Text("Name: ")
TextField("Item Name", text: $items[index].name)
}
//... more TextFields
}
.padding(.all, 8.0)
VStack(alignment: .center) {
//... button
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}
}.navigationBarTitle(items[index].name)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Resources