Only show horizontal scroll indicator on hover - swift

I'm developing a macOS application. I want to hide the horizontal scroll indicators when the mouse is not hovering over the view.
I got it working with the .onHover modifier. The problem is that the scroll indicator takes up space, so when it appears the view gets bigger and everything is moved below it. Is there a better way to do this, or at least a way to not change the views height when it appears?
Here is some stripped down code:
struct Test: View {
#State private var mouseHover = false
var body: some View {
ScrollView(.horizontal, showsIndicators: mouseHover) {
RowOfItems()
.padding(.bottom)
}
.onHover { hover in
mouseHover = hover
}
}
}

Related

SwiftUI ScrollView does not respond to keyboard

Thanks for taking your time to help others :)
Problem description:
I want to bring up the ScrollView content as keyboard shows up. And I can't.
Despite, if ScrollView is turned upside down... it works!!! But I can't do that because I have to implement .contextMenu(...) and this produces an even worse bug (detailed in this post).
App must support iOS 14.
Simple code demo to show what happens.
import SwiftUI
struct ContentView: View {
#State var text: String = ""
var body: some View {
VStack {
ScrollView() {
LazyVStack {
ForEach(1..<201, id: \.self) { num in
Text("Message \(num)")
}
// .upsideDown() // With these modifiers, will prompt up the scrollView content
}
}
// .upsideDown() // With these modifiers, will prompt up the scrollView content
TextField("Your text here", text: $text)
.textFieldStyle(.roundedBorder)
.padding()
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Example app")
}
}
extension View {
func upsideDown() -> some View {
self.rotationEffect(.degrees(180))
}
}
GIF resources to see behaviour:
Good result (when upside down):
Bad result (on normal scrollView):
What we have checked?
Already tried this solution, but does not help.
Tried setting an offset to ScrollView of keyboards height when it does come up but... nothing happens.
Questions
Why does this happen when it is upside down? (and not at normal scrollview)
Why on both cases it brings up the TextField but does NOT bring up the content of Scrollview if is not upside down???

Vertical ScrollView disabled when cursor is inside horizontal ScrollView in SwiftUI

My SwiftUI app has a List of ScrollView(.horizontal). I would like to vertical scroll to go through each of the horizontal scroll views (Rows) and to horizontal scroll within the Row. The following code accomplishes this :
struct Row: View {
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(0..<100) { i in
Text(String(i))
.padding(5)
}
}
.padding(10)
.background(.red)
}
}
}
struct ContentView: View {
var body: some View {
List(0..<100) {_ in
Row()
}
.background(.blue)
}
}
The issue is, if my cursor is inside the bounds of a Row, the outer vertical scroll is disabled. How can I "promote" the vertical scroll gesture of the nested Row to the List it lives in?
The reason I want this behavior is so that if the user is scrolling through the List, I don't want them to reposition their cursor if it lands inside the bounds of a Row.
Vertical Scroll: GIF
Horizontal Scroll: GIF
Issue
If you exchange the List with another vertical ScrollView it works both ways (don't ask me why).
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
ForEach(0..<100) { row in
Row()
}
}
.background(.blue)
}
}

Button remains tappable after removing from view hierarchy - SwiftUI

I'm trying to show and hide a view based on a certain state. But even after that view is removed from the hierarchy, it still remains tappable for a few moments, leading to phantom button presses. This is occurring only in iOS 16 to my knowledge.
Note that this only occurs when using the .zIndex modifier, which I need in order to transition the view out smoothly. The bug occurs with or without a transition modifier, however.
Minimum working example (tap the show button, then tap the hide button multiple times. If it worked correctly, the hide button handler should only trigger once, since it is removed from the hierarchy. In reality it can be triggered many times)
struct ContentView: View {
#State var show = false
var body: some View {
ZStack {
Button {
print("show")
show = true
} label: {
Text("show")
.foregroundColor(.white)
.padding()
.background(Color.blue.cornerRadius(8))
}
if show {
Button {
// this can be triggered multiple times if you tap fast
print("hide")
show = false
} label: {
Text("hide")
.foregroundColor(.white)
.padding(64)
.background(Color.red.cornerRadius(8))
}
.zIndex(1) // if we remove the zindex, it won't happen. but then we lose the ability to transition this view out.
}
}
}
}
Has anyone else experience this bug? I don't know a workaround besides removing zIndex, is there a way to fix it without losing transitions?
I filed a feedback for this FB11753719

How to disable vertical scroll in TabView with SwiftUI?

I have set up a TabView in my application, so that I can swipe horizontally between multiple pages, but I also have an unwanted vertical scroll that may appear, with a bounce effect so. How can I disable this vertical scroll?
My code:
struct ContentView: View {
#State private var currentTabIndex: Double = 0
var body: some View {
VStack {
TabView(selection: $currentTabIndex) {
Text("Text n°1")
.tag(0)
Text("Text n°2")
.tag(1)
}
.border(Color.black)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
I had this same problem. It's not an exact solution, but you can turn off bouncing on scrollviews (which is used within a TabView). And as long as the items within the TabView are not larger than the TabView frame, it should act as if you disabled vertical scrolling.
I would call it either .onAppear or in your init function:
.onAppear(perform: {
UIScrollView.appearance().bounces = false
})
Note: this disables the bouncing on ALL scrollviews across your app... So you may want to re-enable it .onDisappear.
Still an issue with Xcode 12.4.
I managed to workaround that by wrapping the TabView within a ScrollView and using the alwaysBounceVertical property set to false, as follow:
ScrollView(.horizontal) {
TabView {
///your content goes here
}
.tabViewStyle(PageTabViewStyle())
}
.onAppear(perform: {
UIScrollView.appearance().alwaysBounceVertical = false
})
.onDisappear(perform: {
UIScrollView.appearance().alwaysBounceVertical = true
})
I actually came across this because I saw this effect in a tutorial but couldn’t replicate it on iOS 15.2. However, I managed to replicate it on iOS 14.4 on another simulator side by side. So I guess this behaviour is disabled or fundamentally changed in the newer iOS.
Demonstration

How to disable vertical bounce in SwiftUI on a single ScrollView

I have a half modal view coming from the bottom of the screen with a scrollView, and when there's isn't enough content to scroll I want the drag gesture on the internal scrollView to apply to the modal and expand it or collapse it.
I tried using:
init() {
UIScrollView.appearance().bounces = false
}
And it works fine but this disables the bouncing effect on all the scrollViews in my app.
Is there a way to apply this for a single ScrollView or at least a single View?
you can add this to a ViewModifier:
struct SomeModifier: ViewModifier {
init() {
UIScrollView.appearance().bounces = false
}
func body(content: Content) -> some View {
return content
}
}
ScrollView {
Text("Some scroll view")
}.modifier(SomeModifier()