SwiftUI problems: Decreasing the clickable area of buttons inside the scrollview if the view is adjacent to the list - list

I have a scrollable panel with buttons above the list. And in this layout the lower part of buttons is unclicable.
import SwiftUI
struct TestListScreen: View {
var body: some View {
NavigationView {
VStack(spacing: 0) {
ScrollView(.horizontal, showsIndicators: true) {
HStack(spacing: 8) {
ForEach(1..<10) { i in
Button { print(i) } label: {
Text("Button \(i)")
.padding(24)
.background(Color.yellow)
}
}
}
}
List {
ForEach(1..<100) { i in
Text(String(i))
}
}
.listStyle(.plain)
.listRowInsets(EdgeInsets())
.frame(maxHeight: .infinity)
}
.navigationBarTitle("Buttons above the list", displayMode: .inline)
}
}
}
struct TestListScreen_Previews: PreviewProvider {
static var previews: some View {
TestListScreen()
}
}
If I remove ScrollView wrapped buttons HStack, or if I remove the list under buttons, everything works well. Also I cannot replace list with a ScrollView + VStack because of poor performance.

So... the "problem" is that you have a scrolling view of button. That 16 pixels is to show the scrollbar when you drag the view.
Easy fix is to leave space for it by adding padding.
ScrollView(.horizontal, showsIndicators: true) {
HStack(spacing: 8) {
ForEach(1..<10) { i in
Button { print(i) } label: {
Text("Button \(i)")
.padding(24)
.background(Color.yellow)
}
}
}
.padding(.bottom, 16)
note: while adding the space does make the whole button clickable, if you just finished dragging the buttons, the 16 pixels is increased for a couple seconds after you finish dragging. During that time, part of the button sill won't be clickable, instead it makes the scrollbar tab get bigger.

Related

TapGesture and Button is not working in list if adding simultaneousGesture with minimumDistance: 0.0 - SwiftUI

I need to use a List for reusability that it provided and disable the scroll based on adding simultaneousGesture with DragGesture(minimumDistance: 0.0) to support iOS 15. scrollDisabled is only for iOS 16.
So when adding the gesture to the list onTapGesture and Button is not working inside each row of my list.
Here is a sample code:
ZStack {
List {
ForEach(0..<10, id:\.self) { index in
VStack {
Text("Sample text \(index)")
.onTapGesture {
print("Sample Text tapped \(index)")
}
Button("Sample button \(index)") {
print("Sample button tapped \(index)")
}
.buttonStyle(.bordered)
.frame(height: 50)
}
.frame(height: 100)
}
}
.simultaneousGesture(
DragGesture(minimumDistance: 0.0)
.onChanged({ _ in
print("Drag onChanged")
})
.onEnded({ _ in
print("Drag onEnded")
})
)
}
if we set the minimumDistance to 0.1 tapGesture and button will work, but the scroll is not disabled anymore.
Do you have any idea or workaround?
Thanks.
I'm not sure if I understood your problem correctly. I guess what you want to do is to keep onTapGesture, Button and List's scroll functionalities all them working, except when a drag event occurs.
If you place the simultaneousGesture in the ForEach instead of assigning it to the List, then they all work, except when you drag over the Text or Button elements. In this case, the scroll does not work.
struct ContentView: View {
var body: some View {
ZStack {
List {
ForEach(0..<10, id:\.self) { index in
VStack {
Text("Sample text \(index)")
.onTapGesture {
print("Sample Text tapped \(index)")
}
Button("Sample button \(index)") {
print("Sample button tapped \(index)")
}
.buttonStyle(.bordered)
.frame(height: 50)
}
.frame(height: 100)
}
.simultaneousGesture(
DragGesture(minimumDistance: 0.0)
.onChanged { _ in
print("Drag onChanged")
}
.onEnded { _ in
print("Drag onEnded")
}
)
}
}
}
}

How can implement a navigationLink inside of a LazyVGrid and ForEach statement?

How can I implement play buttons that navigate to different views in the corner of each music category item? Here is an image of what I am looking for:
Here is my code:
struct ScrollCategories: View {
var body: some View {
ZStack {
ScrollView {
LazyVGrid (columns: [GridItem(.fixed(200)), GridItem(.fixed(200))]) {
ForEach(musics) { sub in
ZStack {
VStack(alignment: .leading) {
//Hidden Code
} // END OF VSTACK
NavigationLink {
// Naviagte to a different view!
Onboarding()
} label: {
Image(systemName: "play.circle")
.font(.system(size: 30))
.padding(.leading, -72.0)
.padding(.bottom, 130.0)
}
} // END OF ZSTACK
}// END OF FOR EACH
} // END OF GRID ITEM
} // END OF SCROLL VIEW
} //END OF ZSTACK
}
}
As you can see, I have a navigation link, yet it does not show in the preview or simulator.
Here is an image of what the preview looks like:
Your navigation link is there. You just use padding too high that it was out of the frame. You can use ZStack with alignment to put on the left-top corner and add padding to make it nice.
ZStack(alignment: .topLeading) { <- positioning for each View
VStack(alignment: .leading) {
//Hidden Code
} // END OF VSTACK// END OF VSTACk
NavigationLink {
// Navigate to a different view!
Onboarding()
} label: {
Image(systemName: "play.circle")
.font(.system(size: 30))
.padding( {{ put your desire padding }})
}
} // END OF ZSTACK

Button Text Out-Of-Sync with Tappable Area

I have a navigation view with tab views inside. The reason for this arrangement is to make the tab views disappear when within the navigation views.
I am trying to place a button at the top right corner of the screen (outside the safe area). If EntryView is called directly, the view correctly places the button ("Save") in the corner without use of offset() or position() view modifiers.
When the tab view is called before EntryView, the Save button appears an inch or so below where I would like it to appear. With offset() or position() I can move the button text back where I would like it to appear, but the tappable area doesn't move to the new location.
I have tried different arrangements of ZStack and VStack, but the arrangement below is the closest I have come to getting the button to work in the upper right corner.
Here is where I would like the button to appear: https://i.stack.imgur.com/AMdKQ.png
Is there any way to move the tappable area up to where the text is located?
Or is there some better way to draw the top part of the view?
This code can be dropped directly into Xcode for analysis.
struct ContentView: View {
#State private var selectedTab: Tabs = .home
var body: some View {
NavigationView {
TabView (selection: $selectedTab) {
EntryView()
.tabItem {
Label("Home", systemImage: "house.circle.fill")
}.tag(Tabs.home)
HistoryView()
.tabItem {
Label("History", systemImage: "clock.fill")
}.tag(Tabs.history)
}
}
}
}
struct EntryView: View {
var body: some View {
GeometryReader { g in
ZStack (alignment: .top) {
Color.blue
.frame(height: (g.safeAreaInsets.top) * 0.6, alignment: .top)
.ignoresSafeArea()
HStack {
Spacer()
Button(action: {
print("Save tapped!")
}) {
Text("Save")
.font(Font.title3.bold())
.foregroundColor(.red)
.offset(y: g.size.height * -0.14)
// .position(x: g.size.width * 0.90, y: g.size.height * -0.115)
}
}
.padding(.trailing, 10)
}
}
}
}
struct HistoryView: View {
var body: some View {
VStack (alignment: .leading) {
Text("History Tab")
.padding(.top)
}
}
}
enum Tabs: String {
case home
case history
}

How can I remove the toggle button in Swiftui and how can I show a sidebar on the iPad when this is shown high up

I want a sidebar to be displayed on the iPad. However, what bothers me about the Swiftui Navigazion View is that I have this ugly toggle button. Furthermore I would like to show a sidebar when the iPad is held horizontally. Can I change the Navigation View component so that this works?
no, but you can custom build your own:
struct ContentView: View {
#State private var selection: Int? = nil
var body: some View {
HStack {
List {
Button { selection = 1
} label: {
Text("Item 1")
}
Button { selection = 2
} label: {
Text("Item 2")
}
Button { selection = 3
} label: {
Text("Item 3")
}
}
.frame(width: 200)
.frame(maxHeight: .infinity)
.background(.gray.opacity(0.3))
VStack {
if selection != nil {
// Detail View
Text("Your detail view \(selection!)")
.font(.title)
} else {
Text("Select an item")
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
}
}
}
However, what bothers me about the Swiftui Navigazion View is that I have this ugly toggle button
The possible workaround to avoid button is to hide navigation bar, then in landscape (aka horizontal) you will see just sidebar
NavigationView {
VStack {
Text("Header")
.padding()
List(0..<100, id: \.self) { i in
NavigationLink(
tag: i,
selection: $activeLink,
destination: { Text("Details for \(i)") }
) {
Text("Row #\(i)")
}
}
}
.navigationBarHidden(true) // << here !!
Text("Default Details")
}

Dismiss button (X) on an image - top right alignment HOW?

What is an effective & effecient way to get the Dismiss button (X) into the top right corner?
I'm struggling with container alignment... can't say I GROK it.
Needless to say ... this ain't it!
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
HStack(alignment: .top) {
VStack() {
Spacer(minLength: 5) // vertical space
HStack() {
Spacer()
// close Welcome page (X) button
Button(action: {
//print(" - Button to dismiss page \(self.isPresented)")
self.isPresented = false // dismiss the Welcome view
//print(" - after action Button to dismiss Welcome page \(self.isPresented)")
}, label: {
Image(systemName: "xmark.circle" )
.scaledFont(name: "Georgia", size: Const.titleText)
.minimumScaleFactor(0.3)
.accentColor(.white)
.padding(10)
})
}
Spacer()
}
}
}
}
You need to remove Spacer(minLength: 5) and replace it with padding for HStack.
Spacer(minLength: 5) doesn't mean its length will be exactly 5 (only that the minimum length will be 5).
You may also want to extract close button to another function for clarity.
Try the following:
struct ContentView: View {
...
var body: some View {
ZStack {
Image("Biz-card_2020")
.resizable()
.edgesIgnoringSafeArea(.all)
closeButton
}
}
var closeButton: some View {
VStack {
HStack {
Spacer()
Button(action: {
...
}) {
Image(systemName: "xmark.circle")
.padding(10)
}
}
.padding(.top, 5)
Spacer()
}
}
}
ZStack can be configured using .topTrailing etc. You should use those alignments to configure your view. Using something like Spacer() is going to cause the rest of your views to get pushed down.
ZStack(alignment: .topTrailing) {
// your code here
}
If you need to move your code a bit more, use either padding, or offset modifiers.