Add delete function to Grid layout in swiftUI for iOS - ios

I've managed to build a user form to get inputs and display them in grids. Right now I have them displayed in a scroll view. I'm avoiding lists so as to get a card like appearance. However, I'm not able to get a delete feature to work as it is done with Lists. I'm new to swift and not sure how to implement delete feature in this grid layout. I would like for users to be able to delete entries. I'm using Core Data, Swift and SwiftUI. Thanks.
struct ContentView: View {
// MARK: PROPERTIES
#Environment(\.managedObjectContext) var managedObjectContext
#State private var showingAddChangeView: Bool = false
#State private var dateAdded: Date = Date()
#State private var showingActionSheet = false
#FetchRequest(
entity: Agent.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Agent.dateAdded, ascending: false)]) var cas: FetchedResults<Agent>
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
return formatter
}()
private var gridItemLayout = [GridItem(.flexible())]
// MARK: BODY VIEW
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: gridItemLayout , alignment: .center, spacing: 30) {
ForEach(self.cas, id: \.self) { caRequest in
VStack(alignment: .leading) {
HStack {
Image(systemName: "calendar")
Text(caRequest.dateAdded ?? "Missing Dates")
.font(.callout).fontWeight(.bold)
Spacer()
Button(action: {
}) {
Image(systemName: "trash.fill")
.foregroundColor(.black)
}
.padding(.trailing, 20)
}
//.padding()
.padding(.leading, 20)
.padding(.top, 25)
Divider().frame(height: 1).background(Color.gray)
.padding()
HStack{
Image(systemName: "paintbrush.fill")
.foregroundColor(Color(.systemGreen))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.intention ?? "No Intentions Recorded")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "checkmark.seal.fill")
.foregroundColor(Color(.systemBlue))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.affirmation ?? "No Affirmation Recorded")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.gratitudeOne ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 5)
Text(caRequest.gratitudeTwo ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 5)
}
HStack{
Image(systemName: "heart.fill")
.foregroundColor(Color(.systemPink))
.padding(.leading, 20)
.padding(.bottom, 20)
Text(caRequest.gratitudeThree ?? "Missing Gratitude")
.font(.callout).multilineTextAlignment(.leading)
.padding(.leading, 5)
.padding(.bottom, 20)
}
}
// .frame(width: 350, height: 300)
.frame(minWidth: 0, maxWidth: 350, minHeight: 0, maxHeight: .infinity)
} // END: FOR EACH
} // END: LAZYVGRID
} // END: SCROLLVIEW
.padding(.top, 100)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
HStack {
Image(systemName: "person.crop.square.fill")
.foregroundColor(.white)
.font(.title)
Text("Change Agent")
.foregroundColor(.white)
.font(.title2)
.fontWeight(.bold)
.shadow(radius: 20)
}
}
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(
trailing:
Button(action: {
self.showingAddChangeView.toggle()
}) {
Text("Add")
Image(systemName: "plus")
.imageScale(.medium)
} // END: ADD BUTTON
.foregroundColor(.white)
.sheet(isPresented: $showingAddChangeView) {
AddChangeView()
}
.navigationBarItems(trailing: EditButton())
) // END: NAVIGATION BAR ITEMS
} // END: NAVIGATIONVIEW
} // END: BODY VIEW
} // END: STRUCT

Related

Reload body items once data from service layer loads in swiftUI

