How to create List row seperate one another in SwiftUI - ios

I'm new in SwiftUI
I'm trying to create my section like this, where each row is separated with another. Kinda like section where the background is not connected one another. take a look at the pict: (picture from dribbble)
but mine ended up like this:
(I created the bg blue so that y'all can see the rows clearly)
here's my code:
import SwiftUI
struct ProductPageView: View {
init() {
UITableView.appearance().backgroundColor = .clear // Uses UIColor
}
var body: some View {
NavigationView {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
List {
ForEach(arrayProduct, id: \.name) { dataProduct in
ProductListCell(item: dataProduct)
}
.listRowInsets(EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10))
}
.listStyle(InsetGroupedListStyle())
}
}
.navigationTitle("Produk")
}
}
}
I've used listRowInsets but it's just stretch it.
How do I create that separation between rows?
Any help will be appreciated.
Thank you

I did not attempt a pixel perfect reproduction since I don't have the assets, but the following outlines one way you can accomplish this.
Comments are in the code to explain some of the key pieces:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Color.blue.edgesIgnoringSafeArea(.all)
VStack {
// This handles the display of the
// section header
HStack {
// The text & spacer are grouped
// so that you can pad accordingly
Text("Stuff")
Spacer()
}
.padding(.bottom, 2)
.padding(.leading, 10)
// A VStack contains your list of items
VStack {
ForEach(0...3, id: \.self) { element in
// Each grouping (a product for you)
// will exist within this top level
// HStack
HStack {
// A new HStack (or another view of
// your choice) will contain the views
// that compose your product entry
HStack {
Text("\(element)")
Spacer()
}
.padding() // Provides some buffer around the product
.background(Color.white)
.contentShape(Rectangle()) // For tapping
.cornerRadius(5.0)// Rounding!
}
// Custom padding can tailor your spacing needs
.padding([.top, .bottom], 2)
.padding([.trailing, .leading], 10)
}
}
Spacer()
}
}
.navigationTitle("Produk")
}
}
}

Related

In SwiftUI how can I have a View exceed its parents frame/boundary in a List?

Consider the following code:
import SwiftUI
struct ContentView: View {
private var listContent: [String] = ["This", "is", "a", "placeholder", "list"]
var body: some View {
List {
ForEach(listContent, id: \.self) { entry in
listElement(text: entry)
}
}
}
}
struct listElement: View {
#State var text: String
var body: some View {
Section() {
HStack {
Rectangle()
.fill(.green)
.frame(width: 30, height: nil)
Text(text)
}
.listRowInsets(EdgeInsets())
}
}
}
which generates this
I would like to add additional markers to the left of each List element and/or section, like this (mockup):
I have the data that describes the width of the green bars leading into the list elements on the child and parent element. Coming from web development I expect to do something like a child element having negative margin and is thus exceeding its parents container. However I could not get it to work with SwiftUI's padding. Is there a way to do this?
For example I have tried the following modification to the previously mentioned listElement:
var body: some View {
Section() {
HStack {
Rectangle()
.fill(.red)
.frame(width: 30, height: 10)
.padding(.leading, -20)
.zIndex(9999)
Rectangle()
.fill(.green)
.frame(width: 30, height: nil)
Text(text)
}
.listRowInsets(EdgeInsets())
}
}
which results in:
The red rectangle overflow is hidden. I've tried pushing it to the top with the dreaded zIndex hack but it doesn't work. Even setting the Lists .scrollContentBackground to .hidden and the .background to .clear doesn't show the red Rectangle. I need the overflow to be visible.

Is there a better way to create a multi-column (Data table) List view in SwiftUI

EDIT Below
I'm trying to present a JSON file as a data table, similar to what one would see in a database application. So far my best results have come from an HStack of Lists, however in doing so, each list is scrollable on its own (nor can I imagine any easy way to sort the list(s). Is there a way to create this database view effect using a single List view as opposed to multiple ones?
As an aside, the pod "SwiftDataTables" is exactly what I'm trying to achieve, although that pod seems out of date and not enough documentation for me to get working with my data.
This is a screenshot of what I am currently working with:
And here is the code I'm using:
//
// ContentView.swift
// Shared
//
// Created by Kyle Carroll on 7/19/21.
//
import SwiftUI
import SwiftDataTables
struct ContentView: View {
#StateObject var bookStore : BookStore = BookStore(books:bookData)
var body: some View {
HStack(spacing: 0) {
List {
ForEach (bookStore.books) { book in
ListCell3(book: book)
}
}
.listStyle(PlainListStyle())
.frame(width: 75, height: nil, alignment: .center)
List {
ForEach (bookStore.books) { book in
ListCell(book: book)
}
}
.listStyle(PlainListStyle())
List {
ForEach (bookStore.books) { book in
ListCell2(book: book)
}
}
.listStyle(PlainListStyle())
}
}
}
struct ListCell: View {
var book : Book
var body: some View {
HStack {
Text(book.title)
}
}
}
struct ListCell2: View {
var book : Book
var body: some View {
HStack {
Text(book.author)
}
}
}
struct ListCell3: View {
var book : Book
var body: some View {
HStack {
Text(book.id)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
if #available(iOS 15.0, *) {
ContentView()
.previewInterfaceOrientation(.landscapeLeft)
} else {
// Fallback on earlier versions
}
}
}
Edit:
I've chanced my ListCell struct to list all elements in a single row with dividers between them, however, I can't figure out how to get defined spacing between the elements. A global "spacing" in the HStack causes the elements to be misaligned as the text in each cell is a different length. I'd like certain "columns" to have different fixed widths.
var book : Book
var body: some View {
HStack(alignment: .center, spacing: nil) {
Text(book.id)
Divider()
Text(book.title)
Divider()
Text(book.author)
}
}
}
By changing the contents of the list view cell struct, I was able to put each attribute in a single row and then use modifiers to evenly space them as follows:
struct ListCell: View {
var book : Book
var body: some View {
HStack(alignment: .center, spacing: nil) {
Text(book.id)
.frame(maxWidth: 50, alignment: .leading)
Divider()
Text(book.title)
.frame(maxWidth: 200, alignment: .leading)
Divider()
Text(book.author)
.frame(maxWidth: 200, alignment: .leading)
}
}
}

Adding shapes in List row of SwiftUI

I am trying to create a List View where rows looks like this:
However, I am unable to align the Circle on the leading side. Tried using Spacer(), HStack within VStack, it just doesn't work. Here's my code and its output.
struct PeopleView: View {
let people = ["Adam", "James"]
var body: some View {
NavigationView {
List {
ForEach(people, id: \.self) { person in
HStack {
Circle()
VStack {
Text("\(person)")
}
}
}
}
.navigationBarTitle("People", displayMode: .inline)
}
}
}
Actually you don't need shape itself in this case, but only as a mask to visually present text in circle.
So the solution can be like following
HStack {
Text(person.prefix(2).uppercased()).bold()
.foregroundColor(.white)
.padding()
.background(Color.red)
.mask(Circle()) // << shaping text !!
Spacer()
VStack {
Text("\(person)")
}
}
Some views in SwiftUI fill all available space. Such views are shapes, colors, spacers, dividers, and GeometryReader.
Your Circle is a shape and it behaves similarly like a Spacer (in terms of filling space).
If you replace Circle with an image of a circle it will work:
ForEach(people, id: \.self) { person in
HStack {
Image(systemName: "circle.fill")
.imageScale(.large)
Spacer()
VStack {
Text("\(person)")
}
}
}
That is happening because you did not give a fixed (or relative) frame to the Circle Shape, so the Circle is taking up the maximum available width.
If you add a frame(width:height:), everything should work correctly:
struct PeopleView: View {
let people = ["Adam", "James"]
var body: some View {
NavigationView {
List {
ForEach(people, id: \.self) { person in
HStack {
Circle()
.frame(width: 50, height: 50)
VStack {
Text("\(person)")
}
}
}
}
.navigationBarTitle("People", displayMode: .inline)
}
}
}

SwiftUI HStack fill whole width with equal spacing

