Disable to Change Page On Swipe of Tab View in SwiftUI - swift

I am using Tab View in my SwiftUI app. I want the changing of page disabled, while swiping left or right. And I had achieved it from this. This works fine but the issue I am facing is I have a button on the bottom of every view, and when I try to swipe from the button, it is swiping left right. I want to disable it too but don't know how to do it. Here is my code:
struct TabViewTesting: View {
#State var tabSelection = 0
var body: some View {
TabView(selection: $tabSelection) {
firstView().tag(0).contentShape(Rectangle()).gesture(DragGesture())
secondView().tag(1).contentShape(Rectangle()).gesture(DragGesture())
thirdView().tag(2).contentShape(Rectangle()).gesture(DragGesture())
}.tabViewStyle(.page(indexDisplayMode: .never))
}
}
And this is the code for the Views:
extension TabViewTesting {
func firstView() -> some View {
VStack {
Text("First screen")
Spacer()
Button {
self.tabSelection = 1
} label: {
ZStack {
RoundedRectangle(cornerRadius: 20)
.frame(height: 50)
Text("move to 2nd view")
.foregroundColor(.white)
}
}.padding()
}.background(.green)
}
func secondView() -> some View {
VStack {
Text("second screen")
Spacer()
Button {
self.tabSelection = 2
} label: {
ZStack {
RoundedRectangle(cornerRadius: 20)
.frame(height: 50)
Text("move to 3rd view")
.foregroundColor(.white)
}
}.padding()
}.background(.red)
}
func thirdView() -> some View {
VStack {
Text("Third screen")
Spacer()
Button {
self.tabSelection = 0
} label: {
ZStack {
RoundedRectangle(cornerRadius: 20)
.frame(height: 50)
Text("move to first view")
.foregroundColor(.white)
}
}.padding()
}.background(.yellow)
}
}
And this is what happening:

I actually found an answer in the comments of this question.
The issue is: any view that has an "onTapGesture" will ignore ".gesture(DragGesture())".
The solution is to use ".simulataneousGesture(DragGesture())" instead to ensure the gesture is capture and handled by both view/modifier.
It worked perfectly in my case after changing it. The only exception is for 2 finger drag gesture.

Related

How to prevent SwiftUI to rerender the entire tabview when using a Custom Alert view on top of it?

I have a tab view with 5 tabs. An alert view will be shown at the top when my view model's property gets true? But every time the alert is shown, the entire tab view is redrawn causing high cpu usage.
I want the alert to be shown regardless of which tab the user is in so I did a ZStack on the tab view to show the alert. But how do I prevent SwiftUI to redraw the entire tab view?
struct ContentView: View {
var body: some View {
ZStack {
TabView() {
MiniPlayer(content:
BrowseView()
)
.tabItem {
Group {
Image(systemName: "square.grid.2x2")
Text("Browse")
}
.onTapGesture {
selectedTab = 0
}
}
.tag(0)
MiniPlayer(content:
AllSongsListView()
)
.tabItem {
Group {
Image(systemName: "music.note")
Text("Songs")
}
.onTapGesture {
selectedTab = 1
}
}
.tag(1)
MiniPlayer(content:
PlaylistsView()
)
.tabItem {
Group {
Image(systemName: "music.note.list")
Text("Playlists")
}
.onTapGesture {
selectedTab = 2
}
}
.tag(2)
MiniPlayer(content:
MoreView()
)
.tabItem {
Group {
Image(systemName: "ellipsis")
Text("More")
}
.onTapGesture {
selectedTab = 3
}
}
.tag(3)
}
Alert()
}
.ignoresSafeArea(.keyboard)
}
}
struct Alert: View {
#EnvironmentObject var manager: Manager
var body: some View {
if manager.showSuccessAlert {
VStack {
VStack {
Text("Successful.")
.font(.system(size: 20, weight: .bold, design: .rounded))
.multilineTextAlignment(.center)
}
.frame(height: 50)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color.blue)
.clipShape(Capsule())
Spacer()
}
.zIndex(1)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
withAnimation {
manager.showSuccessAlert = false
}
}
}
.transition(.asymmetric(insertion: .move(edge: .top), removal: .move(edge: .top)))
}
}
}
Try to move entire TabView into separated view, so that ZStack looks like
ZStack {
TabsContentView() // << move everything inside !!
Alert()
}

Spacer not pushing view to top of screen inside ZStack?