I'm new to the swiftUI, looking for a way to refresh view once service data retrieved. What I want is, once page loads, -or before page loads- I want to call fillUI function so I can retrieve parsed json data from service layer, with that data I want to refresh this code block so my foreach block works with data I have:
VStack(spacing:15 ) {
ScrollView(.horizontal) {
VStack {
ForEach(0..<5) { i in
MeetingCellView(meetingData: meetingListData?.get(at: i) )
.frame(width: 340, height: 150)
.padding(.horizontal)
}.frame(width: 350)
}
}.frame(width: 350)
}
In UIKit, i was able to solve these issues with a way like loading and retrieving all the data first, then showing the view itself with a data. For swiftUI what is the best approach to do that?
Here is the full code of my related view body. I had searched internet but couldn't find the one yet.
import SwiftUI
struct HomeView: View {
#State var username: String = ""
#State var meetingCode: String = ""
#State var data: GetUserDetailModel? = nil
#State var meetingListData: [Datum]? = nil
#State var requestLoaded = false
var body: some View {
ZStack{
VStack {
Image("home.background")
.resizable()
.frame(maxHeight: 338)
Spacer()
ZStack {
VStack {
RoundedRectangle(cornerRadius: 8)
.padding(.top, -20)
.foregroundColor(.white)
}
ScrollView(.vertical, showsIndicators: false)
{
//MARK: - ScrollView Start
VStack {
HStack(spacing: 13.0) {
NavigationLink(destination: CreateMeetingView()) {
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color(red: 0.935, green: 0.914, blue: 0.957))
VStack(spacing: 18) {
HStack {
Image("icon.camera")
Spacer()
}
HStack {
Text("New Meeting")
.foregroundColor(Color(red: 0.476, green: 0.228, blue: 0.701))
.font(.custom("inter-semibold", size: 15))
Spacer()
}
}.padding(.horizontal, 15)
}.frame(maxWidth: .infinity, idealHeight: 101)
.padding(.leading, 25)
}
NavigationLink(destination: Text("there"))
{
ZStack {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(Color(red: 0.897, green: 0.939, blue: 0.978))
VStack(spacing: 18) {
HStack {
Image("icon.calendar")
Spacer()
}
HStack {
Text("Schedule Now")
.foregroundColor(Color(red: 0.038, green: 0.525, blue: 0.917))
.font(.custom("inter-semibold", size: 15))
Spacer()
}
}.padding(.horizontal, 15)
}.frame(maxWidth: .infinity, idealHeight: 101)
.padding(.trailing, 25)
}
}
}
VStack {
HStack {
Text("Upcoming Meetings")
.font(.custom("inter-semibold", size: 17))
.padding(.leading, 25)
Spacer()
}
ZStack {
VStack(spacing: 30) {
HStack {
Image("upcoming.calendar")
}
HStack {
Text("You do not have a upcoming meeting.")
.font(.custom("inter-regular", size: 15))
.foregroundColor(Color(red: 0.692, green: 0.692, blue: 0.692))
}
}.hidden()
.padding(.vertical, 46)
VStack(spacing:15 ) {
ScrollView(.horizontal) {
VStack {
ForEach(0..<5) { i in
MeetingCellView(meetingData: meetingListData?.get(at: i) )
.frame(width: 340, height: 150)
.padding(.horizontal)
}.frame(width: 350)
}
}.frame(width: 350)
}
}
.padding(.horizontal ,15)
}.padding(.top, 25)
.padding(.bottom, 100)
//MARK: ScrollView End
}
}
}.ignoresSafeArea()
VStack {
HStack {
Image("splash.logo")
.resizable()
.frame(maxWidth: 100, maxHeight: 20)
.padding(.leading, 24)
Spacer()
Image("icon.profile")
.padding(.trailing,25)
}
VStack(spacing: 10.0) {
HStack {
Text("Good Morning,")
.font(.custom("inter-semibold", size: 13))
.foregroundColor(.white)
Spacer()
}
HStack {
Text(username)
.font(.custom("inter-semibold", size: 22))
.foregroundColor(.white)
Spacer()
}
}.padding(.leading, 24)
ZStack {
Color.white
VStack(spacing: 10.0) {
HStack {
Text("Join Meeting")
.font(.custom("inter-semibold", size: 13))
Spacer()
}
HStack {
TextField("Enter meeting code", text: $meetingCode)
.font(.custom("inter-regular", size: 15))
.keyboardType(.numbersAndPunctuation)
}
}.padding(.horizontal, 15)
}
.cornerRadius(6)
.frame(maxWidth: .infinity, maxHeight: 75.0)
.padding(.horizontal, 25.0)
.padding(.top, 30)
Spacer()
}.hiddenNavigationBarStyle()
}
.onAppear(perform: fillUI)
}
func fillUI() {
username = data?.givenName ?? "error"
Network.meetingList {meetings in
//print(meetings)
meetingListData = meetings?.data
requestLoaded = true
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
We use task(priority:_:) for that, .e.g
List {
ForEach(meetings) { meeting in
MeetingView(meeting: meeting)
}
}
.navigationTitle(loaded ? "Meetings" : "Loading...")
.task {
loaded = false
meetings = await Meeting.fetchAll()
loaded = true
}
Also your body is far too large and you have too many #State. Break it up into subviews and try to only have 1 or 2 #State per View.

How to set the background of a View in SwiftUI

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)
}
}
}
}
}

iOS 15 Keyboard hides textfield

