ScrollViewReader broke on 2-dimension ScrollView - swiftui

I have a 2d ScrollView (vertical/horizontal) wrapped in a ScrollViewReader and populated by two ForEachs ... straight from the book.
Now the buttons don't scroll to the specified items, but strangely always to double of the row, i.e. 10_10 scrolls to 20_10, 20_20 scrolls to 40_20, 40_40 scrolls out of bounds??
What am I missing?
Is it a macOS 12.2beta issue?
struct ContentView: View {
var body: some View {
ScrollViewReader { scrollProxy in
VStack {
HStack {
Button("10-10") {
withAnimation {
scrollProxy.scrollTo("10-10", anchor: .center)
}
}
Button("20-20") {
withAnimation {
scrollProxy.scrollTo("20-20", anchor: .center)
}
}
Button("40-40") {
withAnimation {
scrollProxy.scrollTo("40-40", anchor: .center)
}
}
}
ScrollView([.vertical, .horizontal]) {
VStack {
ForEach(0..<50) { row in
HStack {
ForEach(0..<50) { col in
Rectangle()
.fill(.gray)
.overlay(
Text("\(row)-\(col)").font(.caption)
)
.frame(width: 50, height: 50)
.id("\(row)-\(col)")
}
}
}
}
}
}
}
}
}

Related

SwiftUI - How to add a pageTabView inside a scrollview with PinnedViews

I had one problem while using SwiftUI. I have implemented a sectionHeader using PinnedView which is currently scrollable all up and down, has a header area, and is LazyVStack. Below is the implementation to show the corresponding content.
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
Section {
Text("Header")
.frame(maxWidth: .infinity, minHeight: 300)
.background(Color.red)
}
Section {
LazyVStack {
ForEach(0...100, id: \.hashValue) { num in
Text("\(num)")
}
}
} header: {
ZStack {
Color.black.ignoresSafeArea()
Text("Section Header")
.frame(maxWidth: .infinity, minHeight: 50)
.background(Color.black)
}
}
}
}
}
}
https://imgur.com/a/xYIFzgy
However, I did not stop here and declared a TabView to enable horizontal paging scrolling in the corresponding content area as shown below, but nothing was displayed in the Contents area.
struct ContentView: View {
#State private var currentIndex = 0
var body: some View {
ScrollView {
LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
Section {
Text("Header")
.frame(maxWidth: .infinity, minHeight: 300)
.background(Color.red)
}
Section {
TabView(selection: $currentIndex) {
LazyVStack {
ForEach(0...100, id: \.hashValue) { num in
Text("A: \(num)")
}
}
.tag(0)
LazyVStack {
ForEach(0...100, id: \.hashValue) { num in
Text("B: \(num)")
}
}
.tag(1)
}
} header: {
ZStack {
Color.black.ignoresSafeArea()
Text("Section Header")
.frame(maxWidth: .infinity, minHeight: 50)
.background(Color.black)
}
}
}
}
}
}
https://imgur.com/a/TvKVoIR
I have found through debugging that if I declare a TabView inside a ScrollView, the Contents area doesn't show anything. Can you give me an idea on how to use the stickyHeader as in the above example to make it horizontally paging?
Thanks
add These Modifiers to the TabView:
TabView {
...
}
.frame(height: UIScreen.main.bounds.height)
.tabViewStyle(.page(indexDisplayMode: .never))

Problem implementing scrollTo in SwiftUI using Slider and Button

I'm trying to reposition a list using scrollTo with a Slider and Button and have 2 problems. The list has Sections with id's and nested Texts below, so the scrolling (by Slider or Button) is to the Section heads only.
Here are the problems:
Slider works fine except that the animation briefly shows the list at the top (you can see the navigationTitle go to full size) before it scrolls to the desired position correctly.
The Button moves the Slider but appears to not find the id's in the Section, so it doesn't reposition to the right Section.
Anyone have any ideas?
struct Floors: Identifiable {
let id: String
let name: String
}
struct UnitLine: Identifiable {
let id=UUID()
let name: String
}
struct UnitKeyboardView: View {
#Environment(\.presentationMode) var presentationMode
#State private var sliderValue = 8.0
var body: some View {
let floors=[Floors(id:"8",name:"8"),Floors(id:"9",name:"9"),Floors(id:"10",name:"10"),Floors(id:"11",name:"11"),Floors(id:"12",name:"12"),Floors(id:"13",name:"13"),Floors(id:"14",name:"14"),Floors(id:"15",name:"15"),Floors(id:"16",name:"16"),Floors(id:"17",name:"17"),Floors(id:"18",name:"18"),Floors(id:"19",name:"19"),Floors(id:"20",name:"20"),Floors(id:"21",name:"21"),Floors(id:"22",name:"22"),Floors(id:"23",name:"23"),Floors(id:"24",name:"24"),Floors(id:"25",name:"25"),Floors(id:"26",name:"26"),Floors(id:"27",name:"27"),Floors(id:"28",name:"28"),Floors(id:"29",name:"29")]
let unitlines=[UnitLine(name:"01"),UnitLine(name:"02"),UnitLine(name:"03"),UnitLine(name:"04"),UnitLine(name:"05"),UnitLine(name:"06"),UnitLine(name:"07"),UnitLine(name:"08")]
NavigationView {
ScrollViewReader {proxy in
VStack {
Spacer()
List {
ForEach(floors) { i in
Section(header: Text("Floor \(i.name)")) {
ForEach(unitlines) { u in
Text(u.name)
.padding(.trailing,200)
.contentShape(Rectangle())
.onTapGesture {
print("tapped")
presentationMode.wrappedValue.dismiss()
}
}
}
}
}
HStack {
Spacer()
Text("Flr: \(Int(sliderValue))")
.font(.title)
.padding(10)
Button {
if sliderValue>8 {
sliderValue=sliderValue-1.0
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
}
else { print("slider below (\(sliderValue))") }
print(proxy)
} label: {
Image(systemName: "minus")
}
.frame(width:30, height:15)
.padding(10)
.background(Color.red)
.clipShape(Capsule())
Slider(value: $sliderValue, in: 8...42, step: 1.0, onEditingChanged: {_ in
withAnimation {
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
print(String(Int(sliderValue)))
}
}
)
.background(Color.cyan)
.border(Color.blue, width: 1)
.padding(10)
Button {
if sliderValue<42 {
print(sliderValue)
sliderValue=sliderValue+1.0
print(sliderValue)
proxy.scrollTo(String(Int(sliderValue)), anchor: .topLeading)
}
} label: {
Image(systemName: "plus")
}
.frame(width:30, height:15)
.padding(10)
.background(Color.green)
.clipShape(Capsule())
Spacer()
}
.background(Color.gray)
}
}
.navigationTitle("Select Address")
}
}
}

