Spacing between sections in the same LazyVGrid? - swiftui

Might be a dumb question and I might have already answered this myself. But, I am trying to put a space between my sections of grids. Currently I am using .padding but this does not seem ideal at all. Or is this really the only way?
var body: some View {
ScrollView(.vertical) {
LazyVGrid(
columns: columns,
alignment: .center,
spacing: 3,
pinnedViews: [.sectionHeaders, .sectionFooters]) {
Section(header:Text("GRID 1").font(.title).bold().padding(.top, 10.0)) {
ForEach(0...9, id: \.self) {
index in Color(UIColor.random)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 75, maxHeight: .infinity)
}
}
Section(header:Text("GRID 2").font(.title).bold().padding(.top, 50.0)) {
ForEach(10...20, id: \.self) {
index in Color(UIColor.random)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 75, maxHeight: .infinity)
}
}
}
}
}

If I understand your question correctly, one way to add vertical space between sections in a grid is to add a spacer with an explicit length (e.g. Spacer(minLength: 45)) inside the grid.
struct ContentView: View {
let gridItemLayout = [GridItem(.adaptive(minimum: 50, maximum: 50))]
var body: some View {
List {
LazyVGrid(columns: gridItemLayout, spacing: 25) {
SectionView()
Spacer(minLength: 45) <-------- VERTICAL SPACE BETWEEN SECTIONS
SectionView()
}
}
}
}
struct SectionView: View {
var body: some View {
Section() {
Color.red.frame(height: 50)
Color.black.frame(height: 50)
Color.green.frame(height: 50)
Color.blue.frame(height: 50)
Color.orange.frame(height: 50)
Color.pink.frame(height: 50)
Color.gray.frame(height: 50)
}
}
}

Related

Full span(width) view in n'th index of LazyVGrid with 2 columns SwiftUI

I have a LazyVGrid with 2 columns in my ContentView.
I want to add full width item on every n'th index of the grid.
In this code snippet I am trying to add full width item only in the 2nd position for testing.
How can I achieve this ?
Thanks in advance
`LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], alignment: .center) {
ForEach(0..<10, id: \.self) { index in
if index == 2 {
Text("Full Width Item")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100)
.background(Color.gray)
.foregroundColor(.white)
.cornerRadius(8)
.padding()
} else {
Text("Regular Item")
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 50)
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(8)
.padding()
}
}
}`
Here's a solution using a GeometryReader to get the width of the LazyVGrid to calculate the cell sizes.
The regular cells are the same as in your sample code, but for the full width cell, I'm using a Color.clear and overlaying it with the large cell, and then using another Color.clear to fill space for the second cell in the row.
struct ContentView: View {
let spacing: CGFloat = 20
let columnCount = 2
var columns: [GridItem] {
Array(repeatElement(GridItem(.flexible()), count: columnCount))
}
var body: some View {
GeometryReader { proxy in
LazyVGrid(columns: columns, alignment: .center) {
ForEach(0..<10, id: \.self) { index in
if index == 2 {
Color.clear
.frame(minHeight: 100)
.overlay(alignment: .leading) {
Text("Full Width Item \(index)")
.frame(maxHeight: .infinity)
.frame(width: proxy.size.width)
.background(Color.gray)
.foregroundColor(.white)
.cornerRadius(8)
}
Color.clear
} else {
Text("Regular Item \(index)")
.frame(width: cellWidth(for: proxy.size))
.frame(minHeight: 100)
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(8)
.padding()
}
}
}
}
.padding()
}
func cellWidth(for size: CGSize) -> CGFloat {
(size.width - ((CGFloat(columnCount) - 1) * spacing)) / CGFloat(columnCount)
}
}

How to detect tap location in SwiftUI?

I want to print the location (x,y coordinates) of the tapped point in the <#code#> block along with the dimensions of my view.
struct ContentView: View {
var body: some View {
self.onTapGesture {
<#code#>
}
}
}
Add DragGesture with minimumDistance: 0 to your body, example:
var body: some View {
VStack
{
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .topLeading
)
.background(Color.red)
.gesture(DragGesture(minimumDistance: 0).onEnded({ (value) in
print(value.location)
}))
}

How to ensure view appears above other views when iterating with ForEach in SwiftUI?

