In SwiftUI, Buttons highlight on touch.
But when you place one into a ScrollView, it will also highlight when the user scrolls.
On UIKit, the delayscontenttouches UIScrollView's property exactly fixes this problem, but I haven't found a SwiftUI way to do it.
Here's some basic code that reproduces the problem.
ScrollView {
VStack {
Button(action: {
print("1")
}, label: {
Color.blue.frame(height: 300)
})
Button(action: {
print("2")
}, label: {
Color.blue.frame(height: 300)
})
Button(action: {
print("3")
}, label: {
Color.blue.frame(height: 300)
})
}
}
Related
I'm new to coding. Can someone please explain to me why this code doesn't work?
I'm trying to open a sheet when a button is tapped.
The button is part of a Floating Action Button like in Gmail with multiple options.
This 'SecondaryButton' is the options the user sees when the FAB is active.
Button(action: {
scanSheet.toggle()
}, label: {
//Scan Button
SecondaryButton(open: $open, icon: "camera.viewfinder", label: "Scan", color: "Blue", offsetY: -90)
.padding(.trailing, 40)
})
.sheet(isPresented: $scanSheet, content: {
Text("Hello")
})
I think that your SecondaryButton has something to do with this. I tested it without just a Text as label and it works.
Did you try to load a Button into the Label?
struct ContentView: View {
#State var scanSheet = false
var body: some View {
Button(action: {
scanSheet.toggle()
}, label: {
//Scan Button
Text("Button")
.padding(.trailing, 40)
})
.sheet(isPresented: $scanSheet, content: {
Text("Hello")
})
}
}
I'm trying to implement a ZStack inside of navigationBarItems to load a custom alert. Here is my implementation:
var body: some View {
VStack{
List(self.itemsStore.names){ item in
Text("hello")
}
}
.navigationBarItems(trailing: Button(action: {
ZStack {
ItemsAlert(isShown: $isPresented, text: $text)
}
}, label: {
Image(systemName: "plus")
}))
}
On this line I'm getting this error:
Any of you knows why I'm getting this warning? or if is a work around this error?
I'll really appreciate your help
You cannot put SwiftUI view in closure - it has not sense, view should be in the view hierarchy, button can activate states to manipulate with views, like
var body: some View {
ZStack {
ItemsAlert(isShown: $isPresented, text: $text)
VStack{
List(self.itemsStore.names){ item in
Text("hello")
}
}
}
.navigationBarItems(trailing: Button(action: {
self.isPresented = true // << activate state
}, label: {
Image(systemName: "plus")
}))
}
In Xcode 12 Beta 6, dismissing a sheet doesn't work inside a button's action inside a ToolbarItem.
My sheet view looks like:
NavigationView {
Form {
Section {
TextField("Name", text: $name)
}
}
.navigationTitle("New Thing")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(action: {
self.presentation.wrappedValue.dismiss()
}, label: {
Text("Cancel")
})
}
ToolbarItem(placement: .confirmationAction) {
Button(action: {
do {
// some saving logic
try managedObjectContext.save()
self.presentation.wrappedValue.dismiss()
} catch {
print("didn't save due to \(error.localizedDescription)")
}
}, label: {
Text("Save")
})
}
}
}
EDIT: here's how I constructed the sheet
var body: some View {
List {
ForEach(results) { result in
HStack {
NavigationLink(destination: SingleResultView(result: result)) {
SingleResultRowView(result: result)
}
}
}
.onDelete(perform: deleteResult)
}
.navigationTitle("All Results")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
self.isNewResultSheetPresented.toggle()
}, label: {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
})
.sheet(isPresented: $isNewResultSheetPresented) {
NewResultView()
// ^ this contains the code above
.environment(\.managedObjectContext, self.managedObjectContext)
}
}
}
}
When the sheet is first presented, immediately a console log appears:
2020-09-13 20:52:02.333679-0700 MyApp[2710:89263]
[Presentation] Attempt to present <_TtGC7SwiftUI22SheetHostingControllerVS_7AnyView_: 0x1027b7890> on
<_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x10270d620>
(from <_TtGC7SwiftUIP10$194f39bd428DestinationHostingControllerVS_7AnyView_: 0x103605930>)
which is already presenting <_TtGC7SwiftUI22SheetHostingControllerVS_7AnyView_: 0x103606d60>.
I can dismiss the sheet only by swiping down.
For reference, I went back to an older commit where I used NavigationBarItems and it worked perfectly. But from what I understand, this is a situation where I'm supposed to be using ToolbarItem.
Does anybody know why the good old self.presentation.wrappedValue.dismiss() doesn't work here or why is the sheet being presented twice?
Move sheet out of toolbar, as
var body: some View {
List {
ForEach(results) { result in
HStack {
NavigationLink(destination: SingleResultView(result: result)) {
SingleResultRowView(result: result)
}
}
}
.onDelete(perform: deleteResult)
}
.navigationTitle("All Results")
.sheet(isPresented: $isNewResultSheetPresented) { // << here !!
NewResultView()
// ^ this contains the code above
.environment(\.managedObjectContext, self.managedObjectContext)
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
self.isNewResultSheetPresented.toggle()
}, label: {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
})
}
}
}
I'm attempting to recreate the following animation from the Castro app...
(The GIF is slowed down so you can see the effect)
As you can see in the GIF above, you have a row of buttons that appear when the cell is tapped. Each button has a zoom-in and zoom-out effect. The animations are staggered, such that the first button finishes first and the last button finishes last.
What I've tried...
struct SwiftUIView: View {
#State var show: Bool = false
var body: some View {
VStack {
Button(action: {
withAnimation {
show.toggle()
}
}, label: {
Text("Button")
})
HStack {
if show {
Button(action: {}, label: { Image(systemName: "circle") })
.transition(.scale)
Button(action: {}, label: { Image(systemName: "circle") })
.transition(.scale)
Button(action: {}, label: { Image(systemName: "circle") })
.transition(.scale)
}
}
}
}
}
As you can see in the image above... Each button does zoom in, but only as the view is removed. Also, I don't know how to stagger the animation.
Try the following:
struct ContentView: View {
#State var show = false
#State var showArray = Array(repeating: false, count: 3)
var body: some View {
VStack {
Button(action: toggleButtons) {
Text("Button")
}
HStack {
ForEach(showArray.indices, id: \.self) { index in
self.circleView(for: index)
}
}
}
}
#ViewBuilder
func circleView(for index: Int) -> some View {
if show {
ZStack {
Image(systemName: "circle")
.opacity(.leastNonzeroMagnitude)
.animation(nil)
if showArray[index] {
Image(systemName: "circle")
.transition(.scale)
}
}
}
}
func toggleButtons() {
showArray.indices.forEach { index in
withAnimation {
self.show = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(index)) {
withAnimation {
self.showArray[index].toggle()
if index == self.showArray.count - 1, self.showArray[index] == false {
self.show = false
}
}
}
}
}
}
It uses a little hack to align the views correctly - in ZStack there is a fake Image with almost no opacity.
The following code presents the same view PatientView in 2 ways:
As a Sheet
NavigationLink
var body: some View {
NavigationView {
List {
ForEach(patients, id: \.self) { patient in
NavigationLink(destination:
PatientView(selectedPatient: patient, isDependant: false, mainID: "")) {
PatientRowView(patient: patient)
}
}
.onDelete { indexSet in
for index in indexSet {
self.moc.delete(self.patients[index])
}
}
}
.navigationBarItems(trailing: Button(action: {
self.showingAddScreen.toggle()
}) {
Image(systemName: "plus")
})
.sheet(isPresented: $showingAddScreen) {
PatientView(selectedPatient: nil, isDependant: false, mainID: "").environment(\.managedObjectContext, self.moc)
}
.navigationBarTitle("Patients")
.background(NavigationConfigurator { nc in
nc.navigationBar.barTintColor = .launchpadBackgroundGray
nc.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.black]
})
}
}
When presented as a Sheet the Pickers on the PatientView are disabled:
But when presented through the Navigationlink the pickers work as expected:
Does anyone know why this is happening? Any solutions to make the pickers were properly when presented as a Sheet would be greatly appreciated.
Thank You
Form picker requires NavigationView so provide it for sheet workflow explicitly
.sheet(isPresented: $showingAddScreen) {
NavigationView {
PatientView(selectedPatient: nil, isDependant: false,
mainID: "").environment(\.managedObjectContext, self.moc)
}
}