How to use if statement with struct in SwiftUI? - xcode

I am trying to use an if statement with a bool value in a struct to then see if a content view should be shown using that data. I keep getting the error "Ambiguous reference to member 'indices'" but I know this is not the actual problem it is just swiftUI saying there's an error on the forEach when it is the thing inside the forEach. This is the struct definition where needsToBeDeleted is false by default because it should be shown when first created
import Foundation
struct SEvent {
var eventName:String;
var timeSpent:String;
var timeStarted:Date;
var lastStarted:Date?;
var needsToBeDeleted = false;
}
and this is the piece where I am iterating over an array of the events
ForEach(arrayOfEvents.indices, id: \.self) { index in
if !self.arrayOfEvents[index].needsToBeDeleted {
EventContentView(eventName: self.arrayOfEvents[index].eventName, timeSpent:
self.arrayOfEvents[index].timeSpent, shouldDelete:
self.$arrayOfEvents[index].needsToBeDeleted)
}
}
Also this is the array declaration if that helps
#State private var arrayOfEvents = [SEvent]();
Edit:
This is the event content view code
import SwiftUI
struct EventContentView: View {
var eventName:String;
var timeSpent:String;
var startStop = ["Start", "Stop"];
#State var deleteTrigger = false;
var buttonColor = [Color.blue, Color.yellow]
#State var i = 0;
#Binding var shouldDelete:Bool;
var body: some View {
VStack{
VStack{
Text("\(eventName)")
.frame(minWidth: 0, idealWidth: 150, maxWidth: 200, minHeight: 0, idealHeight: 50, maxHeight: 75, alignment: .center)
Text("\(timeSpent)")
.frame(minWidth: 0, idealWidth: 150, maxWidth: 200, minHeight: 0, idealHeight: 50, maxHeight: 100, alignment: .leading)
if !deleteTrigger {
HStack {
Button(action: {
if self.i == 0{
self.i = 1
}
else if self.i == 1 {
self.i = 0
}
}) {
Text(startStop[i])
}
.foregroundColor(buttonColor[i])
.frame(minWidth: 0, idealWidth: 50, maxWidth: 100, minHeight: 0, idealHeight: 20, maxHeight: 40, alignment: .leading)
Button(action: {
self.deleteTrigger = true;
}) {
Text("Delete")
}
.foregroundColor(.red)
.animation(.interactiveSpring(response: 1, dampingFraction: 0.5, blendDuration: 1.5))
.frame(minWidth: 0, idealWidth: 50, maxWidth: 100, minHeight: 0, idealHeight: 20, maxHeight: 40, alignment: .trailing)
}
}
else{
HStack {
Button(action: {
self.shouldDelete = true
}) {
Text("Delete")
}
.foregroundColor(.red)
.frame(minWidth: 0, idealWidth: 50, maxWidth: 100, minHeight: 0, idealHeight: 20, maxHeight: 40, alignment: .leading)
Button(action: {
self.deleteTrigger = false;
}) {
Text("Don't Delete")
}
.foregroundColor(.green)
.animation(.easeInOut)
.frame(minWidth: 0, idealWidth: 50, maxWidth: 100, minHeight: 0, idealHeight: 20, maxHeight: 40, alignment: .trailing)
}
}
}
.frame(minWidth: 0, maxWidth: 200, minHeight: 20, idealHeight: 30, maxHeight: 150, alignment: .center)
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 5.0)
.stroke(Color.black, lineWidth: 1)
.shadow(radius: 5)
)
.padding()
}
}
and the specific part that deletes the view is this button where it changes the binding variable shouldDelete to true which changes the needsToBeDeleted element of the SEvent Struct in arrayOfEvents to true
Button(action: {
self.shouldDelete = true
})
{
Text("Delete")
}

You can use it like this
var body: some View {
ForEach(arrayOfEvents.indices, id: \.self) { index -> AnyView in
if !self.arrayOfEvents[index].needsToBeDeleted {
return AnyView(EventContentView(eventName: self.arrayOfEvents[index].eventName, timeSpent:
self.arrayOfEvents[index].timeSpent, shouldDelete:
self.$arrayOfEvents[index].needsToBeDeleted))
} else {
return AnyView(EmptyView())
}
}
}