I have a SwiftUI view that is a circular view which when tapped opens up and is supposed to extend over the UI to its right. How can I make sure that it will appear atop the other ui? The other UI elements were created using a ForEach loop. I tried zindex but it doesn't do the trick. What am I missing?
ZStack {
VStack(alignment: .leading) {
Text("ALL WORKSTATIONS")
ZStack {
ChartBackground()
HStack(alignment: .bottom, spacing: 15.0) {
ForEach(Array(zip(1..., dataPoints)), id: \.1.id) { number, point in
VStack(alignment: .center, spacing: 5) {
DataCircle().zIndex(10)
ChartBar(percentage: point.percentage).zIndex(-1)
Text(point.month)
.font(.caption)
}
.frame(width: 25.0, height: 200.0, alignment: .bottom)
.animation(.default)
}
}
.offset(x: 30, y: 20)
}
.frame(width: 500, height: 300, alignment: .center)
}
}
}
}
.zIndex have effect for views within one container. So to solve your case, as I assume expanded DataCircle on click, you need to increase zIndex of entire bar VStack per that click by introducing some kind of handling selection.
Here is simplified replicated demo to show the effect
struct TestBarZIndex: View {
#State private var selection: Int? = nil
var body: some View {
ZStack {
VStack(alignment: .leading) {
Text("ALL WORKSTATIONS")
ZStack {
Rectangle().fill(Color.yellow)//ChartBackground()
HStack(alignment: .bottom, spacing: 15.0) {
ForEach(1...10) { number in
VStack(spacing: 5) {
Spacer()
ZStack() { // DataCircle()
Circle().fill(Color.pink).frame(width: 20, height: 20)
.onTapGesture { self.selection = number }
if number == self.selection {
Text("Top Description").fixedSize()
}
}
Rectangle().fill(Color.green) // ChartBar()
.frame(width: 20, height: CGFloat(Int.random(in: 40...150)))
Text("Jun")
.font(.caption)
}.zIndex(number == self.selection ? 1 : 0) // << here !!
.frame(width: 25.0, height: 200.0, alignment: .bottom)
.animation(.default)
}
}
}
.frame(height: 300)
}
}
}
}

SwiftUI, is there a traitCollectionDidChange-like method in a View?

I'm researching SwiftUI to see if it fits our compony's use case. However, I find it rather bare bones and wonder wether I am missing some key insights.
Currently I am researching if V/HStack's can respond dynamically to Accesability font-size changes.
The issue is that I need to give the items a frame, it does not automatically pin the outer Stacks items-view constraints to their inner content. When the user sets the font-size to full blown Bananas-mode the UI just breaks.
struct PatientHistoryView: View {
#State var model : PatientHistoryModel = historyModel;
var body : some View {
GeometryReader { g in
ScrollView(.vertical, showsIndicators: true) {
List
{
ForEach(self.model.data)
{ labResults in
LabDetailView(labeResults: labResults)
}
}.frame(width: 300, height: g.size.height).background(Color.clear)
}
}
}
}
adding these properties do not seem to affect the Font-scaling issues:
struct PatientHistoryView: View {
#State var model : PatientHistoryModel = historyModel;
var body : some View {
GeometryReader { g in
ScrollView(.vertical, showsIndicators: true) {
List
{
ForEach(self.model.data)
{ labResults in
LabDetailView(labeResults: labResults).frame(minWidth: 200, idealWidth: 400, maxWidth: 600, minHeight: 50, idealHeight: 100, maxHeight: 600)
}
}.frame(width: 300, height: g.size.height).background(Color.clear)
}
}
}
}
Here is the labDetailView
struct LabDetailView: View {
var labeResults : LabResults
var color : Color = .red
var spacer : some View {
Spacer().frame(width: 10, height: 0)
}
var body : some View {
GeometryReader { g in
HStack
{
Group
{
VerticalLineView().frame(width: 10, height: g.size.height, alignment: .leading).background(self.color)
self.spacer
self.spacer
LineStack(lineColor: .white, lineHeight: 2).frame(width: 20, height: 20, alignment: .center)
self.spacer
self.spacer
}
VStack(alignment: .leading, spacing: 8)
{
Text(self.labeResults.title).fontWeight(.bold)
Text(self.labeResults.type)
}
Spacer()
VStack(alignment: .center)
{
Text(self.labeResults.max.asString)
Spacer()
Text(self.labeResults.avg.asString).fontWeight(.bold).font(.system(size: 20))
Spacer()
Text(self.labeResults.min.asString)
}.frame(width: 50, height: g.size.height, alignment: .center)
self.spacer
self.spacer
}.frame(width: g.size.width, height: g.size.height, alignment: .leading)
}
}
}
In UIkit these issues can be resolved in the traitCollectionDidChange hook. However, in Swift UI I just cannot find an equivalent.
Doe anybody have any ideas?
Thanks!