How can I position my search-bar and button to the top of the screen with 65 padding from the top AND KEEP the onTapGestureFunctionality?
I tried the modifiers .edgesIgnoringSafeArea(.top) with padding(.top, 65) applied to the VStack inside my ZStack but then my onTapGesture would not work when tapping on either the search bar or the button...
Why does the Spacer() only push the elements about 80% to the top of the screen? Could this be because its inside a navigation view?
See image below.
Thank you
struct MapScreen: View {
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
VStack {
HStack(spacing: 15) {
SearchBar()
.padding(.leading, 5)
.onTapGesture {
print("search pressed")
}
ActivateClaimButton()
.onTapGesture {
print("claim pressed")
}
}
// 2 modifiers below positions correctly but
// onTapGesture doesn't work
// .padding(.top, 65)
// .edgesIgnoringSafeArea(.top)
Spacer()
}.zIndex(2)
MapView(locations: $locations, spotArray: $spotArray).edgesIgnoringSafeArea([.top, .bottom])
BottomNavBar().zIndex(1).edgesIgnoringSafeArea(.all).offset(y: 35)
}
.navigationViewStyle(StackNavigationViewStyle())
.fullScreenCover(isPresented: $presentSearchView, content: {
SearchView()
})
}
}
}
The answer that worked for me was
.navigationBarHidden(true)

Problem with SwiftUI: Vertical ScrollView with Button

I'm quite new to programming so please excuse any dumb questions. I'm trying to make a ScrollView with the content being buttons. Although the button prints to console, when shown in the simulator the button displays as a large blue rectangle rather than displaying the image I would like it to.
Code Regarding ScrollView:
struct ContentView: View {
var body: some View {
[Simulator Display][1]
VStack {
Image("logo")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(.leading, 50)
.padding(.trailing, 50)
.padding(.top, 20)
.padding(.bottom, -20)
Spacer()
ScrollView {
VStack(spacing: 20) {
Button(action: {
//ToDo
print("Executed")
}) {
Image("Logo")
}
}
}
}
}
}
Simulator Display:
Image(Placeholder for now) I want to be displayed:
So I tried it an yeah it was very weird. Anyway, here is an example of how you can include the image. Just take the portion of the Button and paste it
struct ContentView: View {
var body: some View {
ZStack {
Button(action: {
print("button pressed")
}) {
Image("image")
.renderingMode(.original)
}
}
}
}

Custom action sheet with SwiftUI

I have a TabView at the top of navigation and a tab which contains the user profile.
I want to present an action sheet with custom style when users touch cover/profile picture.
The custom style looks like this:
I already try to attach a second view at the bottom of the ZStack but the TabView always looks in front.
How can I custom the action sheet or how can I hide TabView?
Thanks
ZStack approach will work in this scenario.
I have tried with a small example, you can tweak and try to fit in your project and see if it works.
Implementation :
struct ContentView: View {
#State var showingCustomSheet = false
var body: some View {
ZStack {
VStack {
Button(action: {
withAnimation {
self.showingCustomSheet.toggle()
}
}) {
Text("Show Custom sheet")
}
}
ZStack {
HStack(alignment: .bottom) {
Spacer()
VStack (alignment: .leading, spacing: 10) {
Spacer()
Button(action: {}) { Text("Delete photo") }
Button(action: {}) { Text("Take photo") }
Button(action: {}) { Text("Choose photo") }
Button(action: {
withAnimation {
self.showingCustomSheet.toggle()
}
}) { Text("Cancel") }
}
Spacer()
}
}.background(Color.black.opacity(0.1))
.edgesIgnoringSafeArea(.all)
.offset(x:0, y: self.showingCustomSheet ? 0 : UIApplication.shared.keyWindow?.frame.height ?? 0)
}
}
}

swiftui textfiled and button inside a button

I am trying to trigger button when a user clicks background area of a view.
As a solution, I decided to make a button and put the view into the button as a label.
But the Problem is that the button is triggered even on the TextField and sometimes on another button inside.
How can i disable the back button on some views (TextField and Button in this case) I select?
I attach my code below and rendered preview.
import SwiftUI
struct ButtonOnbutton: View {
#State var memo: String = ""
var body: some View {
Button(action: {
print("down button clicked")
}) {
VStack(spacing: 10) {
Text("before")
.foregroundColor(.white)
TextField("type memo", text: $memo)
.font(.custom("SFProDisplay-Regular", size: 12))
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("after")
.foregroundColor(.white)
Button(action: {
print("inside button clicked")
}) {
Text("inside button")
.foregroundColor(.white)
}
.background(Color.red)
}
}
.background(Color.blue)
.padding()
}
}
struct buttonOnbutton_Previews: PreviewProvider {
static var previews: some View {
ButtonOnbutton()
}
}
If I correctly understood your goal then it is better to used onTapGesture instead of button, as shown below
var body: some View {
VStack(spacing: 10) {
Text("before")
.foregroundColor(.white)
TextField("type memo", text: $memo)
.font(.custom("SFProDisplay-Regular", size: 12))
.textFieldStyle(RoundedBorderTextFieldStyle())
.onTapGesture {} // << just blocks outer tap
Text("after")
.foregroundColor(.white)
Button(action: {
print("inside button clicked")
}) {
Text("inside button")
.foregroundColor(.white)
}
.background(Color.red)
}
.background(Color.blue)
.onTapGesture {
print("background area clicked")
}
.padding()
}