How Do i maintain Scrollview subitem height with Swiftui - xcode

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)

Related

How to duplicate the same HStack with the value +=1 within the View pressing button in SwiftUI?

I have exerciseName first and below Set 1
VStack {
Text("\(self.exerciseName)")
.font(.system(size: 27))
.fontWeight(.regular)
.foregroundColor(.white)
.frame(maxWidth: .infinity, minHeight: 60, maxHeight: 60, alignment: .leading)
.padding(.leading, 20)
.background(Color(red: 0, green: 0.32, blue: 0.575, opacity: 1))
}
.cornerRadius(15, corners: .allCorners)
.padding(.horizontal, 10)
HStack(spacing: 30) {
Text("Set \(self.set)")
.font(.system(size: 27))
.fontWeight(.medium)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
TextField("Weight", text: self.$weight)
.keyboardType(.decimalPad)
.multilineTextAlignment(.center)
.frame(minWidth: 90, maxWidth: .infinity, minHeight: 60, maxHeight: 60)
.font(.system(size: 23))
.background()
.cornerRadius(15, corners: .allCorners)
TextField("Reps", text: self.$reps)
.keyboardType(.decimalPad)
.multilineTextAlignment(.center)
.frame(minWidth: 90, maxWidth: .infinity, minHeight: 60, maxHeight: 60)
.font(.system(size: 23))
.background()
.cornerRadius(15, corners: .allCorners)
}
.padding(.horizontal, 10)
and I need pressing button "Add another set" duplicate the same HStack (Set 1) with the new name "Set 2" under Set 1.
HStack(spacing: 30) {
Text("Add another set")
.font(.system(size: 14)).foregroundColor(.white)
Button {
} label: {
Image("plus")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minWidth: 10, maxWidth: 30, minHeight: 10, maxHeight: 30 )
.foregroundColor(.white)
}
}
.padding()
How could I do that?
my project on git:
https://github.com/kulikmark/FitnessPad

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

SwiftUI Scrollview contents are outside scrollable area

I'm having trouble keeping the contents of a ScrollView contained within the scrollview:
Initially, I want to display letters A and B in the ScrollView and have the user scroll to see additional letters. However, even though I've constrained the parent VStack to a frame with height of 120, you can also see the letter C which is outside of the ScrollView (as indicated by the blue background). Here's the code:
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .center , spacing: 5) {
Text("Letter").font(.tableHeader).frame(width: 75, height: 30, alignment: .center)
} // HStack
.frame(width: 195, height: 50, alignment: .center)
.background(Color.green)
VStack(alignment: .leading, spacing: 0) {
GeometryReader { outsideProxy in
ScrollView (.vertical, showsIndicators: false) {
ZStack(alignment: .top) {
GeometryReader { insideProxy in
Color.clear
// get offset
} // GeometryReader inside
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .center, spacing: 5){
Text("A").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("B").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("C").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("D").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
} // VStack
} // ZStack
} // Scrollview
} // GeometryReader outside
.background(Color.blue)
} // VStack
.frame(width: 195, height: 120, alignment: .leading)
} // VStack
}
The full code requires that use of GeometryReader (and consequently, the ZStack) which is why I've left those items in the sample above.
What is the best way to solve this issue? Open to any improvements for coding the above layout. Keep in mind that ultimately, I want to the user to be able to click on A, B, C, or D to be taken to the next view in the navigation stack.
Make it clipped
ScrollView (.vertical, showsIndicators: false) {
ZStack(alignment: .top) {
GeometryReader { insideProxy in
Color.clear
// get offset
} // GeometryReader inside
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .center, spacing: 5){
Text("A").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("B").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("C").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
HStack(alignment: .center, spacing: 5){
Text("D").font(.tableData).frame(width: 75, height: 50, alignment: .center)
}
} // VStack
} // ZStack
} // Scrollview
.clipped() // << here !!

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

How to use if statement with struct in SwiftUI?

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

Resources