SwiftUI stop Divider from expanding vertically in HStack

I'm using SwiftUI to create something like an alert popup, which I present from UIKit code using UIHostingController. The view looks like this:
VStack(spacing: 0) {
// Some text ...
HStack(spacing:0) {
Button(action: self.onCancel) { Text("Cancel") }
.padding().inExpandingRectangle().fixedSize(horizontal: false, vertical: true)
// This divider is the problem
Divider() // .fixedSize()
Button(action: self.onDelete) { Text("Delete") }
.padding().inExpandingRectangle().fixedSize(horizontal: false, vertical: true)
}
}.frame(minHeight: 0)
The inExpandingRectangle is something I found in another stackoverflow question. It centers the text in each side of the HStack.
extension View {
func inExpandingRectangle() -> some View {
ZStack {
Rectangle().fill(Color.clear)
self
}
}
}
It looks like this. Garbage.
If I put the .fixedSize() on the divider, it does this. Not horrible, but the divider is stupid looking and doesn't expand to the size of the buttons.
Here is a demo of possible simplified alternate, without that artificial extension. Tested with Xcode 11.4 / iOS 13.4.
Divider() // or Rectangle().fill(Color.gray).frame(height: 1)
HStack {
Button(action: {}) { Text("Cancel").fixedSize() }
.padding().frame(maxWidth: .infinity)
Divider() // or Rectangle().fill(Color.gray).frame(width: 1)
Button(action: {}) { Text("Delete").fixedSize() }
.padding().frame(maxWidth: .infinity)
}.fixedSize(horizontal: false, vertical: true)
Note: It worth also to consider custom divider, like
Rectangle().fill(Color.gray).frame(width: 1) // or any other color
than might give much appropriate visual feedback, like
Putting a fixedSize() modifier on the HStack instead of the Divider fixies the problem.
var body : some View {
VStack(spacing: 0) {
// Some text ...
Text("gsfdsfkajflkasdjflkas,jdflaskjf")
HStack(spacing:0) {
Button(action: {print("hi")}) { Text("Cancel") }
.padding().inExpandingRectangle().fixedSize(horizontal: true, vertical: true)
// This divider is the problem
Divider()
Button(action: {print("hello")}) { Text("Delete") }
.padding().inExpandingRectangle().fixedSize(horizontal: true, vertical: true)
}.fixedSize() <------- Insert this
}.frame(minHeight: 0)
}
Result:
You can try below code hope its help you (Xcode 11.2.1)
Note: Change Font, Frame and Color as per your requirement
var body: some View {
VStack(spacing: 0) {
Text("Delete")
.font(Font.system(size: 20, weight: .bold))
Text("Are you sure you want to delete ?")
.font(Font.system(size: 18, weight: .regular))
.padding([.top,.bottom], 15)
Rectangle().fill(Color.gray.opacity(0.5)).frame(height:0.5)
HStack(spacing:0) {
Button(action: {
//Your action
}) {
Text("Cancel")
.foregroundColor(Color.black)
.font(Font.system(size: 18, weight: .medium))
}
.frame(minWidth: 150, maxWidth: .infinity, minHeight: 40, maxHeight: 40, alignment: .center)
Rectangle().fill(Color.gray.opacity(0.5)).frame(width:0.5,height:20)
Button(action: {
//Your action
}) {
Text("Delete")
.font(Font.system(size: 18, weight: .medium))
.foregroundColor(Color.red)
}
.frame(minWidth: 150, maxWidth: .infinity, minHeight: 40, maxHeight: 40, alignment: .center)
}
}
}
Output :