Scroll conflict when there is ScrollView inside a ScrollView in SwiftUI - swift

I'm trying to reproduce in a way the finder column view, but I'm facing an issue. A scroll conflict occurs when I put a ScrollView or a List inside another ScrollView. The horizontal scroll is lagging when the cursor is over the nested ScrollView.
Here is my simplified code :
struct SplitView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false){
HSplitView{
ScrollView(showsIndicators: false) {
VStack(alignment: .leading) {
ForEach(0..<100) {
Text("Row \($0)")
}
}.frame(minWidth: 200, alignment: .leading)
}
ScrollView {
VStack(alignment: .leading) {
ForEach(0..<100) {
Text("Row \($0)")
}
}.frame(minWidth: 200, alignment: .leading)
}
ScrollView {
VStack(alignment: .leading) {
ForEach(0..<100) {
Text("Row \($0)")
}
}.frame(minWidth: 200, alignment: .leading)
}
}
}
}
}
I have the same issue with nested List.
Do you have any idea how I can prevent that? Or any workaround?

Related

Pin text to bottom of scroll view SwiftUI

How can I pin a view element to the bottom of a scrollview/VStack in SwiftUI? I've tried using spacers but that doesn't seem to be working. I need the 'Footer' text to be at the bottom of the scroll view/Vstack, how can I do that?
struct ContentView: View {
var body: some View {
VStack {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
Text("Top Title")
Text("Body")
Spacer()
Text("Footer") // SHOULD BE PINNED TO BOTTOM OF SCROLL VIEW
.frame(alignment: .bottom)
}
}
.background(Color.red)
Button("Done") {
//some action
}
}
}
}
Here is possible approach:
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
Text("Top Title")
Text("Body")
}
}
.overlay(
Text("Footer")
, alignment: .bottom) // << here !!
Guess 2: probably you wanted this
GeometryReader { gp in
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading) {
Text("Top Title")
Text("Body")
Spacer()
Text("Footer") // SHOULD BE PINNED TO BOTTOM OF SCROLL VIEW
.frame(alignment: .bottom)
}
.frame(maxWidth: .infinity, minHeight: gp.size.height)
}
.background(Color.red)
}
You are properly looking for the safeAreaInset modifier: https://developer.apple.com/documentation/swiftui/menu/safeareainset(edge:alignment:spacing:content:)-1dqob/

Can't show dropdown menu over HStack in SwiftUI

I have a list item that needs to have a dropdown menu that shows on top of the other views in the HStack.
This is what I have currently:
struct ListRow: View {
#Binding var item: Item
#State var showDropdown: Bool = false
var body: some View {
ZStack {
HStack {
VStack {
Text(item.name)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .topLeading)
ProgressView(value: item.percentDone)
}.padding(.all, 10)
Button(action: {
self.showDropdown.toggle()
}, label: {
Image(systemName: "ellipsis.circle")
}).buttonStyle(BorderlessButtonStyle())
.overlay (
VStack {
if self.showDropdown {
Spacer(minLength: 40)
Text("TEST")
.onTapGesture {
print("Delete")
}
}
}.zIndex(10)
)
}
}
}
}
But when I run it and click the button this is what I see
It looks like the Text is not showing due to a view overflow/overlap with the rest of the HStack, but I thought that adding the .zIndex would place the dropdown on top of the rest of the views in the ZStack
Just set frame for your overlay view to make it exceed its main view 's size
.overlay (
VStack {
if self.showDropdown {
Spacer(minLength: 40)
Text("TEST")
.onTapGesture {
print("Delete")
}
}
}.frame(width: 100)
)
Or you can try another approach like ZStack or Menu (as you mentioned a better solution)
I figured out a better solution.
struct ListRow: View {
#Binding var item: Item
var body: some View {
HStack {
VStack {
Text(item.name)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .topLeading)
ProgressView(value: item.percentDone)
}.padding(.all, 10)
Menu {
Button("Delete", action: {
print("Delete")
})
} label: {
}
.menuStyle(BorderlessButtonMenuStyle())
.frame(width: 10, height: 10, alignment: .center)
}
}
}

How to set contentInset of a ScrollView in SwiftUI

