SwiftUI .shadow blocks List scroll gesture for all but first List {}. Why? - list

if I place 2 or more Lists with items in VStack or HStack and give the Vstack .shadow attribute, only the first List is scrollable and receives "gesture" events. Anybody has an idea why and is this intended behaviour? Seems like a bug to me. Tried on Xcode and device with iOS 14 and 14.+
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ListView1()
ListView2()
}
.shadow(radius: 5)
}
}
struct ListView1:View {
var values=["1","2","3"]
var body: some View {
List {
ForEach(values, id: \.self) { (value) in
Text("Value \(value)")
}
}
}
}
struct ListView2:View {
var values=["5","6","7"]
var body: some View {
List {
ForEach(values, id: \.self) { (value) in
Text("Value \(value)")
}
}
}
}

Use compositingGroup() for solving issue!
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ListView1()
ListView2()
}
.compositingGroup() // <<: Here!
.shadow(radius: 5)
}
}

Related

Overlay views one by one in SwiftUI

I have the following code with a struct and two views. On tap of the firstScreenOverlay button i want to show the secondScreenOverlay and hide the previous one and so on. Any help appreciated!
import SwiftUI
struct ContentView: View {
var body: some View {
Text("hello there")
.overlay(firstScreenOverlay, alignment: .center)
}
}
private var firstScreenOverlay: some View {
ZStack {
Color.blue
.opacity(0.5)
Button {} label: {
Text("Next")
.fullWidth()
}
}
}
private var secondScreenOverlay: some View {
ZStack {
Color.red
.opacity(0.5)
}
}
You just need a way to keep track of what is showing. You can use a variable and an enum
import SwiftUI
struct DynamicOverlay: View {
//Keeps track of what is showing
#State var selectedOverlay: OverlayViews = .none
var body: some View {
VStack{
Text("hello there")
//Button changes what is being displayed
Button("first", action: {
selectedOverlay = .first
})
}
//Displays the selected view
.overlay(selectedOverlay.view($selectedOverlay), alignment: .center)
}
}
enum OverlayViews{
case first
case second
case none
//Holds all the options for the views
#ViewBuilder func view(_ selectedView: Binding<OverlayViews>) -> some View{
switch self{
case .first:
ZStack {
Color.blue
.opacity(0.5)
Button {
selectedView.wrappedValue = .second
} label: {
Text("Next")
}
}
case .second:
ZStack {
Color.red
.opacity(0.5)
Button("home") {
selectedView.wrappedValue = .none
}
}
case .none:
EmptyView()
}
}
}
struct DynamicOverlay_Previews: PreviewProvider {
static var previews: some View {
DynamicOverlay()
}
}

SwiftUI list does not scroll using 2-finger swipe on trackpad

I have been testing this simple SwiftUI code: It has a list in ContentView. When a cell of the list is tapped, it pushes a SecondList into the navigation stack. When this SwiftUI code was run on a real iPad with a trackpad, I used 2-finger swipe to scroll the list up and down. The list in ContentView scrolls smoothly. However, for SecondList, it may freeze randomly and thereafter not responding.
It also failed in a UIKit project, where I replaced a UINavigationView with this ContentView using UIHostingViewController.
It was testing it with iPadOS 14.5 on both Simulator and iPad Pro hardware with Magic Keyboard.
How do I fix this? Thank you.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0..<30, id: \.self) { index in
NavigationLink(
destination: SecondList(),
label: {
Text(String(index))
})
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct SecondList: View {
var body: some View {
List {
ForEach(0..<30, id: \.self) { index in
Text(String(index))
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Use a scrollView
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0..<30, id: \.self) { index in
NavigationLink(
destination: SecondList(),
label: {
Text(String(index))
})
}
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct SecondList: View {
var body: some View {
scrollView {
List {
ForEach(0..<30, id: \.self) { index in
Text(String(index))
}
}
}
}
}

Corrupted Navigation Views

I'm pretty sure this is a bug in SwiftUI, but I wondered if anyone has encountered it and figured out a workaround. My normal use case is to have a search field appear, but I've simplified it to the point where a simple text string exhibits the bug.
Create a single-view app, copy this into ContentView, and run it. Tap the search icon twice, then scroll the view; you'll see the text scrolling UNDER the title.
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
}
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Maybe it is a bug, submit feedback to Apple, but currently this is how NavigationView behaves - it collapses navigation bar only if its top content is List/ScrollView/Form. So to solve the issue move your VStack either into a List or out of NavigationView
1)
var body: some View {
NavigationView {
List {
if condition {
Text("Peekaboo")
}
ForEach(items, id: \.self) {item in
2)
var body: some View {
VStack {
if condition {
Text("Peekaboo")
}
NavigationView {
List {
It seems that a View cannot cope with variable number of views.
A workaround this strange behavior is this:
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
} else {
Text("")
}
// or use this Text(condition ? "Peekaboo" : "")
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Let me know if it works, if not let us know what device/system you are using. Tested with Xcode 11.6 beta, Mac 10.15.5, target ios 13.5 and mac catalyst.

SwiftUI How to stop the animation of List in Multi-level NavigationView

I just want to stop the animation when I have a multi List in multi-level NavigationView. Maybe this is not "ANIMATION", I just want to fix that.
On Xcode Version 11.3.1 (11C504) + iOS 13.2
The code is simple and you can find out it's wired.
import SwiftUI
struct TestView: View {
var body: some View {
NavigationView {
List {
ForEach(1...4, id: \.self) {_ in
NavigationLink(destination: AView()) {
Text("root")
}
}
}
}
}
}
struct AView: View {
var body: some View {
List {
ForEach(1...4, id: \.self) {_ in
NavigationLink(destination: BView()) {
Text("aview")
}
}
}
}
}
struct BView: View {
var body: some View {
List {
ForEach(1...4, id: \.self) {_ in
NavigationLink(destination: BView()) {
Text("bview")
}
}
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
Ok... I've installed this bad-luck Xcode 11.3.1... and here is a solution (or workaround, anyway) - use explicit .listRowInsets as in below example (btw, insets can be any value)
List {
ForEach(1...1000, id: \.self) {_ in
NavigationLink(destination: BView()) {
Text("bview")
}
}
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20))
}
Works for any dynamic List

Multi Level Navigation SwiftUI

I have a master detail application I'm working on in SwiftUI but once on the 1st DetailView, NavigationLink in the NavBar no longer works. I wrote this as a simple demonstration:
struct NavView: View {
var body: some View {
NavigationView {
NavigationLink(destination: Layer()) {Text("Go to Layer 1")}
}
}
}
struct Layer: View {
var body: some View {
Text("Welcome to Layer 1")
.navigationBarItems(trailing: NavigationLink(destination: AnotherLayer()) { Text("Go to Layer 2") })
}
}
struct AnotherLayer: View {
var body: some View {
Text("Welcome to Layer 2")
}
}
Everything renders and you can tap the navigationBarItem in Layer but nothing happens.
What's going on here? How can I access AnotherLayer?
A NavigationLink used as a navigationBarItem will not work no matter if you use it in the first or second level but a NavigationDestinationLink would solve the problem.
import SwiftUI
struct TestSwift: View {
var body: some View {
NavigationView {
NavigationLink(destination: Layer()) {Text("Go to Level")}
}
}
}
struct Layer: View {
let detailView = NavigationDestinationLink(AnotherLayer())
var body: some View {
VStack {
Text("Text")
}
.navigationBarItems(trailing:
Button(action: {
self.detailView.presented?.value = true
}, label: {
Text("Present Now!")
})
)
}
}
struct AnotherLayer: View {
var body: some View {
Text("Hello")
}
}