Reputation: 195
I've got a couple of simple Swift UI screens all running of a structure. The structure defines a widget's view, its name, and the order it goes in. I'm trying to make a horizontal list where each button, when pressed, centres itself in the scroll view. This is what I'm trying to make:
The probelm that I've having is that I can't get ScrollViewReader to move the scoll view in to the corect position. It think that this has something to do with the ids, but I'm not sure. I'm very new to SwiftUI, and this is proabbly a very basic question, but any help is appreciated!
And here's my structure
struct WidgetView: Identifiable, Hashable{
static func == (lhs: WidgetView, rhs: WidgetView) -> Bool {
return lhs.id == rhs.id && lhs.friendlyName == rhs.friendlyName && lhs.order == rhs.order && lhs.selected == rhs.selected
}
let id = UUID()
var friendlyName: String
var view: Any
var order: Int
var selected: Bool
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
extension WidgetView {
static var data: [WidgetView] {
[
WidgetView(friendlyName: "News", view: MusicView(), order: 1, selected: false),
WidgetView(friendlyName: "Music", view: MusicView(), order: 2, selected: false),
WidgetView(friendlyName: "Ambiance", view: AmbianceView(), order: 3, selected: true),
WidgetView(friendlyName: "Weather", view: WeatherView(), order: 4, selected: false),
WidgetView(friendlyName: "Routines", view: MusicView(), order: 5, selected: false)
]
}
}
And here are my two SwiftUI views, the first one being just the little text box, the second being the one with scroll view thats not working.
struct WidgetName: View {
let widget: WidgetView
var body: some View {
ZStack(alignment: .center) {
Color.clear
Button(action: {
print("Hello")
}){
ZStack{
Text(widget.friendlyName)
.fontWeight(.semibold)
.opacity(0)
.padding(widget.selected ? 15: 12)
.overlay(
RoundedRectangle(cornerRadius: 15)
.fill(Color.white)
.opacity(widget.selected ? 1: 0.5)
)
Text(widget.friendlyName)
.fontWeight(.semibold)
.foregroundColor(.black)
.opacity(widget.selected ? 1: 0.5)
}
}
}.padding()
}
}
struct WidgetName_Previews: PreviewProvider {
static var previews: some View {
WidgetName(widget: WidgetView.data[1])
.previewLayout(.fixed(width: 400, height: 60))
.background(Color.purple)
WidgetName(widget: WidgetView.data[2])
.previewLayout(.fixed(width: 400, height: 60))
.background(Color.purple)
WidgetName(widget: WidgetView.data[3])
.previewLayout(.fixed(width: 400, height: 60))
.background(Color.purple)
}
}
struct WidgetNameStrip: View {
var widgets: [WidgetView]
var body: some View {
VStack{
Spacer()
ScrollView(.horizontal, showsIndicators: false) {
ScrollViewReader { value in
Button("Jump to #32") {
value.scrollTo(3)
}
.foregroundColor(.white)
HStack(spacing: 10) {
ForEach(widgets, id: \.self) { widget in
WidgetName(widget: widget)
}
}
}
}
.frame(height: 80)
}
}
}
struct WidgetNameStrip_Previews: PreviewProvider {
static var previews: some View {
WidgetNameStrip(widgets: WidgetView.data)
.background(Color.purple)
.previewDevice("iPhone 8")
}
}
Upvotes: 3
Views: 2637
Reputation: 162
Building on @Asperi's response, you can also use the anchor parameter on the .scrollTo after you establish the id.
Anchor controls where the selected item is positioned inside of the scroll view once it is scrolled to.
Button("Jump to #32") {
value.scrollTo(id, anchor: .center)
}
You can animate it by using withAnimation
withAnimation { value.scrollTo(3, anchor: .center) }
Upvotes: 1
Reputation: 258541
For your code, as I see, it needs to use order
as id for view, because scrollTo
uses id/s to find a view, ie.:
Button("Jump to #32") {
value.scrollTo(3)
}
.foregroundColor(.white)
HStack(spacing: 10) {
ForEach(widgets, id: \.self) { widget in
WidgetName(widget: widget)
.id(widget.order) // << here !!
}
}
Upvotes: 2