Related

How Do i maintain Scrollview subitem height with Swiftui

I am new to swift and I am trying to wrap my view in Scrollview. The issue is that, when I wrap the content in the scrollview, the contents shrink. I have the images below.
sample code:
ScrollView(.vertical) {
GeometryReader { metrics in
HStack{
Button(action:{
isVisible = true
}, label: {
VStack(alignment: .center, spacing: 10){
Image(systemName: "book.fill")
.resizable()
.frame(width: 28, height: 22)
.padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
.foregroundColor(CustomColor.primaryColor)
Text("Daily Reading")
.padding(EdgeInsets(top: 0, leading: 0, bottom: 15, trailing: 0))
.foregroundColor(CustomColor.primaryColor)
}.frame(maxWidth: metrics.size.width/2.3, maxHeight: .infinity)
.background(RoundedRectangle(cornerRadius: 15)
.fill(Color.white)
.shadow(color: .gray, radius: 2, x: 0, y: 2))
})
Spacer()
Button(action: {
}, label: {
VStack(alignment: .center, spacing: 10){
Image("church")
.resizable()
.frame(maxWidth: 28, maxHeight: 22)
.padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
.foregroundColor(CustomColor.primaryColor)
Text("Church Prayers")
.padding(EdgeInsets(top: 0, leading: 0, bottom: 15, trailing: 0))
.foregroundColor(CustomColor.primaryColor)
}.frame(maxWidth: metrics.size.width/2.3, maxHeight: .infinity)
.background(RoundedRectangle(cornerRadius: 15)
.fill(Color.white)
.shadow(color: .gray, radius: 2, x: 0, y: 2))
})
}.frame(maxHeight: metrics.size.width/2)
.padding(.horizontal, 15)
.padding(.vertical, 7.5)
}
}
I have tried removing the GeometryReader but I am still getting the same result and I can't seem to find any helpful solution online. Thanks
on the HStack{...}, try replacing .frame(maxHeight: metrics.size.width/2) with .frame(height: metrics.size.width/2)

SwiftUI - Animating HUD / Pop Over like iOS Silent Mode

I'm trying to clone the following animation from iOS:
Here's my code. I'm stuck with the animation of the star. I'd for example like to shake it a little bit or rotate it around it's y-axis.
Here's my code:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
PopOver(image: "star.fill", color: .primary, title: "Thank You!")
}
}
struct PopOver: View{
var image: String
var color: Color
var title: String
#Environment(\.colorScheme) var scheme
var body: some View{
HStack(spacing: 10){
Image(systemName: image)
.font(.title3)
.foregroundColor(color)
Text(title)
.foregroundColor(.primary)
}
.padding(.vertical,10)
.padding(.horizontal)
.background(
scheme == .dark ? Color.black : Color.white
)
.clipShape(Capsule())
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: 1, y: 5)
.shadow(color: Color.primary.opacity(0.03), radius: 5, x: 0, y: -5)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.offset(y: 0)
}
}
Can you tell me how this can be achieved?
Many Thanks!
you could try something like this approach:
struct ContentView: View {
#State var popUpPresented = false
var body: some View {
Button(action: {popUpPresented = true}) {
Text("click me")
}
.popover(isPresented: $popUpPresented, arrowEdge: .top) {
PopOver(image: "star.fill", color: .primary, title: "Thank You!")
}
}
}
struct PopOver: View{
var image: String
var color: Color
var title: String
#State var rotate = 0.0 // <-- here
#Environment(\.colorScheme) var scheme
var body: some View{
HStack(spacing: 10){
Image(systemName: image).font(.title3).foregroundColor(color)
// --- here ---
.rotation3DEffect(Angle.degrees(rotate), axis: (x: 0, y: 1, z: 0))
.task {
withAnimation(Animation.default.speed(0.3).delay(0).repeatForever(autoreverses: false)){
rotate = 360.0
}
}
Text(title).foregroundColor(.primary)
}
.padding(.vertical,10)
.padding(.horizontal)
.background(scheme == .dark ? Color.black : Color.white)
.clipShape(Capsule())
.shadow(color: Color.primary.opacity(0.1), radius: 5, x: 1, y: 5)
.shadow(color: Color.primary.opacity(0.03), radius: 5, x: 0, y: -5)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.offset(y: 0)
}
}