How to layout properly in ZStack (I have visibility problem)?

Here is reproducable small code below;
As you'll see when you run the demo code, the Element view does stay under Color.blue when dragged eventhough its above according to ZStack. By the way I also played with zIndex modifier but still no luck. Any solution you offer? Thanks all.
struct ContentView: View {
var body: some View {
GeometryReader { gr in
ZStack {
Color.blue.opacity(0.3)
.aspectRatio(1, contentMode: .fit)
.frame(width: gr.size.width)
VStack {
Spacer()
ScrollView(.horizontal) {
HStack {
ForEach(1...15, id: \.self) { (idx) in
Element(index: idx)
}
}
.padding()
}
.background(Color.secondary.opacity(0.3))
}
}
}
}
}
struct Element: View {
#State private var dragAmount = CGSize.zero
var index: Int
var body: some View {
Rectangle()
.frame(width: 80, height: 80)
.overlay(Text("\(index)").bold().foregroundColor(.white))
.offset(dragAmount)
.gesture(
DragGesture(coordinateSpace: .global)
.onChanged {
self.dragAmount = CGSize(width: $0.translation.width, height: $0.translation.height)
}
.onEnded { _ in
self.dragAmount = .zero
}
)
}
}
iOS 15.5: still valid
How can achieve my goal then, like dragging Element on different view (in this scenario Color.blue)
Actually we need to disable clipping by ScrollView.
Below is possible approach based on helper extensions from my other answers (https://stackoverflow.com/a/63322713/12299030 and https://stackoverflow.com/a/60855853/12299030)
VStack {
Spacer()
ScrollView(.horizontal) {
HStack {
ForEach(1...15, id: \.self) { (idx) in
Element(index: idx)
}
}
.padding()
.background(ScrollViewConfigurator {
$0?.clipsToBounds = false // << here !!
})
}
.background(Color.secondary.opacity(0.3))
}

SwiftUI Popover Size is not expanding to fit content

Here is my code
struct ContentView: View {
#State var showingPopover = false
var body: some View {
VStack {
Spacer()
Text("Hello World")
Spacer()
HStack {
Spacer()
Button {
self.showingPopover.toggle()
} label: {
Image(systemName: "plus.circle")
}
.popover(isPresented: $showingPopover) {
List(0..<100) { Text("\($0)") }
}.padding(30)
}
}
}
}
This should produce a really nice popover coming from the plus button. But all I get is a really squashed down popover.
Any idea what I am missing here? Is there a way to tell the popover to expand more (without specifying a size)?
You may use a ScrollView and ForEach instead of a List:
struct ContentView: View {
#State var showingPopover = false
var body: some View {
VStack {
Spacer()
Text("Hello World")
Spacer()
HStack {
Spacer()
Button(action: {
self.showingPopover.toggle()
}) {
Image(systemName: "plus.circle")
}
.padding(30)
}
}
// can be attached to the button as well (as in the question)
.popover(isPresented: $showingPopover,
attachmentAnchor: .point(.bottomTrailing),
arrowEdge: .bottom) {
ScrollView(.vertical, showsIndicators: false) {
ForEach(0 ..< 100) {
Text("\($0)")
}
}
}
}
}
You can provide a custom frame for the List. Also, don't forget to embed List inside a ScrollView if you want it to scroll.
ScrollView {
List(0..<100) {
Text("\($0)")
}
.frame(width: 100, height: 250)
}

Equal heights of subviews with SwiftUI

Equals heights of Image (square form) and TextOne with paddings. How can I do it without GeometryReader?
I can't use GeometryReader because I use this view as row of List
var body: some View {
return HStack {
VStack {
Image(systemName: "name").resizable().frame(width: ???, height: ???)
Spacer()
}
VStack {
HStack (alignment: .center) {
Text("TextOne").padding()
Spacer()
Text("TextTwo").padding()
}
HStack (alignment: .top) {
Text("TextDownOne").padding()
Spacer()
Button(action: { action() }) {
Text("ButtonText").padding()
}
}
}
}
}