As continue research of reverted List in SwiftUI How to make List reversed in SwiftUI.
Getting strange spacing in reverted list, which looks like extra UITableView header/footer.
struct ContentView: View {
#State private var ids = ["header", "test2"]
#State private var text = "text"
init() {
UITableView.appearance().tableFooterView = UIView()
UITableView.appearance().separatorStyle = .none
}
var body: some View {
ZStack (alignment: .bottomTrailing) {
VStack {
List {
ForEach(ids, id: \.self) { id in
Group {
if (id == "header") {
VStack {
Text("Test")
.font(.largeTitle)
.fontWeight(.heavy)
Text("header")
.foregroundColor(.gray)
}
.scaleEffect(x: 1, y: -1, anchor: .center)
} else {
Text(id).scaleEffect(x: 1, y: -1, anchor: .center)
}
}
}
}
.scaleEffect(x:
1, y: -1, anchor: .center)
.padding(.bottom, -8)
Divider()
VStack(alignment: .leading) {
HStack {
Button(action: {}) {
Image(systemName: "photo")
.frame(width: 60, height: 40)
}
TextField("Message...", text: $text)
.frame(minHeight: 40)
Button(action: {
self.ids.insert(self.text, at:0 )
}) {
Image(systemName: "paperplane.fill")
.frame(width: 60, height: 40)
}
}
.frame(minHeight: 50)
.padding(.top, -13)
.padding(.bottom, 50)
}
.foregroundColor(.secondary)
}
}
}
}
Looks not critical but in my more complicated code it shows more spacing.
Ok, turns out it is another SwiftUI bug.
To get around it, you should add .offset(x: 0, y: -1) to the List.
Working example:
struct ContentView: View {
#State private var ids = ["header", "test2"]
#State private var text = ""
init() {
UITableView.appearance().tableFooterView = UIView()
UITableView.appearance().separatorStyle = .none
UITableView.appearance().backgroundColor = .clear
}
var body: some View {
ZStack (alignment: .bottomTrailing) {
VStack {
List(ids, id: \.self) { id in
Group {
if (id == "header") {
VStack(alignment: .leading) {
Text("Test")
.font(.largeTitle)
.fontWeight(.heavy)
Text("header").foregroundColor(.gray)
}
} else {
Text(id)
}
}.scaleEffect(x: 1, y: -1, anchor: .center)
}
.offset(x: 0, y: -1)
.scaleEffect(x: 1, y: -1, anchor: .center)
.background(Color.red)
Divider()
VStack(alignment: .leading) {
HStack {
Button(action: {}) {
Image(systemName: "photo").frame(width: 60, height: 40)
}
TextField("Message...", text: $text)
Button(action: {
self.ids.append(self.text)
}) {
Image(systemName: "paperplane.fill").frame(width: 60, height: 40)
}
}
}
.foregroundColor(.secondary)
}
}
}
}
Note that I changed your code a bit to make it more observable and have less code.
Related
I'm making a swiftui app, I have a NavigationView that contains a VStack and a List inside.
I've tried to put the following line of code in a lot of places .background(Color.blue) but it had no effect anywhere (nothing happened).
How can I set a background for the view itself?
I know it’s a very simple thing, but it doesn’t work out at all...
This is my code:
struct ContentView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(entity: Todo.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Todo.name, ascending: true)]) var todos: FetchedResults<Todo>
#State private var showingAddTodoView: Bool = false
#State private var animatingButton: Bool = false
var body: some View {
NavigationView {
ZStack {
List {
ForEach(self.todos, id: \.self) { todo in
HStack {
Circle()
.frame(width: 12, height: 12, alignment: .center)
.foregroundColor(self.colorize(priority: todo.priority ?? "Normal"))
Text(todo.name ?? "Unknown")
.fontWeight(.semibold)
Spacer()
Text(todo.priority ?? "Unkown")
.font(.footnote)
.foregroundColor(Color(UIColor.systemGray2))
.padding(3)
.frame(minWidth: 62)
.overlay(
Capsule().stroke(Color(self.colorize(priority: todo.priority ?? "Normal")), lineWidth: 0.75)
)
Spacer()
Image(systemName: todo.completed ? "checkmark.square": "square")
.foregroundColor(todo.completed ? .green : .black)
.onTapGesture {
self.updateTodo(todo)
}
}
.padding(.vertical, 10)
}
.onDelete(perform: deleteTodo)
}
.navigationBarTitle("Todos", displayMode: .inline)
.navigationBarItems(
leading: EditButton(),
trailing:
Button(action: {
self.showingAddTodoView.toggle()
}) {
Image(systemName: "plus")
}
.sheet(isPresented: $showingAddTodoView) {
AddTodoView().environment(\.managedObjectContext, self.managedObjectContext)
}
)
if todos.count == 0 {
NoTodosView()
}
}
.sheet(isPresented: $showingAddTodoView) {
AddTodoView().environment(\.managedObjectContext, self.managedObjectContext)
}
.overlay(
ZStack {
Group {
Circle()
.fill(Color.blue)
.opacity(self.animatingButton ? 0.2 : 0)
.scaleEffect(self.animatingButton ? 1 : 0)
.frame(width: 68, height: 68, alignment: .center)
Circle()
.fill(Color.blue)
.opacity(self.animatingButton ? 0.15 : 0)
.scaleEffect(self.animatingButton ? 1 : 0)
.frame(width: 88, height: 88, alignment: .center)
}
.animation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true))
Button(action: {
self.showingAddTodoView.toggle()
}) {
Image(systemName: "plus.circle.fill")
.resizable()
.scaledToFit()
.background(Circle().fill(Color("Color")))
.frame(width: 48, height: 48, alignment: .center)
}
.onAppear(perform: {
self.animatingButton.toggle()
})
}
.padding(.bottom, 15)
.padding(.trailing, 15)
, alignment: .bottomTrailing
)
}
}
I want to set the background color for this view:
try this...
struct ContentView: View {
var body: some View {
UITableView.appearance().backgroundColor = .clear
UITableViewCell.appearance().backgroundColor = .clear
return NavigationView {
ZStack {
Color.blue
.edgesIgnoringSafeArea(.all)
List{
ForEach((1...5), id: \.self){_ in
HStack{
Text("item")
}
}
// .listRowBackground(Color.blue)
}
}
}
}
}
the swift code below represents two views the first that is displayed is home with the login button and the register button, what I have to do is that when the SIGNUP button is clicked the push to the HomeView view which contains the MapView, practically I have to push to the new view with the click on signup, but with the navigationlink this does not show me the view, how do I solve this?
Home.Swift
import SwiftUI
import MapKit
struct HomeView: View {
#State private var userTrackingMode: MapUserTrackingMode = .follow
#State private var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: 25.7617,
longitude: 80.1918
),
span: MKCoordinateSpan(
latitudeDelta: 10,
longitudeDelta: 10
)
)
var body: some View {
Map(
coordinateRegion: $region,
interactionModes: MapInteractionModes.all,
showsUserLocation: true,
userTrackingMode: $userTrackingMode
)
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
ContectView.Swift
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
LinearGradient(gradient: .init(colors: [Color("Color"), Color("Color1"), Color("Color2")]), startPoint: .top, endPoint: .bottom).edgesIgnoringSafeArea(.all)
if UIScreen.main.bounds.height > 800 {
Home()
}
else {
ScrollView(.vertical, showsIndicators: false) {
Home()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home: View {
#State var index = 0
#State var showingDetail = false
#State var isModal: Bool = false
func login() {
}
var body: some View {
VStack {
Image("logo")
.resizable()
.frame(width: 200, height: 180)
HStack {
Button(action: {
withAnimation(.spring(response: 0.8, dampingFraction: 0.5, blendDuration: 0.5)) {
self.index = 0
}
}) {
Text("Login")
.foregroundColor(self.index == 0 ? .black : .white)
.fontWeight(.bold)
.padding(.vertical, 10)
.frame(width: (UIScreen.main.bounds.width - 50) / 2).sheet(isPresented: $isModal, content: {
})
}.background(self.index == 0 ? Color.white : Color.clear)
.clipShape(Capsule())
Button(action: {
withAnimation(.spring(response: 0.8, dampingFraction: 0.5, blendDuration: 0.5)) {
self.index = 1
}
}) {
Text("New User")
.foregroundColor(self.index == 1 ? .black : .white)
.fontWeight(.bold)
.padding(.vertical, 10)
.frame(width: (UIScreen.main.bounds.width - 50) / 2)
}.background(self.index == 1 ? Color.white : Color.clear)
.clipShape(Capsule())
}.background(Color.black.opacity(0.1))
.clipShape(Capsule())
.padding(.top, 25)
if self.index == 0 {
Login(mail: "", pass: "", areYouGoingToSecondView: false)
}
else {
SignUp()
}
if self.index == 0 {
Button(action: {
}) {
Text("Forget Password?")
.foregroundColor(.white)
}
.padding(.top, 20)
}
}
.padding()
}
}
struct Login: View {
#State var mail = ""
#State var pass = ""
#State var areYouGoingToSecondView: Bool = false
var body: some View {
VStack {
VStack {
HStack(spacing: 15) {
Image(systemName: "envelope")
.foregroundColor(.black)
TextField("Enter Email Address", text: self.$mail)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Password", text: self.$pass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
}
.padding(.vertical)
.padding(.horizontal, 20)
.padding(.bottom, 40)
.background(Color.white)
.cornerRadius(10)
.padding(.top, 25)
Button(action: {
print("Prova di stampa")
}) {
Text("LOGIN")
.foregroundColor(.white)
.fontWeight(.bold)
.padding(.vertical)
.frame(width: UIScreen.main.bounds.width - 100)
}.background(
LinearGradient(gradient: .init(colors: [Color("Color2"), Color("Color1"), Color("Color")]), startPoint: .leading, endPoint: .trailing)
)
.cornerRadius(8)
.offset(y: -40)
.padding(.bottom, -40)
.shadow(radius: 15)
}
}
}
struct SignUp: View {
#State var mail = ""
#State var pass = ""
#State var checkpass = ""
#State var repass = ""
#State var name = ""
#State var surname = ""
func signup() -> Bool {
print("Sono in signup")
/*let u = User(email: self.mail, name: self.name, password: self.pass, surname: self.surname)
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
u.register(completion: { result in
print("\n Risultato:")
print(result)
}
)
}
)*/
NavigationLink(destination: HomeView()) {
Text("Show Detail View")
}
return true
}
var body: some View {
VStack {
VStack {
HStack(spacing: 15) {
Image(systemName: "envelope")
.foregroundColor(.black)
TextField("Enter Email Address", text: self.$mail)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "person")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
TextField("Enter Name", text: self.$name)
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "person")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
TextField("Enter Surname", text: self.$surname)
}.padding(.vertical, 20)
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Password", text: self.$pass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
Divider()
HStack(spacing: 15) {
Image(systemName: "lock")
.resizable()
.frame(width: 15, height: 18)
.foregroundColor(.black)
SecureField("Re-Enter Password", text: self.$checkpass)
Button(action: {
}) {
Image(systemName: "eye")
.foregroundColor(.black)
}
}.padding(.vertical, 20)
}
.padding(.vertical)
.padding(.horizontal, 20)
.padding(.bottom, 40)
.background(Color.white)
.cornerRadius(10)
.padding(.top, 25)
//Button for signup user
Button(action: {
signup()
}) {
Text("SIGNUP")
.foregroundColor(.white)
.fontWeight(.bold)
.padding(.vertical)
.frame(width: UIScreen.main.bounds.width - 100)
}.background(
LinearGradient(gradient: .init(colors: [Color("Color2"), Color("Color1"), Color("Color")]), startPoint: .leading, endPoint: .trailing)
)
.cornerRadius(8)
.offset(y: -40)
.padding(.bottom, -40)
.shadow(radius: 15)
}
}
}
I would like to display the details of a tourist destination when a destination is selected. Below is the syntax that I created, I call self.presenter.getDetail(request: destination.id) which is in .onAppear, when the program starts and I press a destination, xcode says that self.presenter.detailDestination!.like doesn't exist or nil. Even when I insert print ("TEST") what happens is error nil from self.presenter.detailDestination!.like
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
#State private var showingAlert = false
#ObservedObject var presenter: GetDetailPresenter<
Interactor<String, DestinationDomainModel, GetDetailDestinationRepository<
GetDestinationLocaleDataSource, GetDetailDestinationRemoteDataSource,
DetailDestinationTransformer>>,
Interactor<String, DestinationDomainModel, UpdateFavoriteDestinationRepository<
FavoriteDestinationLocaleDataSource, DetailDestinationTransformer>>>
var destination: DestinationDomainModel
var body: some View {
ZStack {
if presenter.isLoading {
loadingIndicator
} else {
ZStack {
GeometryReader { geo in
ScrollView(.vertical) {
VStack {
self.imageCategory
.padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(width: geo.size.width, height: 270)
self.content
.padding()
}
}
}
.edgesIgnoringSafeArea(.all)
.padding(.bottom, 80)
VStack {
Spacer()
favorite
.padding(EdgeInsets.init(top: 0, leading: 16, bottom: 10, trailing: 16))
}
}
}
}
.onAppear {
self.presenter.getDetail(request: destination.id)
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: btnBack)
}
}
extension DetailView {
var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.left.circle.fill")
.aspectRatio(contentMode: .fill)
.foregroundColor(.black)
Text("Back")
.foregroundColor(.black)
}
}
}
var spacer: some View {
Spacer()
}
var loadingIndicator: some View {
VStack {
Text("Loading...")
ActivityIndicator()
}
}
var imageCategory: some View {
WebImage(url: URL(string: self.destination.image))
.resizable()
.indicator(.activity)
.transition(.fade(duration: 0.5))
.aspectRatio(contentMode: .fill)
.frame(width: UIScreen.main.bounds.width, height: 270, alignment: .center)
.clipShape(RoundedCorner(radius: 30, corners: [.bottomLeft, .bottomRight]))
}
var header: some View {
VStack(alignment: .leading) {
Text("\(self.presenter.detailDestination!.like) Peoples Like This")
.padding(.bottom, 10)
Text(self.presenter.detailDestination!.name)
.font(.largeTitle)
.bold()
.padding(.bottom, 5)
Text(self.presenter.detailDestination!.address)
.font(.system(size: 18))
.bold()
Text("Coordinate: \(self.presenter.detailDestination!.longitude), \(self.presenter.detailDestination!.latitude)")
.font(.system(size: 13))
}
}
var favorite: some View {
Button(action: {
self.presenter.updateFavoriteDestination(request: String(self.destination.id))
self.showingAlert.toggle()
}) {
if self.presenter.detailDestination!.isFavorite == true {
Text("Remove From Favorite")
.font(.system(size: 20))
.bold()
.onAppear {
self.presenter.getDetail(request: destination.id)
}
} else {
Text("Add To Favorite")
.font(.system(size: 20))
.bold()
}
}
.alert(isPresented: $showingAlert) {
if self.presenter.detailDestination!.isFavorite == true {
return Alert(title: Text("Info"), message: Text("Destination Has Added"),
dismissButton: .default(Text("Ok")))
} else {
return Alert(title: Text("Info"), message: Text("Destination Has Removed"),
dismissButton: .default(Text("Ok")))
}
}
.frame(width: UIScreen.main.bounds.width - 32, height: 50)
.buttonStyle(PlainButtonStyle())
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(12)
}
var description: some View {
VStack(alignment: .leading) {
Text("Description")
.font(.system(size: 17))
.bold()
.padding(.bottom, 7)
Text(self.presenter.detailDestination!.placeDescription)
.font(.system(size: 15))
.multilineTextAlignment(.leading)
.lineLimit(nil)
.lineSpacing(5)
}
}
var content: some View {
VStack(alignment: .leading, spacing: 0) {
header
.padding(.bottom)
description
}
}
}
onAppear is called during the first render. That means that any values referred to in the view hierarchy (detailDestination in this case) will be rendered during this pass -- not just after onAppear.
In your header, you refer to self.presenter.detailDestination!.like. On the first render, there is not a guarantee that onAppear will have completed it's actions before you force unwrap detailDestination
The simplest solution to this is probably to only conditionally render the rest of the view if detailDestination exists. It looks like you're already trying to do this with isLoading, but there must be a mismatch of states -- my guess is before isLoading is even set to true.
So, your content view could be something like:
if self.presenter.detailDestination != nil {
VStack(alignment: .leading, spacing: 0) {
header
.padding(.bottom)
description
}
} else {
EmptyView()
}
This is all assuming that your presenter has a #Published property that will trigger a re-render of your current component when detailDestination is actually loaded.
I hope you are having a more pleasant evening than mine!
So as I mentioned in the title, my for each loop crashes whenever I try to remove an item from the original list with a binding. I did some research and the problem is that for each generates a view with an id but when you delete the item in your child view it can't find the contents and crashes. Returns 'Thread 1: Fatal error: Index out of range'. I can fix the issue by declaring a #State var instead of #Binding, which really works! However, I have more than a delete button in my child view and if I don't use a binding declaration, changes made don't reflect on the main view. I don't wanna give up on neither the delete button nor buttons. Is there way to keep all of them in my childview?
Mainview declarations;
struct ContentView: View {
#ObservedObject var superReminders = SuperReminders()
#State var superReminder = SuperReminder()}
My list View;
List{
ForEach(superReminders.reminderlist.indices, id: \.self) { index in
NavigationLink(destination: DetailedRemView(superReminder : self.$superReminders.reminderlist[index] ).environmentObject(superReminders)) {
squareImageView(superReminder : self.$superReminders.reminderlist[index]).environmentObject(superReminders).environmentObject(superReminders)
}.listRowBackground(Color.clear)
}.onDelete { indexSet in
superReminders.reminderlist.remove(atOffsets: indexSet)}
}
Childview declarations;
import SwiftUI
struct DetailedRemView: View {
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EE, MMM d, YYYY"
return formatter
}
#State public var showingDetail = false
#Environment(\.colorScheme) var colorScheme: ColorScheme
#State private var deleteReminderAlert = false
#EnvironmentObject var superReminders : SuperReminders
#Environment(\.presentationMode) var presentationMode
#Binding var superReminder : SuperReminder
#State private var showDialog = false
#State var animate = false
var body: some View {
VStack{
HStack(alignment: .center){
Text(superReminder.remdate)
.font(.title)
.multilineTextAlignment(.leading)
.padding(.leading)
.frame(minWidth: 100,maxWidth: .infinity, maxHeight: 50)
Spacer()
Button(action: {
self.showDialog.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue)
.frame(width: 80, height: 35)
HStack{
Text("Edit")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "pencil")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.leading)
.alert(isPresented: $showDialog,
TextAlert(title: "Edit reminder title",
message: "Enter a new title or dissmis.", placeholder: superReminder.remdate,
keyboardType: .default) { result in
if let text = result {
if text != "" {
superReminder.remdate = text }
else{}
} else {
}
})
})
.padding(.leading)
}
.frame(minWidth: 100, maxWidth: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/, maxHeight: 50, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(.vertical, -10)
ZStack(alignment: .topTrailing){
Image(superReminder.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 200,maxHeight: .infinity)
.saturation(superReminder.pastreminder ? 0.1 : 1)
.clipShape(Rectangle())
.cornerRadius(10)
.padding(.all)
.pinchToZoom()
HStack{
Text(superReminder.dateactual, formatter: dateFormatter)
.foregroundColor(.white)
.frame(width: 180, height: 30, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.background( superReminder.pastreminder ? Color.gray : Color.lightlygreen)
.cornerRadius(8)
.animation(/*#START_MENU_TOKEN#*/.easeIn/*#END_MENU_TOKEN#*/)
if superReminder.pastreminder == true {
ZStack{
RoundedRectangle(cornerRadius: 8)
.fill(Color.black)
.frame(width: 30, height: 30)
Image(systemName: "moon.zzz")
.foregroundColor(.white)
}.offset(x: animate ? -3 : 0)
.onAppear(perform: {
shake()
})
}
else{}
}
.zIndex(0)
.offset(x: -10, y: 10)
.padding()
}
.zIndex(1)
.shadow(color: Color.gray.opacity(0.4), radius: 3, x: 1, y: 2)
HStack{
Button(action: {
self.showingDetail.toggle()
}){
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.lightlygreen)
.frame(width: 140, height: 40)
HStack{
Text("Reschedule")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "calendar")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
}
.sheet(isPresented: $showingDetail, content :{
remdatepicker(isPresented: self.$showingDetail, superReminder: $superReminder)})
Button(action: {
if superReminder.pastreminder == true {
superReminder.pastreminder = false
}
else if superReminder.pastreminder == false{
superReminder.pastreminder = true
}
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(superReminder.pastreminder == true ? Color.lightlyblue : Color.gray)
.frame(width: 100, height: 40)
HStack{
Text(superReminder.pastreminder == true ? "Activate" : "Silence")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: superReminder.pastreminder == true ? "checkmark.circle" : "moon.zzz")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
Button(action: {
self.deleteReminderAlert.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color(.red))
.frame(width: 40, height: 40)
HStack{
Image(systemName: "trash")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
}.padding(.bottom, 20)
.alert(isPresented: $deleteReminderAlert){
Alert(
title: Text("Are you sure?"),
message: Text("Do you want to delete this reminder?"),
primaryButton: .destructive(Text("Yes"), action: {
superReminders.remove(superReminder: superReminder)
self.presentationMode.wrappedValue.dismiss()
}),
secondaryButton: .cancel(Text("No"))
)
}
}
}
func shake() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation(Animation.default.repeatCount(6).speed(7)){
animate.toggle()}}}
}
Class and List;
import SwiftUI
struct SuperReminder: Identifiable, Codable, Equatable {
var id = UUID()
var remdate = ""
var dateactual = Date.init()
var image = "New1"
var pastreminder = false
}
class SuperReminders: ObservableObject {
#Published var reminderlist: [SuperReminder]
init() {
self.reminderlist = [
]
}
func add(superReminder: SuperReminder) {
reminderlist.append(superReminder)
}
func remove(superReminder: SuperReminder) {
if let index = reminderlist.firstIndex(of: superReminder) {
reminderlist.remove(at: index)
}
}
}
This answer is similar to
Accessing and manipulating array item in an EnvironmentObject
Loop over superReminders.reminderlist since SuperReminder: Identifiable, Codable, Equatable.
ForEach(superReminders.reminderlist) { superReminder in
NavigationLink(destination: DetailedRemView(superReminders: superReminders,
superReminder: superReminder)) {
-----
}
}
In DetailedRemView, do the following:
struct DetailedRemView: View {
#ObservedObject var superReminders : SuperReminders
var superReminder : SuperReminder
// find index of current superReminder
var indexOfReminder: Int? {
superReminders.reminderlist.firstIndex {$0 == superReminder}
}
var body: some View {
// Unwrap indexOfReminder
if let index = indexOfReminder {
VStack {
------
}
}
}
----
}
Use superReminders.reminderlist[index] in DetailRemView whereever you need to update superReminder.
superReminders.reminderlist[index].pastreminder = false
I just upgrade to Xcode 11 Beta 5 and update my SwiftUI project.
In previous version I wanted to use PresentationLink component to show up a modal. I had the same problem than now, the modal has only shown once. I thought it was a bug as I saw in other SO posts. So I tried my chance by upgrading to Beta 5 but still no luck.
I noticed that this behaviour seems to be caused by wrapping in a ScrollView component. If I delete the ScrollView component everything works fine as expected.
Here's the code:
struct HomeList : View {
var listViewItems = listViewItemsData
#State var show = false
var body: some View {
VStack {
HStack {
VStack(alignment: .leading) {
Text("Project title").font(.largeTitle).fontWeight(.heavy)
Text("Project subtitle").foregroundColor(Color.gray)
}
Spacer()
}.padding(.top, 78).padding(.leading, 60)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 30) {
ForEach(listViewItems) { item in
GeometryReader { geometry in
Button(action: { self.show.toggle()}) {
ListView(title: item.title, image: item.image, color: item.color, destination: item.destination)
.rotation3DEffect(Angle(degrees: Double((geometry.frame(in: .global).minX - 30) / -30)), axis: (x: 0, y: 10, z: 0))
.sheet(isPresented: self.$show, content: { InformationView() })
}
}.frame(width: 246, height: 360)
}
}.padding(30)
Spacer()
}.frame(width: UIScreen.main.bounds.width, height: 480)
Spacer()
}
}
}
To summarize, without ScrollView wrapper the Modal behaviour works as expected.
I would like to know if there is a solution / workaround ? Or I just have to wait a release :)
Edit from answer:
struct HomeList : View {
var listViewItems = listViewItemsData
#State var show = false
#State var view: AnyView = AnyView(Text(""))
var body: some View {
VStack {
HStack {
VStack(alignment: .leading) {
Text("Project title").font(.largeTitle).fontWeight(.heavy)
Text("Project subtitle").foregroundColor(Color.gray)
}
Spacer()
}.padding(.top, 78).padding(.leading, 60)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 30) {
ForEach(listViewItems) { item in
GeometryReader { geometry in
Button(action: {
self.show.toggle()
self.view = item.destination
}) {
ListView(title: item.title, image: item.image, color: item.color, destination: item.destination)
.rotation3DEffect(Angle(degrees: Double((geometry.frame(in: .global).minX - 30) / -30)), axis: (x: 0, y: 10, z: 0))
}
}.frame(width: 246, height: 360)
}
}.padding(30)
Spacer()
}.frame(width: UIScreen.main.bounds.width, height: 480)
.sheet(isPresented: self.$show, content: { self.view })
Spacer()
}
}
}
This is the same issue as https://stackoverflow.com/a/57087399/3179416
Just move your .sheet outside of your ForEach.
import SwiftUI
struct Testing : View {
var listViewItems: [Int] = [1, 2, 3]
#State var show = false
var body: some View {
VStack {
HStack {
VStack(alignment: .leading) {
Text("Project title").font(.largeTitle).fontWeight(.heavy)
Text("Project subtitle").foregroundColor(Color.gray)
}
Spacer()
}.padding(.top, 78).padding(.leading, 60)
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 30) {
ForEach(listViewItems, id: \.self) { item in
GeometryReader { geometry in
Button(action: { self.show.toggle()}) {
Text("Button")
.rotation3DEffect(Angle(degrees: Double((geometry.frame(in: .global).minX - 30) / -30)), axis: (x: 0, y: 10, z: 0))
}
}.frame(width: 246, height: 360)
}
}.padding(30)
Spacer()
}.frame(width: UIScreen.main.bounds.width, height: 480)
.sheet(isPresented: self.$show, content: { Text("Modal") })
Spacer()
}
}
}