Button and List interaction modifications - swiftui

I am trying to remove the "sectionStyle" for my List rows. In order to achieve a tableview-like view, you would write the following:
List(items, id: \.id) { item in
ItemRow(with: item)
}
If you want to interpret tap events on each row, you would wrap the ItemRow into a Button:
List(items, id: \.id) { item in
Button(action: rowTapped) {
ItemRow(item: item)
}
}
The interesting part is that when the List knows about the Button, it will automatically give a selection animation like tableView's selectionStyle. For my application, I'd like to not have that. Is there a way to set the "selectionStyle" for the List to "none"?

There are a number of List behaviours, that I haven't been able to alter. It's quite easy to build your own list though if you have specific needs different than the system List view. Here's a simple example that I've used.
struct MyListView:View {
var body: some View {
ScrollView {
ForEach(items, id: \.id) { item in
VStack {
Button(action: self.rowTapped) {
ItemRow(item: item)
}
Divider()
}
}
}.padding()
}
func rowTapped() {
print("rowTapped")
}
}
struct ItemRow:View {
var item:Item
var body: some View {
HStack {
Text(item.name)
Spacer() // force text to left justify
}
}
}

Related

Picker scroll through one element horizontally

I have a SwiftUI Picker in which an item is selected. The text of one element can be large, so I used UIKit UIPickerView and set the manual height to 100, but at some point it became not enough. Is it possible to make scrolling horizontal for each element?
I want to get something like this:
Picker("Items", select: self._selectItem) {
ForEach(self.items, id: \.self) { item in
ScrollView(.horizontal, showsIndicators: false) {
Text(item.description)
}
.tag(item)
}
}
That should work fine. If you only want to scroll one item, you would have to insert a check of the item length.
let items = [
"A long item text.",
"And a even longer item text which is really going further.",
"Another item text which is really going further."
]
struct ContentView: View {
#State private var select = ""
var body: some View {
VStack {
Text("Make your selection!")
List(items, id: \.self) { item in
ScrollView(.horizontal) {
Text(item)
}
.listRowBackground(item == select ? Color.red : Color.white)
.onTapGesture {
select = item
}
}
}
}
}
I would strongly suggest to separate the picking from the text display and scrolling, e.g. like this:
struct ContentView: View {
#State private var select = items[0]
var body: some View {
VStack {
Text("Make your selection!")
Picker("Items", selection: $select) {
ForEach(items) { item in
Text(item.title)
.tag(item)
}
}
ScrollView {
Text(select.text)
}
.padding()
.frame(height: 200)
}
}
}

SwiftUI Add view below a list

I have a list with some items.
Below the list I'd like to have to button to load more items.
(As loading all items requires some user actions like entering a TAN, this should not be done automatically when the user scrolls to the end of the list, but only if he likes to.)
What I'd like to have is a view like this:
However, if I place the List and the Button in a VStack, the Button get always displayed at the bottom of the screen, not only when I scroll to the end of the List:
struct ContentView: View {
private let items = Range(0...15).map { "Item " + String($0) }
var body: some View {
VStack {
List(items, id: \.self) { item in
Text(item)
}
HStack {
Spacer()
Button("Load more") { print("Load more items") }
Spacer()
}
}
}
}
If I add the Button to the List, the Button obviously gets displayed as a List item with a white background and without any space to the list:
struct ContentView: View {
private let items = Range(0...15).map { "Item " + String($0) }
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
HStack {
Spacer()
Button("Load more") { print("Load more items") }
Spacer()
}
}.listStyle(GroupedListStyle())
}
}
Is there any way to add a view that becomes visible when the user scrolls to the end of the List but that is not part of the List? (Or at least looks like being below the List and not part of it?)
You should use second variant, but a bit tuned, like below (colors/spaces modify per your needs
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
HStack {
Button("Load more") { print("Load more items") }
}
.listRowInsets(EdgeInsets())
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color(UIColor.systemGroupedBackground))
}.listStyle(GroupedListStyle())
}

Is it possible to have a Button(action: -) in a List/ForEach view in SwiftUI

I want to add a Button(action: - view to a List view with a ForEach in it which has a NavigationLink in it. So a user can look at a detail screen of list which has a Button(action: - however if they if user does not need to see details they can select using Button in list.
List {
ForEach(DATA, id: \.self { data in
NavigationLink(destination: DetailView(data: data)) {
HStack {
Text(data.name)
Button(action: {
// add to an array
}) {
Text(data.price)
}
}
}
}
}
but when i click on the button always take to DetailView
Not sure if it possible. Any advice
Here is possible approach
List {
ForEach(DATA, id: \.self { data in
NavigationLink(destination: DetailView(data: data)) {
HStack {
Text(data.name)
Button(action: {
// add to an array
}) {
Text(data.price) // some padding might be useful
.foregroundColor(Color.blue) // << can be any
}.buttonStyle(PlainButtonStyle()) // << here !!
}
}
}
}

SwiftUI List - How to scroll to item

I have list with items. How can i scroll to list 12. I can use geometry reader to calculate offset. But how to scroll to this offset?
List {
ForEach(0..<12) { index in
Text("...")
}
}
Form Xcode 12, You can turn in to a ScrollView and then you can do it by .scrollTo(id):
var body: some View {
ScrollViewReader { scrollProxy in
ScrollView {
ForEach((1...100), id: \.self) { Text("\($0)") }
}
Button("Go!") {
withAnimation { scrollProxy.scrollTo(50) }
}
}
}
Note that ScrollViewReader should support all scrollable content, but now it only supports ScrollView

How to perform animated transitions of a view inside a ScrollView?

I have a ScrollView, which contains an HStack, which contains a ForEach, which contains some custom view with an attached transition. It looks something like:
ScrollView {
HStack {
ForEach(self.objects) { object in
ObjectView(object)
.transition(.slide)
}
}
}
When I update the contents of self.objects (within a call to withAnimation), I want the old/new ObjectViews to animate in and out as specified by the transition.
Unfortunately, no animated transitions appear. If I remove the ScrollView (leaving the HStack to be the root view), the animated transitions work as expected.
Is there a way I can animate the transitions of views inside a ScrollView?
maybe you are interested...this works:
struct ObjectView : View {
var text: String
var body: some View {
Text(text)
}
}
struct ContentView: View {
#State var objects = ["a", "b", "c"]
var body: some View {
Group() {
Button("add") {
withAnimation() {
self.objects.append("f")
}
}
ScrollView {
HStack {
ForEach(objects, id: \.self) { object in
ObjectView(text: object)
.transition(.slide)
}
}.frame(width: 500)
}
}
}
}