SwiftUI: Align subviews one by one from different stacks - swiftui

I would like to align the views of two HStacks one by one. This means:
The first text of the first stack is aligned with the first text of the second stack.
The second text of the first stack is aligned with the second text of the second stack.
The third text of the first stack is aligned with the third text of the second stack.
I need to have the two HStacks, because I have to apply styling to the them horizontally.
I have tried using layout guides, but it will just align each whole HStack to the layout guide, so it does not work in my case.
Playground screenshot:
Playground code:
import SwiftUI
struct ContentView: View {
let alignmentGuide = HorizontalAlignment(
HorizontalAlignment.CustomAlignment.self
)
var body: some View {
VStack(alignment: self.alignmentGuide, spacing: 20) {
topHstack
.background(Color.gray)
bottomHstack
.background(Color.gray)
}
}
var topHstack: some View {
HStack {
Text("1 top")
.alignmentGuide(self.alignmentGuide) { $0[.leading] }
Text("2 top")
Text("3 top")
}
}
var bottomHstack: some View {
HStack {
Text("1 bottom")
.alignmentGuide(self.alignmentGuide) { $0[.leading] }
Text("2 bottom")
Text("3 bottom")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension HorizontalAlignment {
struct CustomAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[HorizontalAlignment.leading]
}
}
}
Thanks in advance
Edit:
I can use a LazyVGrid to fix the alignment, but then I can not style the rows.
var body: some View {
LazyVGrid(columns: [GridItem(.flexible()),GridItem(.flexible()),GridItem(.flexible())], alignment: .leading) {
Text("1 top")
.alignmentGuide(self.alignmentGuide) { $0[.leading]}
Text("2 top")
Text("3 top")
Text("1 bottom")
.alignmentGuide(self.alignmentGuide) { $0[.leading] }
Text("2 bottom")
.alignmentGuide(.leading) { $0[.leading] + 20 }
Text("3 bottom")
}.frame(maxWidth: .infinity)
}

I have ended up using a grid and a background view to style the first row, since this is all that I needed.
In case one has a dynamic number of rows, calculating the frames for the background view might become trickier, but I have not found another way of doing it.
import SwiftUI
struct ContentView: View {
#State private var heightOverlay = 0.0
var body: some View {
LazyVGrid(columns: [GridItem(.flexible()),GridItem(.flexible()),GridItem(.flexible())], alignment: .leading) {
Group {
Text("1 top")
Text("2 top")
Text("3 top")
}
.padding(.vertical, 10)
.background(GeometryReader { reader in
Color.clear.preference(key: HeightPreferenceKey.self, value: reader.size.height)
})
Text("1 bottom")
Text("2 bottom")
Text("3 bottom")
}.frame(maxWidth: .infinity)
.onPreferenceChange(HeightPreferenceKey.self) { preference in
self.heightOverlay = preference
}
.padding(.horizontal, 20)
.background(Color.red.frame(height: self.heightOverlay), alignment: .top)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct HeightPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}

If it is not oversimplified example of real code in the question, then a possible solution can be quite simple - just simulate some kind of regular grid inside HStack (yes, it is limited, but sometimes be enough, and even correct behaves with .fixedSize)
Tested with Xcode 13.4 / iOS 15.5
extension Text {
func wideLeading() -> some View {
self.frame(maxWidth: .infinity, alignment: .leading)
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
topHstack
.background(Color.gray)
bottomHstack
.background(Color.gray)
}
.padding(.horizontal, 20)
}
var topHstack: some View {
HStack {
Text("1 top").wideLeading()
Text("2 top").wideLeading()
Text("3 top").wideLeading()
}
}
var bottomHstack: some View {
HStack {
Text("1 bottom").wideLeading()
Text("2 bottom").wideLeading()
Text("3 bottom").wideLeading()
}
}
}

Related

SwiftUI: Double picker wheels with system behavioral

I want to recreate system picker behavioral with two options in wheels with SwiftUI and faced ton of problem. Some of this I solved but some still unsolved. I have pop-ups with different views inside. One of the view it's a DatePicker with displayedComponents: .hourAndMinute. And other one is two Pickers inside HStack. My question is how to make Pickers make look like in system: without white spacing between?
struct MultyPicker: View {
#State var value = 1
#State var value2 = 1
var body: some View {
ZStack(alignment: .bottom) {
Color.black.opacity(0.5)
ZStack {
VStack {
Text("Header")
.font(.title3)
.fontWeight(.bold)
HStack(spacing: 0) {
Picker(selection: $value, label: Text("")) {
ForEach(1..<26) { number in
Text("\(number)")
.tag("\(number)")
}
}
.pickerStyle(WheelPickerStyle())
.compositingGroup()
.clipped(antialiased: true)
Picker(selection: $value2, label: Text("")) {
ForEach(25..<76) { number in
Text("\(number)")
.tag("\(number)")
}
}
.pickerStyle(WheelPickerStyle())
.compositingGroup()
.clipped(antialiased: true)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: 34)
.foregroundColor(.white)
)
}
.padding(.horizontal)
.padding(.bottom, 50)
}
.edgesIgnoringSafeArea([.top, .horizontal])
}
}
// This extension for correct touching area
extension UIPickerView {
open override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: super.intrinsicContentSize.height)
}
}
Want to achive looks like that with one grey line in selected value
//
// Test2.swift
// Test
//
// Created by Serdar Onur KARADAĞ on 26.08.2022.
//
import SwiftUI
struct Test2: View {
#State var choice1 = 0
#State var choice2 = 0
var body: some View {
ZStack {
Rectangle()
.fill(.gray.opacity(0.2))
.cornerRadius(30)
.frame(width: 350, height: 400)
Rectangle()
.fill(.white.opacity(1))
.cornerRadius(30)
.frame(width: 300, height: 350)
VStack {
Text("HEADER")
HStack(spacing: 0) {
Picker(selection: $choice1, label: Text("C1")) {
ForEach(0..<10) { n in
Text("\(n)").tag(n)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.clipped()
Picker(selection: $choice2, label: Text("C1")) {
ForEach(0..<10) { n in
Text("\(n)").tag(n)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.clipped()
}
}
}
}
}
struct Test2_Previews: PreviewProvider {
static var previews: some View {
Test2()
}
}
SwiftUI multi-component Picker basically consists of several individual Picker views arranged horizontally. Therefore, we start by creating an ordinary Picker view for our first component. I am using Xcode version 13.4.1(iOS 15.0).
import SwiftUI
struct ContentView: View {
#State var hourSelect = 0
#State var minuteSelect = 0
var hours = [Int](0..<24)
var minutes = [Int](0..<60)
var body: some View {
ZStack {
Color.black
.opacity(0.5)
.ignoresSafeArea()
.preferredColorScheme(.light)
Rectangle()
.fill(.white.opacity(1))
.cornerRadius(30)
.frame(width: 300, height: 350)
VStack {
Text("Header")
HStack(spacing: 0) {
Picker(selection: $hourSelect, label: Text("")) {
ForEach(0..<self.hours.count) { index in
Text("\(self.hours[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.compositingGroup()
.clipped()
Picker(selection: $minuteSelect, label: Text("")) {
ForEach(0..<self.minutes.count) { index in
Text("\(self.minutes[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.compositingGroup()
.clipped()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Output :

How to set the color of unsafe area in a NavigationView

I want the color above the titlebar of NavigationView to remain the same, even when I scroll the list. I've tried using UINavigationBar to set it, but it doesn't work in unsafe area, and tt still turns gray when sliding down.
import SwiftUI
struct ContentView: View {
init(){
UINavigationBar.appearance().backgroundColor = UIColor.blue // it doesn't works in unsafe area
}
var body: some View {
TabView {
NavigationView{
List{
ForEach((1...10).reversed(), id: \.self) {
Text("list \($0)")
}
}.toolbar(content: {
ToolbarItem(placement: .principal, content: {
Text("Title Bar")
.bold()
.frame(maxWidth: .infinity,maxHeight: .infinity, alignment: .center)
.background(Color.red) // it doesn't works
})})
.navigationBarTitleDisplayMode(.inline)
}.tabItem {
Image(systemName: "person")
Text("tag1")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
the preview
I refer to this link https://stackoverflow.com/a/62125578/6845277 to solve this problem.
It feels quite troublesome. I don't know a simple solution yet
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
NavigationView{
List{
ForEach((1...10).reversed(), id: \.self) {
Text("list \($0)")
}
}.toolbar(content: {
ToolbarItem(placement: .principal, content: {
Text("Title Bar")
})})
.navigationBarColor(backgroundColor: .blue, titleColor: .white)
.navigationBarTitleDisplayMode(.inline)
}.tabItem {
Image(systemName: "person")
Text("tag1")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct NavigationBarModifier: ViewModifier {
var backgroundColor: UIColor?
var titleColor: UIColor?
init(backgroundColor: UIColor?, titleColor: UIColor?) {
self.backgroundColor = backgroundColor
let coloredAppearance = UINavigationBarAppearance()
coloredAppearance.configureWithTransparentBackground()
coloredAppearance.backgroundColor = backgroundColor
coloredAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .white]
coloredAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .white]
UINavigationBar.appearance().standardAppearance = coloredAppearance
UINavigationBar.appearance().compactAppearance = coloredAppearance
UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
}
func body(content: Content) -> some View {
ZStack{
content
VStack {
GeometryReader { geometry in
Color(self.backgroundColor ?? .clear)
.frame(height: geometry.safeAreaInsets.top)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
}
}
}
extension View{
func navigationBarColor(backgroundColor: UIColor?, titleColor: UIColor?) -> some View {
self.modifier(NavigationBarModifier(backgroundColor: backgroundColor, titleColor: titleColor))
}
}
preview

SwiftUI Multiple Labels Vertically Aligned

There are a lot of solutions for trying to align multiple images and text in SwiftUI using a HStacks inside of a VStack. Is there any way to do it for multiple Labels? When added in a list, multiple labels automatically align vertically neatly. Is there a simple way to do this for when they are embedded inside of a VStack?
struct ContentView: View {
var body: some View {
// List{
VStack(alignment: .leading){
Label("People", systemImage: "person.3")
Label("Star", systemImage: "star")
Label("This is a plane", systemImage: "airplane")
}
}
}
So, you want this:
We're going to implement a container view called EqualIconWidthDomain so that we can draw the image shown above with this code:
struct ContentView: View {
var body: some View {
EqualIconWidthDomain {
VStack(alignment: .leading) {
Label("People", systemImage: "person.3")
Label("Star", systemImage: "star")
Label("This is a plane", systemImage: "airplane")
}
}
}
}
You can find all the code in this gist.
To solve this problem, we need to measure each icon's width, and apply a frame to each icon, using the maximum of the widths.
SwiftUI provides a system called “preferences” by which a view can pass a value up to its ancestors, and the ancestors can aggregate those values. To use it, we create a type conforming to PreferenceKey, like this:
fileprivate struct IconWidthKey: PreferenceKey {
static var defaultValue: CGFloat? { nil }
static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
switch (value, nextValue()) {
case (nil, let next): value = next
case (_, nil): break
case (.some(let current), .some(let next)): value = max(current, next)
}
}
}
To pass the maximum width back down to the labels, we'll use the “environment” system. For that, we need an EnvironmentKey. In this case, we can use IconWidthKey again. We also need to add a computed property to EnvironmentValues that uses the key type:
extension IconWidthKey: EnvironmentKey { }
extension EnvironmentValues {
fileprivate var iconWidth: CGFloat? {
get { self[IconWidthKey.self] }
set { self[IconWidthKey.self] = newValue }
}
}
Now we need a way to measure an icon's width, store it in the preference, and apply the environment's width to the icon. We'll create a ViewModifier to do those steps:
fileprivate struct IconWidthModifier: ViewModifier {
#Environment(\.iconWidth) var width
func body(content: Content) -> some View {
content
.background(GeometryReader { proxy in
Color.clear
.preference(key: IconWidthKey.self, value: proxy.size.width)
})
.frame(width: width)
}
}
To apply the modifier to the icon of each label, we need a LabelStyle:
struct EqualIconWidthLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.icon.modifier(IconWidthModifier())
configuration.title
}
}
}
Finally, we can write the EqualIconWidthDomain container. It needs to receive the preference value from SwiftUI and put it into the environment of its descendants. It also needs to apply the EqualIconWidthLabelStyle to its descendants.
struct EqualIconWidthDomain<Content: View>: View {
let content: Content
#State var iconWidth: CGFloat? = nil
init(#ViewBuilder _ content: () -> Content) {
self.content = content()
}
var body: some View {
content
.environment(\.iconWidth, iconWidth)
.onPreferenceChange(IconWidthKey.self) { self.iconWidth = $0 }
.labelStyle(EqualIconWidthLabelStyle())
}
}
Note that EqualIconWidthDomain doesn't just have to be a VStack of Labels, and the icons don't have to be SF Symbols images. For example, we can show this:
Notice that one of the label “icons” is an emoji in a Text. All four icons are laid out with the same width (across both columns). Here's the code:
struct FancyView: View {
var body: some View {
EqualIconWidthDomain {
VStack {
Text("Le Menu")
.font(.caption)
Divider()
HStack {
VStack(alignment: .leading) {
Label(
title: { Text("Strawberry") },
icon: { Text("🍓") })
Label("Money", systemImage: "banknote")
}
VStack(alignment: .leading) {
Label("People", systemImage: "person.3")
Label("Star", systemImage: "star")
}
}
}
}
}
}
This has been driving me crazy myself for a while. One of those things where I kept approaching it the same incorrect way - by seeing it as some sort of alignment configuration that was inside the black box that is List.
However it appears that it is much simpler. Within the List, Apple is simply applying a ListStyle - seemingly one that is not public.
I created something that does a pretty decent job like this:
public struct ListLabelStyle: LabelStyle {
#ScaledMetric var padding: CGFloat = 6
public func makeBody(configuration: Configuration) -> some View {
HStack {
Image(systemName: "rectangle")
.hidden()
.padding(padding)
.overlay(
configuration.icon
.foregroundColor(.accentColor)
)
configuration.title
}
}
}
This uses a hidden rectangle SFSymbol to set the base size of the icon. This is not the widest possible icon, however visually it seems to work well. In the sample below, you can see that Apple's own ListStyle assumes that the label icon will not be something significantly larger than the SFSymbol with the font being used.
While the sample here is not pixel perfect with Apple's own List, it's close and with some tweaking, you should be able to achieve what you are after.
By the way, this works with dynamic type as well.
Here is the complete code I used to generate this sample.
public struct ListLabelStyle: LabelStyle {
#ScaledMetric var padding: CGFloat = 6
public func makeBody(configuration: Configuration) -> some View {
HStack {
Image(systemName: "rectangle")
.hidden()
.padding(padding)
.overlay(
configuration.icon
.foregroundColor(.accentColor)
)
configuration.title
}
}
}
struct ContentView: View {
#ScaledMetric var rowHeightPadding: CGFloat = 6
var body: some View {
VStack {
Text("Lazy VStack Plain").font(.title2)
LazyVStack(alignment: .leading) {
ListItem.all
}
Text("Lazy VStack with LabelStyle").font(.title2)
LazyVStack(alignment: .leading, spacing: 0) {
vStackContent
}
.labelStyle(ListLabelStyle())
Text("Built in List").font(.title2)
List {
ListItem.all
labelWithHugeIcon
labelWithCircle
}
.listStyle(PlainListStyle())
}
}
// MARK: List Content
#ViewBuilder
var vStackContent: some View {
ForEach(ListItem.allCases, id: \.rawValue) { item in
vStackRow {
item.label
}
}
vStackRow { labelWithHugeIcon }
vStackRow { labelWithCircle }
}
func vStackRow<Content>(#ViewBuilder _ content: () -> Content) -> some View where Content : View {
VStack(alignment: .leading, spacing: 0) {
content()
.padding(.vertical, rowHeightPadding)
Divider()
}
.padding(.leading)
}
// MARK: List Content
var labelWithHugeIcon: some View {
Label {
Text("This is HUGE")
} icon: {
HStack {
Image(systemName: "person.3")
Image(systemName: "arrow.forward")
}
}
}
var labelWithCircle: some View {
Label {
Text("Circle")
} icon: {
Circle()
}
}
enum ListItem: String, CaseIterable {
case airplane
case people = "person.3"
case rectangle
case chevron = "chevron.compact.right"
var label: some View {
Label(self.rawValue, systemImage: self.rawValue)
}
static var all: some View {
ForEach(Self.allCases, id: \.rawValue) { item in
item.label
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
// .environment(\.sizeCategory, .extraExtraLarge)
}
}
Combining a few of these answers into another simple option (Very similar to some of the other options but thought it was distinct enough that some may find it useful). This has the simplicity of just setting a frame on the icon, and the swiftUI-ness of using LabelStyle but still adapts to dynamic type!
struct StandardizedIconWidthLabelStyle: LabelStyle {
#ScaledMetric private var size: CGFloat = 25
func makeBody(configuration: Configuration) -> some View {
Label {
configuration.title
} icon: {
configuration.icon
.frame(width: size, height: size)
}
}
}
The problem is that the system icons have different standard widths. It's probably easiest to use an HStack as you mentioned. However, if you use the full Label completion, you'll see that the Title is actually just a Text and the icon is just an Image... and you can then add custom modifiers, such as a specific frame for the image width. Personally, I'd rather just use an HStack anyway.
var body: some View {
VStack(alignment: .leading){
Label(
title: {
Text("People")
},
icon: {
Image(systemName: "person.3")
.frame(width: 30)
})
Label(
title: {
Text("Star")
},
icon: {
Image(systemName: "star")
.frame(width: 30)
})
Label(
title: {
Text("This is a plane")
},
icon: {
Image(systemName: "airplane")
.frame(width: 30)
})
}
}

Using SwiftUI. My Slider/Side-menu launches new Views just fine when clicked but click <back> button and now all the options are 'dead'

Using SwiftUI and a slider/side menu tutorial that I have augmented in order to put actions on each of the side menu selections.
When the side menu is displayed and I tap a menu option, it works great and takes me to a new view with a menu item. But when i tap on and see the side menu still in place, all the menu items are not dead. The menu items still animate a click (with a flicker) but nothing happens. I have to close the side menu, reopen it, and then the menu items work once again - one time.
Can anyone tell me why this is happening?
Here is the pretty contentview, the mainview, and the sidemenu view.
//ContentView.swift
import SwiftUI
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView()
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag)
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here is the sidemenu view.
//MenuView.swift
import SwiftUI
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#State var showMenu = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}
struct MenuView_Previews: PreviewProvider {
static var previews: some View {
MenuView()
}
}
enter code here
1) Binding var in the MenuView
2) OnAppear{} with Zstack to turn off the showMenu
struct ContentView: View {
#State var showMenu = false
var body: some View {
let drag = DragGesture()
.onEnded {
if $0.translation.width < -100 {
withAnimation {
self.showMenu = false
}
}
}
return NavigationView {
GeometryReader { geometry in
ZStack(alignment: .leading) {
MainView(showMenu: self.$showMenu)
.frame(width: geometry.size.width, height: geometry.size.height)
.offset(x: self.showMenu ? geometry.size.width/2 : 0)
.disabled(self.showMenu ? true : false)
if self.showMenu {
MenuView(showMenu: self.$showMenu)
.frame(width: geometry.size.width/2)
.transition(.move(edge: .leading))
}
}
.gesture(drag).onAppear {
self.showMenu = false
}
}
.navigationBarTitle("Side Menu", displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
withAnimation {
self.showMenu.toggle()
}
}) {
Image(systemName: "line.horizontal.3")
.imageScale(.large)
}
))
}
}
}
struct MainView: View {
#Binding var showMenu: Bool
var body: some View {
Button(action: {
withAnimation {
self.showMenu = true
}
}) {
Text("Show Menu")
}
}
}
struct PlayerView: View {
#State var showMenu = true
//#EnvironmentObject var session: SessionStore
var body: some View {
VStack{
//self.showMenu = true
Text("Manage Players Here").foregroundColor(.red)
}
}
}
struct MenuView: View {
#Binding var showMenu: Bool // = true
var body: some View {
VStack(alignment: .leading) {
HStack() {
NavigationLink(destination: PlayerView()) {
HStack(){
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Players")
.foregroundColor(.gray)
.font(.headline)
}
}
}
.padding(.top, 100)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(red: 32/255, green: 32/255, blue: 32/255))
.edgesIgnoringSafeArea(.all)
}
}

How to remove List selection indicator and separator in SwiftUI?

I code a List in a ScrollView, when I selected a List cell to translate to another view and return back, the cell selected indicator did not disappear after selecting.
I hope after selected the list cell, the selected indicator should be disappear.
I debugged, I found that the ScrollView has some problems when it worked with List.If no ScrollView, the list selection behavior is all right, if plus the ScrollView outside the list, the problem become.
The other problem is How to remove the List Separator.
Thank you for your help!!!
#State var valueData: [String] = ["Apple", "Pear", "Orange", "Cake"]
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack(spacing: 10) {
DietListView(valueData: self.$valueData)
DietListView(valueData: self.$valueData)
.padding()
}
}
.frame(width: 352)
}
}
struct DietListView: View {
#Binding var valueData: [String]
var body: some View {
VStack {
List {
ForEach(self.valueData, id: \.self) { item in
NavigationLink(destination: DietItemDetailView()) {
HStack {
Text(item)
Spacer()
Text("100")
}
}
}
.onDelete { index in
self.valueData.remove(at: index.first!)
}
}
.frame(height: 300)
}
.frame(width: 352, height: 350)
.background(Color.white)
.cornerRadius(16)
.shadow(radius: 10)
}
}
the problem just like this:
At the moment (SwiftUI Beta 5) you can't customise List very much changing, for example, the divider style. What you can do, depending on your needs, is to use a ScrollView with ForEach and give the cell the style you want. For example:
struct ContentView: View {
#State var valueData: [String] = ["Apple", "Pear", "Orange", "Cake"]
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack(spacing: 10) {
DietListView(valueData: self.$valueData)
DietListView(valueData: self.$valueData)
.padding()
}
}
.frame(width: 352)
}
}
}
struct DietListView: View {
#Binding var valueData: [String]
var body: some View {
VStack {
ScrollView {
ForEach(self.valueData, id: \.self) { item in
NavigationLink(destination: Text("ciao")) {
HStack {
Text(item)
Spacer()
Text("100")
}
.foregroundColor(.primary)
.padding(10)
}
}
}
.frame(height: 300)
}
.frame(width: 352, height: 350)
.background(Color.white)
.cornerRadius(16)
.shadow(radius: 10)
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Also, take a look at this: https://stackoverflow.com/a/56498261/1291872
EDIT: you can use onDelete, onMove and onInsert only within a List. If you want to let users delete a row in a ScrollView you must implement something yourself. Take a look at the code below for a simple (pretty ugly) example:
struct ContentView: View {
#State var valueData: [String] = ["Apple", "Pear", "Orange", "Cake"]
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack(spacing: 10) {
DietListView(valueData: self.$valueData)
DietListView(valueData: self.$valueData)
.padding()
}
}
.frame(width: 352)
}
}
}
struct DietListView: View {
#Binding var valueData: [String]
var body: some View {
VStack {
ScrollView {
ForEach(self.valueData.indices, id: \.self) { idx in
NavigationLink(destination: Text("ciao")) {
HStack {
Text(self.valueData[idx])
Spacer()
Text("100")
.padding(.trailing, 20)
Button(action: {
self.valueData.remove(at: idx)
}) {
Image(systemName: "xmark.circle")
.resizable()
.frame(width: 25, height: 25)
.foregroundColor(Color.red)
}
}
.foregroundColor(.primary)
.padding([.leading, .trailing], 20)
.padding([.top, .bottom], 10)
}
}
}
.frame(height: 300)
}
.frame(width: 352, height: 350)
.background(Color.white)
.cornerRadius(16)
.shadow(radius: 10)
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif