SwiftUI ScrollView horizontal scroll lag on macOS - swiftui

Faced with a very strange ScrollView behavior on macOS. The content freezes under the mouse during horizontal scrolling. But it is worth taking the mouse away from the window and the content scrolls normally.
This happens when I try to use a vertical scroll inside a horizontal one:
struct ScrollTestView: View {
var body: some View {
ScrollView(.horizontal) {
ScrollView(.vertical) {
VStack {
ForEach(0..<20, id: \.self) { row in
HStack {
ForEach(0..<20, id: \.self) { item in
Text("\(item)")
.font(.title)
.padding()
.background {
Color.gray
}
}
}
}
}
}
}
}
}
Yes, I know that I can use the same ScrollView for both axes simultaneously, but I need solution with two ScrollViews because of desired UX.
This solution is perfectly works on iOS, but I have this strange behavior on macOS.
Also if you swap a horizontal and a vertical ScrollView in the exact same code, everything works just fine:
struct ScrollTestView: View {
var body: some View {
ScrollView(.vertical) {
ScrollView(.horizontal) {
// ...
}
}
}
}
Looks like this is a SwiftUI bug, but I am not sure, maybe I am missing something?
Any ideas?

Related

SwiftUI Is there any built in view that kind of slides in from the side and takes up 3/4 of the screen?

This is probably a custom view but in the Reddit app there's a toolbar and the top left button(3 lines) opens this kind of view from the side that moves the current view to the right so you can only see about 25% of it and a new view that takes up about 75% of the screen slides in. Is there anything like this built into SwiftUI and if there isn't how would I go about implementing something like this?
This is my custom side bar behave similarly to what you just mentioned, you can try it. (Images and Code are below)
Before click:
After clicked:
struct ContentView: View {
#State var isClicked = false
var body: some View {
HStack {
Rectangle()
.fill(.orange)
.frame(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0)
VStack {
HStack {
Button {
withAnimation {
isClicked.toggle()
}
} label: {
Image(systemName: "menucard.fill")
.padding(.leading)
}
Spacer()
}
Spacer()
}
}
}
}

Poor on-screen / scrolling performance using LazyVStack and LazyHStack with ScrollView in SwiftUI

I'm trying to draw a large scrolling SwiftUI View consisting of Rectangles that will be colour-coded. Step one is just testing 1,000s of rectangles on screen within a ScrollView, however, performance is awful. This code draws 16,000 rectangles, but the scrolling performance is really jittery and sometimes pauses a few seconds.
Any ideas on what is going on and how to fix appreciated.
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack {
ForEach (0..<32) { byte in
MemoryBlockView()
}
}
}
}
}
struct MemoryBlockView: View {
var body: some View {
LazyVStack(spacing: 2) {
ForEach (0..<8) { byte in
MemoryBlockRowView()
}
}
.padding(0)
}
}
struct MemoryBlockRowView: View {
var body: some View {
LazyHStack(spacing: 2) {
ForEach (0..<64) { byte in
Rectangle()
.fill(Color.blue)
.cornerRadius(2)
.padding(1)
}
}
}
}

How to solve this top bar problem on SwiftUI, elegantly?

I am trying to use the whole iPhone area for my app.
I have this HStack at the top, used to create a custom toolbar.
var body: some View {
VStack (spacing:0) {
MyTopbar()
// other controls
Spacer()
}
.edgesIgnoringSafeArea(.top)
This appears like this on new devices with a notch and old devices without a notch. The notch cuts my menu.
I can solve that by adding a spacer with a frame height before MyTopbar() on the vertical stack but first of all this seems to be a very awful solution. First I have to guess a height for that spacer. Then I have to detect if the device has a notch or not (?).
Is there a better way?
You can think of it as layers (content that respects safe area and content that doesn't).
Something like this perhaps:
struct ContentView: View {
var body: some View {
ZStack {
Color.blue.ignoresSafeArea() // Whatever view fills the whole screen
VStack (spacing:0) {
MyTopbar()
// other controls
Spacer()
}
}
}
}
A possible solution to add clear color with safe area height. No need for much calculation.
var body: some View {
VStack (spacing:0) {
Color.clear.frame(height: Color.clear.frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
MyTopbar()
// other controls
Spacer()
}
.edgesIgnoringSafeArea(.top)

SwiftUI - Catalyst translucent sidebar

Goal is to make a translucent sidebar on Mac Catalyst.
The code bellow gives a not translucent sidebar (image 1).
On Mac (not catalyst) the sidebar looks fine (image 2).
is it possible to have a translucent sidebar on Mac Catalyst?
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
//sidebar
List {
Label("Books", systemImage: "book.closed")
Label("Tutorials", systemImage: "list.bullet.rectangle")
}
.background(Color.clear)
.listStyle(SidebarListStyle())
//content
Text("Sidebar")
.navigationTitle("Sidebar")
}
}
}
You should select "Optimize Interface for Mac" in your target's General settings tab. Then the sidebar will be translucent.
Start with the AppDelegate main and follow Apple's tutorial re: UISplitViewController "Apply a Translucent Background to Your Primary View Controller".
https://developer.apple.com/documentation/uikit/mac_catalyst/optimizing_your_ipad_app_for_mac
In wrapping UISplitViewController in a UIViewControllerRepresentable, I wasn't able to get translucency, but did get full-height sidebar.
I figured out that using .background(Color.clear) on sidebar View makes possible translucent background even if ListStyle is not specified as SidebarListStyle(). Works in Xcode 13.1 for me
struct ContentView: View {
var body: some View {
NavigationView { // without wrapping to NavigationView it won't work
List { // can be VStack or HStack
Text("Hello, world!")
.padding()
}
.listStyle(SidebarListStyle()) // works with other styles
Text("")
}
}
}
struct YourApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.toolbar {
Button {
} label: {
Image(systemName: "gear")
}
}
.background(Color.clear) // 3 <-- MUST HAVE!
}
}
}

Prevent NavigationLink in VStack from being highlighted when tapped

When navigation link is used inside a VStack, it becomes semi-transparent (highlighted) when tapped. Is it possible to prevent such behavior?
NavigationView {
ScrollView {
VStack() {
NavigationLink() {
MyView()
}
}
}
}