Problem with formatting LazyHGrid - SwiftUI

I am trying to create a basic lazyHGrid but after looking at a couple of different tutorials online I still can't manage to build it properly. I am trying to make a grid that is filled with rectangles with symbols and text inside those rectangles. See image below:
If I make this in a HStack all the rectangles are presented correctly, but as soon as I put it into a LazyHGrid I get this problem. Any ideas?
let rows = [
GridItem(.fixed(200)),
GridItem(.fixed(200)),
]
var product: ProductModel
var body: some View {
LazyHGrid(rows: rows, alignment: .center) {
ForEach(0..<product.imageStack01Image.count, id: \.self) { item in
ZStack(alignment: .top){
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
.frame(width: 200, height: 200)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
VStack(alignment: .leading) {
Image(systemName:product.imageStack01Image[item])
.font(.system(size: 40.0))
.frame(height: 40)
.foregroundColor(.red)
.padding(.top, 40)
.padding(.leading, 10)
Text(product.imageStack01Text[item])
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
}
}
}
}
}
As far as I understood your try to put image and text into rectangle, so try to use overlay instead of ZStack
ForEach(0..<product.imageStack01Image.count, id: \.self) { item in
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
.frame(width: 200, height: 200)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
.overlay(
VStack(alignment: .leading) {
Image(systemName:product.imageStack01Image[item])
.font(.system(size: 40.0))
.frame(height: 40)
.foregroundColor(.red)
.padding(.top, 40)
.padding(.leading, 10)
Text(product.imageStack01Text[item])
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
})
}
}
It might have to do with the hardcoded frame in your square. Try using aspectRatio if you just want a square or frame each layer of the ZStack or frame the ZStack depending on what you want to achieve.
import SwiftUI
struct LazyGridSample: View {
///Sample Products to reproduce
#State var products = ["0xxxxxxxxxx0xxxxxxxxxx","1xxxxxxxxxx0xxxxxxxxxx","2xxxxxxxxxx0xxxxxxxxxx","3xxxxxxxxxx0xxxxxxxxxx","4xxxxxxxxxx0xxxxxxxxxx","5xxxxxxxxxx0xxxxxxxxxx","6xxxxxxxxxx0xxxxxxxxxx","7xxxxxxxxxx0xxxxxxxxxx","8xxxxxxxxxx0xxxxxxxxxx","9xxxxxxxxxx0xxxxxxxxxx"]
let rows = [
GridItem(.fixed(200)),
GridItem(.fixed(200)),
]
let squareSize: CGSize = CGSize(width: 200, height: 200)
var body: some View {
//ScrollView(.horizontal){
LazyHGrid(rows: rows, alignment: .center) {
//HStack{
ForEach(0..<products.count, id: \.self) { idx in
ZStack(alignment: .top){
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill(Color.white)
//.frame(width: squareSize.width, height: squareSize.height)
//.aspectRatio(1, contentMode: .fill)
.shadow(color: Color.black.opacity(0.08), radius: 20, x: 0, y: 5)
VStack(alignment: .leading) {
//Removed Image
Text(products[idx].description)
.font(.title2)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
.padding(10)
}//.frame(width: squareSize.width, height: squareSize.height, alignment: .center)
//.aspectRatio(1, contentMode: .fill)
}//Adjusts the the entire ZStack
//.frame(width: squareSize.width, height: squareSize.height, alignment: .center)
.aspectRatio(1, contentMode: .fill)
}
}
//}//ScrollView
}
}
struct LazyGridSample_Previews: PreviewProvider {
static var previews: some View {
LazyGridSample()
}
}

How to fix gray bar under List in NavigationView in TabView?

