swiftui textfiled and button inside a button - swift

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

Related

SwiftUI Button content uses its own animation where it shouldn't

I have a custom transition to present/dismiss a custom sheet. My problem is that the button content is using its own animation, where it should just follow the rest:
See how the "OK" button is jumping to the bottom in the dismiss animation. It should just be following the rest of the sheet.
Full code:
import SwiftUI
#main
struct SwiftUITestsApp: App {
var body: some Scene {
WindowGroup {
SheetButtonAnimationTestView()
}
}
}
struct SheetButtonAnimationTestView: View {
#State private var sheetPresented = false
var body: some View {
ZStack(alignment: .bottom) {
Button("Present sheet", action: { sheetPresented = true })
.frame(maxWidth: .infinity, maxHeight: .infinity)
if sheetPresented {
Sheet(title: "Sheet", dismiss: { sheetPresented = false })
.transition(.move(edge: .bottom))
}
}
.animation(.easeOut(duration: 2), value: sheetPresented)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.white.edgesIgnoringSafeArea(.all))
}
}
struct Sheet: View {
var title: String
var dismiss: () -> ()
var body: some View {
VStack(spacing: 16) {
HStack {
Text(title)
.foregroundColor(.white)
Spacer()
Button(action: dismiss) {
Text("OK").font(.headline.bold()).foregroundColor(.blue)
.padding(10)
.background(Capsule().fill(Color.white))
}
}
Text("This is the sheet content")
.foregroundColor(.white)
.frame(height: 300)
.frame(maxWidth: .infinity)
}
.padding(24)
.frame(maxWidth: .infinity)
.background(
Rectangle().fill(Color.black).edgesIgnoringSafeArea(.bottom)
)
.ignoresSafeArea(.container, edges: .bottom) // allows safe area for keyboard
}
}
How to make the button follow the sheet animation?
Tested on iOS 16.0.3, iPhone 11, XCode 14.1
Seems like a bug in the library. You can add a slight delay in the button action, which will solve the issue:
Button(action: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
dismiss()
}
}) {
Text("OK").font(.headline.bold()).foregroundColor(.blue)
.padding(10)
.background(Capsule().fill(Color.white))
}
Or if you don't want the click animation, you can remove the button and do the following:
Text("OK").font(.headline.bold()).foregroundColor(.blue)
.padding(10)
.background(Capsule().fill(Color.white))
.onTapGesture {
dismiss()
}

Disable to Change Page On Swipe of Tab View in SwiftUI

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.

SwiftUI: Button Nested Inside ScrollView Won't Fire (WatchOS)

I've tried everything I can think of but I can't get this button to fire consistently when nested inside a ScrollView. If I change the ScrollView to a List, it works fine, but I don't want the UI of a list on the Watch, I would prefer to use a ScrollView. I think it's related to the system detecting taps to scroll and taps on the button but not sure how to fix or any workarounds?
struct WatchWorkoutSummaryView: View {
#EnvironmentObject var workoutManager: WorkoutManager
var body: some View {
ScrollView {
EffortLevelRingViewForWatch(workoutState: WatchWorkoutState.After)
Divider()
.padding(.horizontal)
IntensityViewForWatch()
Divider()
.padding(.horizontal)
HeartRateZonesViewForWatch(workoutState: WatchWorkoutState.After)
Divider()
.padding(.horizontal)
AerobicAnaerobicWatchMasterView()
Divider()
.padding(.horizontal)
VStack {
SummaryView().environmentObject(workoutManager)
Button(action: {
print("button is tapped")
workoutManager.dismissLiveWorkoutTabViewSheet()
workoutManager.resetWorkoutManager()
}, label: {
Text("Done")
.foregroundColor(.white)
})
.background(
Color.buttonColor
.cornerRadius(10)
)
.buttonStyle(BorderedButtonStyle(tint: .clear))
.padding(.horizontal)
.animation(nil)
}
.padding(.vertical)
}
//This doesn't work.
.animation(nil)
}
}

Blur view when button is pressed swiftui