I have an HStack:
struct BottomList: View {
var body: some View {
HStack() {
ForEach(navData) { item in
NavItem(image: item.icon, title: item.title)
}
}
}
}
How do I perfectly center its content with equal spacing automatically filling the whole width?
FYI just like Bootstraps CSS class .justify-content-around
The frame layout modifier, with .infinity for the maxWidth parameter can be used to achieve this, without the need for an additional Shape View.
struct ContentView: View {
var data = ["View", "V", "View Long"]
var body: some View {
VStack {
// This will be as small as possible to fit the data
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.border(Color.red)
}
}
// The frame modifier allows the view to expand horizontally
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.frame(maxWidth: .infinity)
.border(Color.red)
}
}
}
}
}
The various *Stack types will try to shrink to the smallest size possible to contain their child views. If the child view has an ideal size, then the *Stack will not expand to fill the screen. This can be overcome by placing each child on top of a clear Rectangle in a ZStack, because a Shape will expand as much as possible. A convenient way to do this is via an extension on View:
extension View {
func inExpandingRectangle() -> some View {
ZStack {
Rectangle()
.fill(Color.clear)
self
}
}
}
You can then call it like this:
struct ContentView: View {
var data = ["View", "View", "View"]
var body: some View {
VStack {
// This will be as small as possible to fit the items
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.border(Color.red)
}
}
// Each item's invisible Rectangle forces it to expand
// The .fixedSize modifier prevents expansion in the vertical direction
HStack {
ForEach(data, id: \.self) { item in
Text(item)
.inExpandingRectangle()
.fixedSize(horizontal: false, vertical: true)
.border(Color.red)
}
}
}
}
}
You can adjust the spacing on the HStack as desired.
I inserted Spacer() after each item...but for the LAST item, do NOT add a Spacer():
struct BottomList: View {
var body: some View {
HStack() {
ForEach(data) { item in
Item(title: item.title)
if item != data.last { // match everything but the last
Spacer()
}
}
}
}
}
Example list that is evenly spaced out even when item widths are different:
(Note: The accepted answers .frame(maxWidth: .infinity) did not work for all cases: it did not work for me when it came to items that have different widths)
If items are fullwidth compatible, it will be done automatically, you can wrap items between spacers to make it happen:
struct Resizable: View {
let text: String
var body: some View {
HStack {
Spacer()
Text(text)
Spacer()
}
}
}
So you. can use it in a loop like:
HStack {
ForEach(data, id: \.self) { item in
Resizable(text: item)
}
}
You can also use spacing in stacks ... ie
HStack(spacing: 30){
Image("NetflixLogo")
.resizable()
.scaledToFit()
.frame(width: 40)
Text("TV Show")
Text("Movies")
Text("My List")
}
.frame(maxWidth: .infinity)
output result looks like this ...
If your array has repeating values, use array.indices to omit a spacer after the last element.
HStack() {
ForEach(data.indices) { i in
Text("\(data[i])")
if i != data.last {
Spacer()
}
}
}

How to implement a horizontal table view?

I'm trying to implement a horizontal table view in swift with the following functionalities:
Images listed in a horizontal manner
Horizontal scrolling left and right
See wireframe attached.
Can someone point me in the right direction? I tried digging into UICollectionView to implement this.
- SwiftUI
This question is asked a lot and there are so many answers out there for using CollectionView. But in SwiftUI you can use a horizontal ScrollView containing a Horizontal Stack like this:
struct CardView: View {
var body: some View {
Rectangle()
.foregroundColor(.gray)
.frame(width: 100, height: 80, alignment: .center)
.cornerRadius(8)
}
}
struct ContentView: View {
var body: some View {
VStack {
HStack {
Text("Categories")
Spacer()
Button(action: {}) { Text("More") }
}.padding(16)
ScrollView(.horizontal) {
HStack(spacing: 16) {
ForEach(0...15, id: \.self) { _ in
CardView()
}
}.padding(16)
}
}
}
}
- Result
Note that you can use this inside a UITableViewCell later this month when SwiftUI officially released by Apple.

Resources