So I am having a problem where just below my list I am having a gray bar that appears and when I click on a cell to go to the other view there is an even bigger gray bar. Here is the code for the List View:
VStack{
NavigationView{
VStack{
List{
ForEach(answersArray.indices, id: \.self) { day in
NavigationLink(destination: DetailView(questions: self.answersArray[day].questions, answers: self.answersArray[day].answers, date: self.answersArray[day].dayDone.toString(dateFormat: "MM/dd/yy"))) {
HStack{
VStack{
ForEach(self.answersArray[day].questions.indices) { question in
//Text(question)
Text(self.answersArray[day].questions[question])
.lineLimit(1)
.frame(width: 250, height: 30, alignment: .leading)
// .padding(.vertical, 5)
//.padding(.bottom)
}
}
// .truncationMode(.tail)
//.frame(minWidth: 0, idealWidth: 200, maxWidth: .infinity, minHeight: 0, idealHeight: 50, maxHeight: 75, alignment: .leading)
Text(self.answersArray[day].dayDone.toString(dateFormat: "MM/dd/yy"))
.frame(minWidth: 0, idealWidth: 50, maxWidth: .infinity, minHeight: 0, idealHeight: 20, maxHeight: 20, alignment: .trailing)
}
.padding(.horizontal, 5)
}
//.frame(minWidth: 0, idealWidth: 250, maxWidth: .infinity, minHeight: 0, idealHeight: 50, maxHeight: 100, alignment: .center)
}
//.colorMultiply(Color("Background Green"))
// .listRowBackground(Color("Background Green"))
//.listRowBackground(Color("Background Green"))
Button(action: {
self.answersArray.append(DailyAnswer(questions: ["Question 1", "Question 2"], answers: ["I am happy", "I am sad"], dayDone: Date().addingTimeInterval(100000), lastWeek: false))
self.answersArray.append(DailyAnswer(questions: ["Question 1", "Question 2"], answers: ["I am happy", "I am sad"], dayDone: Date(), lastWeek: false))
self.answersArray.append(DailyAnswer(questions: ["Question 1", "Question 2"], answers: ["I am happy", "I am sad"], dayDone: Date(), lastWeek: false))
}) {
Text("Create cells")
}
}
.navigationBarTitle("Title")
//.colorMultiply(Color("Background Green"))
}
}
.accentColor(Color("My Gray"))
}
And here is the code for the separate view:
import SwiftUI
struct DetailView: View {
var questions : [String];
var answers : [String];
var date : String;
var body: some View {
//Color("Background Green")
//.edgesIgnoringSafeArea(.all)
NavigationView{
ZStack{
Color("Background Green")
.edgesIgnoringSafeArea(.all)
ScrollView{
VStack{
ForEach(questions.indices) { pair in
Text(self.questions[pair])
.font(.title)
.padding()
Text(self.answers[pair])
.padding()
.font(.body)
.frame(minWidth: 0, idealWidth: 250, maxWidth: 350, minHeight: 150, idealHeight: 200, maxHeight: .infinity, alignment: .topLeading)
.background(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.black, lineWidth: 1)
)
}
}
.padding(.top)
.navigationBarTitle("\(date)", displayMode: .inline)
}
}
}
}
}
Also I know this appears very similar to This Question, but when I implemented the solution on that page it would just change the color of the top Navigation Bar Title and not the gray on the bottom.
Also, this is where I am styling both the Tab Bar and the Navigation Bar
init() {
UINavigationBar.appearance().backgroundColor = UIColor(named: "Background Green")
UITabBar.appearance().isTranslucent = false
UITabBar.appearance().barTintColor = UIColor.black
}
I see there are many NavigationView in the stack:
VStack{
NavigationView{ // << here in first snapshot
VStack{
and
NavigationView{ // << here in second snapshot
ZStack{
and as there no complete code provided there are possible others as well...
Here is a thumb-rule: there must be only one root NavigationView in one view hierarchy chain. So make sure you place one NavigationView as tab-item root view of Past Journals tab (again, assumption based only on provided code).
Setting UITabBar.appearance().isTranslucent to false will break constraints that NavigationView is relying on.
To fix this, replace these lines of code:
UITabBar.appearance().isTranslucent = false
UITabBar.appearance().barTintColor = UIColor.black
With these:
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithOpaqueBackground()
tabBarAppearance.backgroundColor = UIColor.black
UITabBar.appearance().standardAppearance = tabBarAppearance

Only Display one view at a time so the user can toggle between the views

Problem:
I've got 3 separate views I'm displaying all at once but I only want one view showing at a time so the user can just toggle between the three views. Here is what is happening now.
The code is long below but if you copy and paste the code into Xcode you can see what is happening now. I have three views that have different size frames. You can click each view button and it will open that view.
import SwiftUI
struct ContentView: View {
#State var show1 = false
#State var show2 = false
#State var show3 = false
var body: some View {
ZStack {
NavigationView {
VStack {
Section {
Color(red: 0.88, green: 0.88, blue: 0.88)
.frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all)
HStack {
Button(action: {
withAnimation(.spring())
{
self.show1.toggle()
}
}) {
Text("View1")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 15))
.foregroundColor(show1 ? .blue : .gray)
}.foregroundColor(show1 ? .blue : .gray)
.padding()
Button(action: {
withAnimation(.spring())
{
self.show2.toggle()
}
}) {
Text("View2")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 15))
.foregroundColor(show2 ? .blue : .gray)
}.foregroundColor(show2 ? .blue : .gray)
.padding()
Button(action: {
withAnimation(.spring())
{
self.show3.toggle()
}
}) {
Text("View3")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 10))
.foregroundColor(show3 ? .blue : .gray)
}.foregroundColor(show3 ? .blue : .gray)
.padding()
}}}
}
VStack (alignment: .center) {
if self.show1 {
ShowView1().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
}
VStack (alignment: .center) {
if self.show2 {
ShowView2().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
}
VStack (alignment: .center) {
if self.show3 {
ShowView3().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
}}}}
struct ShowView1: View {
var body: some View {
ZStack() {
Color.white
.frame(maxWidth: .infinity, maxHeight: 52)
}
}
}
struct ShowView2: View {
var body: some View {
ZStack() {
Color.white
.frame(maxWidth: .infinity, maxHeight: 600)
}
}
}
struct ShowView3: View {
var body: some View {
ZStack() {
Color.white
.frame(maxWidth: .infinity, maxHeight: 300)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You actually not switch but toggle each view, so have as a result coexisting views. Here is corrected part of code (only switching logic). Tested with Xcode 11.2 / iOS 13.2.
struct ContentView: View {
#State var selectedView: Int? = nil
var body: some View {
ZStack {
NavigationView {
VStack {
Section {
Color(red: 0.88, green: 0.88, blue: 0.88)
.frame(maxWidth: .infinity, maxHeight: .infinity).edgesIgnoringSafeArea(.all)
HStack {
Button(action: {
withAnimation(.spring())
{
self.selectedView = 1
}
}) {
Text("View1")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 15))
.foregroundColor(self.selectedView == 1 ? .blue : .gray)
}.foregroundColor(self.selectedView == 1 ? .blue : .gray)
.padding()
Button(action: {
withAnimation(.spring())
{
self.selectedView = 2
}
}) {
Text("View2")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 15))
.foregroundColor(self.selectedView == 2 ? .blue : .gray)
}.foregroundColor(self.selectedView == 2 ? .blue : .gray)
.padding()
Button(action: {
withAnimation(.spring())
{
self.selectedView = 3
}
}) {
Text("View3")
.font(.headline)
.fontWeight(.regular)
.lineLimit(nil)
.padding(EdgeInsets.init(top: -5, leading: 15, bottom: 0, trailing: 10))
.foregroundColor(self.selectedView == 3 ? .blue : .gray)
}.foregroundColor(self.selectedView == 3 ? .blue : .gray)
.padding()
}}}
}
VStack (alignment: .center) {
if self.selectedView == 1 {
ShowView1().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
if self.selectedView == 2 {
ShowView2().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
if self.selectedView == 3 {
ShowView3().transition(
AnyTransition.move(edge: .bottom).combined(with: .move(edge: .bottom)).combined(with: .opacity))
}
}}}}

Resources