I have a view with a toolbar on the bottom of the view. When clicked - two buttons are displayed. I am trying to achieve when the toolbar is pressed and the buttons are now displayed, the view (or background) becomes blurred/grayed out, except for the newly produced items.
I attached a screenshot of the desired effect I am aiming for.
struct UserDashController: View {
// #State private var showMealView = false
#State private var showSettingsView = false
#State private var showAddViews = false
#State private var angle: Double = 0
init(){
UIToolbar.appearance().barTintColor = UIColor.white
}
var body: some View {
NavigationView {
VStack{
Text("Blue me Please")
.frame(width: 400, height:600)
.background(.orange)
}
//sets setting bar top right
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
VStack{
Button(action: {
showSettingsView.toggle()
}) {
Image(systemName: "line.3.horizontal")
.font(.title3)
.foregroundColor(.black)
}
.sheet(isPresented: $showSettingsView){
JournalEntryMain()
}
}
}
// sets add meal option bottom/center
ToolbarItem(placement: .bottomBar) {
//displaying add meal and recipe icons when clicked
HStack{
Button(action: {
angle += 90
showAddViews.toggle()
}) {
if showAddViews {
VStack{
AddToolbar(showAddOptions: $showAddViews)
.offset(y:-50)
}
}
Image(systemName: "plus.square")
.opacity(showAddViews ? 0.5 : 1)
.font(.largeTitle)
.foregroundColor(.black)
.rotationEffect(.degrees(angle))
.animation(.easeIn(duration: 0.25), value: angle)
}
}
}
}
}
}
}
Buttons that appear when toolbar is pressed
struct AddToolbar: View {
#Binding var showAddOptions: Bool
#State var showMealView = false
var body: some View {
HStack{
VStack{
Button(action: {
showMealView.toggle()
}){
VStack{
Image(systemName: "square.and.pencil")
.font(.title)
.foregroundColor(.black)
.background(Circle()
.fill(.gray)
.frame(width:50, height:50))
.padding(3)
Text("Meal")
.foregroundColor(.black)
}
}.fullScreenCover(isPresented: $showMealView){
JournalEntryMain()
}
}
VStack{
Image(systemName: "text.book.closed")
.foregroundColor(.black)
.font(.title)
.background(Circle()
.fill(.gray)
.frame(width:50, height:50))
.padding(3)
Text("Recipe")
.foregroundColor(.black)
}
.offset(y: -50)
}
.frame(height:150)
}
}
Desired Effect
I'm a little confused by your desired effect example, partially because in the UI screenshot you attached, the background isn't blurred, it's just darkened. So, the following answer isn't tailored to your specific example but still should be able to help.
Let's say whatever variable you're using to determine whether or not to show the toolbar is showSettingsView. You could put the following modifiers on your background view:
To blur: .blur(showSettingsView ? 0.5 : 0.0)
To darken: .brightness(showSettingsView ? -0.5 : 0.0)
Obviously just replace "0.5" with whatever number feels best.

SwiftUI Navigation Link containing a Button

I have a SwiftUI list that looks like this:
List {
ForEach(items) { item in
NavigationLink(destination: EditItemView(item: item)) {
ItemView(item: item, buttonAction: {
// Do something
})
}
}
where ItemView is:
struct ItemView: View {
var item: Item
let buttonAction: () -> Void
var body: some View {
HStack(alignment: .center) {
Text(item.tile)
.font(.headline)
Spacer()
Button(action: buttonAction,
label: {
Image(systemName: "plus.circle")
})
.padding(.horizontal)
}
}
}
However, whenever I tap on the button, instead of performing the button action it goes to the EditItemView which is the navigation link action.
So how can I have a button performing an action inside a navigation link?
Or if that is not possible, how can I make that tapping the item view does one action and tapping the button does another action?
Here is possible approach - changed only ItemView by replacing default button with tap gesture (tested with Xcode 12 / iOS 14)
struct ItemView: View {
var item: Int
let buttonAction: () -> Void
var body: some View {
HStack(alignment: .center) {
Text("Item \(item)")
.font(.headline)
Spacer()
Image(systemName: "plus.circle")
.padding(.horizontal).contentShape(Rectangle())
.highPriorityGesture(TapGesture().onEnded(buttonAction))
}
}
}