I can't seem to find how to set the contentInset of a ScrollView. My goal is to make the last object in my ScrollView above the Purple Main Button.
https://i.stack.imgur.com/QfjZb.jpg
If there is a command, could someone help how to implement this into my current code below. I would appreciate your help!
struct Overview: View {
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<5) {
Text("Item \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(Color("Boxes"))
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
}
Just add a padding to the VStack embedded inside the ScrollView.
Using paddings with the embedded stacks provides the same behaviour as content insets.
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
// Some content //
}
.padding(.bottom, 200) // Choose a value that works for you
}
You could put an invisible view underneath your ScrollView content and give it bottom padding.
For example with Color.clear and a bottom-padding of 300.
struct Overview: View {
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<5) {
Text("Item \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(Color("Boxes"))
.cornerRadius(10)
}
Color.clear.padding(.bottom, 300)
}
.frame(maxWidth: .infinity)
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
}
iOS 15's SwiftUI now has a safeAreaInset modifier.
It's also correctly adjusts scroll bars.
ScrollView(.vertical, showsIndicators: true) {
// scrollview's content
}
.safeAreaInset(edge: .bottom, spacing: 0) {
// some bottom-sticked content, or just –
Spacer()
.frame(height: 44)
}
In iOS 15 I just add negative padding to the VStack.
struct Overview: View {
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<5) {
Text("Item \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(Color("Boxes"))
.cornerRadius(10)
}
.padding(.top, -50)
}
.frame(maxWidth: .infinity)
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
}
This article has a pretty good solution that seems to work on iOS13+ and calculates the correct inset automatically so you don't need to guess. It looks like it also doesn't handle the scroll indicator though.
You just need to add some Stack
struct Overview: View {
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
VStack {}.frame(width: 16, height: 40)
ForEach(0..<5) {
Text("Item \($0)")
}
VStack {}.frame(width: 16, height: 40)
}
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
}

NavigationBarTitle automatic display mode doesn't work

I am unsure how to fix this. I have implemented a ScrollView and a NavigationView with ZStacks. However, the automatic display mode doesn't work and the ScrollView sits behind it, with the Title overlapping.
https://i.stack.imgur.com/KPkGh.png
https://i.stack.imgur.com/H8NwS.png
Here is my current code:
struct Overview: View {
var body: some View {
ZStack{
NavigationView {
ZStack {
Color.init("Background")
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
.edgesIgnoringSafeArea(.top)
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<10) {
Text("Item \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(ColorManager.BoxColor)
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
}
}
}
}
}
}
var body: some View {
NavigationView {
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
ForEach(0..<10) {
Text("Item \($0)")
.foregroundColor(.blue)
.font(.largeTitle)
.frame(width: 340, height: 200)
.background(Color(.gray))
.cornerRadius(10)
}
}
.frame(maxWidth: .infinity)
}
.navigationBarHidden(false)
.navigationBarTitle("Overview", displayMode: .automatic)
}
}
You had the navigation item in a ZStack. A ZStack is used to display overlapping views on top of each other.
I believe what you really were looking to achieve stacking the title above the scrollView. That would be achieved with VStack. That however, would still be wrong.
Because it's a navigational setting, it goes inline with the NavigationView. So it can be displayed in the NavigationBar. It doesn't need to be apart of any Stacks.

Why NavigationLink background is grey?

I'm following SwiftUI tutorial to create my first app with SwiftUI but on TVOs and I don't know why but the scroll view item are with background color in grey, and when they are focussed they become white.
I don't find the way to change the color...
Can you help me please ?
My code :
struct LigneCategorie: View {
var nomCategorie: String
var items: [Recette]
var body: some View {
VStack(alignment: .leading) {
Text(self.nomCategorie)
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top) {
ForEach(self.items) { recette in
NavigationLink(
destination: RecetteDetail(
recette: recette
)
) {
CategoryItem(recette: recette)
}
}
}
}
.frame(height: 185)
}
}
}
struct CategoryItem: View {
var recette: Recette
var body: some View {
VStack(alignment: .leading) {
recette.image
.renderingMode(.original)
.resizable()
.frame(width: 200, height: 150)
.cornerRadius(10)
Text(recette.name)
.foregroundColor(.primary)
.font(.caption)
}
.padding(.leading, 15)
}
}
Thank you
You can set your school view's background in a preview side. when you click your scroll view you would see a chosen scroll view in preview mode Font,color and similar options must to be appear. But if not, then try this it should be useful.
ScrollView(.horizontal, showsIndicators: false) {
}.color(.gray)