I am trying to create a messaging page layout but the keyboard hides the textfield and moves everything to the the top. I have looked at many tutorials online and replicated them exactly but none of them seem to work.
Here is all the code I have written:
struct MessagingPage: View {
var user: OfficialUserModel
#ObservedObject private var vm = TheUserModel()
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: UserDisplayPage(user: user)) {
HStack{
WebImage(url: URL(string: user.imageURL ))
.resizable()
.aspectRatio( contentMode: .fill)
.frame(width: imageSize, height: imageSize
)
.cornerRadius(imageSize/2)
VStack(alignment: .leading){
Text(user.FullName)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
Text("\(user.City) , \(user.Country)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.myWhite)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.mainBlack)
ScrollView{
Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello \(user.firstName)")
.foregroundColor(.orange)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("\(user.FullName) is unable to recieve your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
HStack{Spacer()}
}
.background(Color.mainBlack)
ZStack{
HStack{
HStack{
HStack{
TextField("Say Something...", text: self.$text)
.placeholder(when: text.isEmpty) {
Text("Say Something...").foregroundColor(.myWhite.opacity(0.5))
}
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.myCyan)
.accentColor(.myCyan)
.background(Color.mainBlack)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
}
.frame(width: screenWidth - 150, height: screenWidth/25)
.padding()
.background(Color.mainBlack)
.cornerRadius(30)
.shadow(color: .myCyan, radius: 5)
.padding(.bottom,5)
}
}
.padding(.bottom, 50)
.frame(width: screenWidth)
}
}
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.mainBlack)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I want the the textfield to move up as well as the messages in the scrollview but the top HStack with the user image and back/dismiss button should remain in place.
You really have a lot of code in there - and two reasons to clean it up:
To get a good answer, you need to post a minimal, reproducible example, which may also help you debug the code before posting it
Many lines of code are redundant or useless, for example:
Why using things like HStack{Spacer()} instead of Spacer(), which by the way is not needed in this context?
Why having a stack that has only another stack inside?
Coming to your issue, the problem is that one of the redundant modifiers is preventing the text file to move up, and this is the line to delete:
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
Why setting a frame that is as high and large as much as the screen? This is forcing the text field to stay at the bottom.
I tried to do some clean up, I bet some more can be done - see the code below, now the keyboard does not cover the text field anymore:
struct MessagingPage: View {
// Removed some lines of code to reproduce your issue
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
// What is the purpose of this ZStack???
// ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: Text("Hello")) {
Text("Top bar")
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.white)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.black)
ScrollView{
// All the views inside the Scrollview need to be inside a VStack
VStack {
// Why this spacer???
// Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello username")
.foregroundColor(.orange)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("User is unable to receive your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
// Why a Spacer inside an HStack???
// HStack{Spacer()}
}
.background(Color.black)
}
// What is the purpose of this ZStack???
// ZStack{
// Why an HStack that has only an HStack inside??
// HStack{
// Why an HStack that has only an HStack inside??
// HStack{
HStack{
TextField("Say Something...", text: self.$text)
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.cyan)
.accentColor(.cyan)
.background(Color.white)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
}
// Are you sure you want set the height of this stack based on the width?
// .frame(width: screenWidth - 150, height: screenWidth/25)
.frame(width: screenWidth - 150)
.padding()
.background(Color.black)
.cornerRadius(30)
.shadow(color: .black, radius: 5)
.padding(.bottom,5)
// }
// }
.padding(.bottom, 50)
.frame(width: screenWidth)
// }
}
// }
// Why setting a frame that is high and large as much as the screen??
// .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.black)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}

onAppear SwiftUI iOS 14.4

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.

How to add .fullScreenCover() to a button by condition in SwiftUI?

I created a simple collection with navigation from the last page to the next screen.
How to correctly write transition condition if I need to apply the .fullScreenCover modifier to the button on the last page array index?
How do I correctly place the background image on the first collection screen so that it is not on the following screens, by convention, if the index is the first?
import SwiftUI
struct IntroView: View {
#ObservedObject var viewModel = IntroViewModel()
#State private var tabSelection = 0
#State private var isLastPage = false
var body: some View {
ZStack {
TabView(selection: $tabSelection) {
ForEach(0..<viewModel.pages.endIndex) { index in
VStack {
Image("icnDE")
.padding(.bottom, 20)
.padding(.top, 50)
.frame(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
Text(viewModel.pages[index].name)
.font(Font.custom("TeXGyreAdventor-Bold", size: 32))
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal,30)
Button(action: {
self.tabSelection += 1
self.isLastPage = false
}) {
Text(viewModel.pages[index].buttonName)
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}
if tabSelection == viewModel.pages.count - 1, isLastPage == true {
Button(action: {
self.tabSelection += 1
self.isLastPage = false
}) {
Text(viewModel.pages[index].buttonName)
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}.fullScreenCover(isPresented: $isLastPage, content: {
LoginView()})
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.tabViewStyle(PageTabViewStyle.init(indexDisplayMode: .never))
.edgesIgnoringSafeArea(.all)
}
.background(
Image("imgHappypeople")
.resizable()
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
}
}
struct Intro_Previews: PreviewProvider {
static var previews: some View {
IntroView()
.previewDevice("iPhone 11")
}
}
For open full screen cover, Add .fullScreenCover at the top of the ZStack and no need to add a condition for two-button.
struct IntroView: View {
#ObservedObject var viewModel = IntroViewModel()
#State private var tabSelection = 0
#State private var isLastPage = false
var body: some View {
ZStack {
TabView(selection: $tabSelection) {
ForEach(0..<viewModel.pages.endIndex) { index in
VStack {
Image("icnDE")
.padding(.bottom, 20)
.padding(.top, 50)
.frame(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
Text(viewModel.pages[index].name)
.font(Font.custom("TeXGyreAdventor-Bold", size: 32))
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal,30)
Button(action: {
if self.tabSelection == viewModel.pages.count - 1 {
self.isLastPage = true
} else {
self.tabSelection += 1
} //<-- Use this condition
}) {
Text("\(index)")
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.tabViewStyle(PageTabViewStyle.init(indexDisplayMode: .never))
.edgesIgnoringSafeArea(.all)
}
.fullScreenCover(isPresented: $isLastPage, content: {
Text("Details")}) //<== Use fullScreenCover here
.background(
Image("imgHappypeople")
.resizable()
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
}
}

Resources