How to disable vertical scroll in TabView with SwiftUI? - swift

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

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???

Only show horizontal scroll indicator on hover

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
}
}
}

How to use Paged TabView inside ScrollView in SwiftUI?

Context
I am having a SwiftUI ScrollView and would like to use Paged TabViews inside to display data. However, this concludes in strange behaviour of the View collapsing to zero height.
Code
struct SomeView: View {
var body: some View {
NavigationView {
ScrollView {
TabView {
Text("View A")
Text("View B")
Text("View C")
}
.tabViewStyle(.page)
}
}
}
}
Questions
How can I achieve my goal of using a Paged TabView inside a larger ScrollView without it collapsing to zero height?
I read in the documentation, that .tabViewStyle(.page) is not available on macOS. Is there any workaround to still use Paged TabViews on macOS?

MacOS Toolbar Space makes Items disappear despite sufficient space

I created a macOS App with a NavigationView and a Toolbar.
Somehow next to my toolbarItem there is a lot of space.. So whenever I change the size of my apps window the toolbarItem disappears. Despite there is still a lot of space for my item.
I did not find out how to reduce that space..
Can you help me?
var body: some View {
NavigationView {
}
.toolbar(content: {
ToolbarItem(placement: .navigation) {
Image(systemName: "gearshape")
.frame(height: 20)
}
})
I found the problem.
The title is existing and just invisible.
You can remove the title by adding .windowToolbarStyle(.unifiedCompact(showsTitle: false))
to the WindowGroup.

Why does SwiftUI View background extend into safe area?

Here's a view that navigates to a 2nd view:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink {
SecondView()
} label: {
Text("Go to 2nd view")
}
}
}
}
struct SecondView: View {
var body: some View {
ZStack {
Color.red
VStack {
Text("This is a test")
.background(.green)
//Spacer() // <--If you add this, it pushes the Text to the top, but its background no longer respects the safe area--why is this?
}
}
}
}
On the 2nd screen, the green background of the Text view only extends to its border, which makes sense because the Text is a pull-in view:
Now, if you add Spacer() after the Text(), the Text view pushes to the top of the VStack, which makes sense, but why does its green background suddenly push into the safe area?
In the Apple documentation here, it says:
By default, SwiftUI sizes and positions views to avoid system defined
safe areas to ensure that system content or the edges of the device
won’t obstruct your views. If your design calls for the background to
extend to the screen edges, use the ignoresSafeArea(_:edges:) modifier
to override the default.
However, in this case, I'm not using ignoresSafeArea, so why is it acting as if I did, rather than perform the default like Apple says, which is to avoid the safe areas?
You think that you use old background modifier version.
But actually, the above code uses a new one, introduced in iOS 15 version of background modifier which by default ignores all safe area edges:
func background<S>(_ style: S, ignoresSafeAreaEdges edges: Edge.Set = .all) -> some View where S : ShapeStyle
To fix the issue just replace .background(.green) with .background(Color.green) if your app deployment target < iOS 15.0, otherwise use a new modifier: .background { Color.green }.