Swiftui Button Toggle - ios

I have a series of buttons like this:
ForEach(context.items){item in
Button(action: {
self.variableSelection.toggle()
}) {
Text("\(item)")
if self.variableSelection{
Text("Extra text")
}
}
}
What is happening is:
A list of items appears and when I click a button the "Extra text" appears on the bottom of each button.
What I wanted to happen is for the "Extra text" to appear only below the button I clicked. How can I achieve that?
Thanks

Extract row into separated view, so each row has own state of expanded/collapsed, like
struct ItemRowView: View {
let item: String
#State private var variableSelection = false
var body: some View {
Button(action: {
self.variableSelection.toggle()
}) {
Text("\(item)")
if self.variableSelection{
Text("Extra text")
}
}
}
}
so now cycle
ForEach(context.items){item in
ItemRowView(item: item)
}

Related

Navigation bar menu with multiple selection in iOS app with SwiftUI

I want to achieve behavior close to native iOS reminders app on iPad.
There is dropdown Menu in NavigationBar that shows tags in it. I thought that I can do it with Picker but as I see there is now multiple selection support in it.
I did it using
...
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Menu {
MenuView()
} label: {
Image(systemName: "number")
}
Button("Done") {}
}
}
And MenuView's code is
struct MenuView: View {
#State var tags = ["#tag1", "#tag2", "#tag3", "#tag4"]
var body: some View {
ForEach(tags, id:\.self) { tag in
Button {
} label: {
if selectedTags.contains(tag) {
Label(tag, systemImage: "checkmark")
} else {
Text(tag)
}
}
}
Section {
Button {
} label: {
Label("Configure", systemImage: "ellipsis")
}
}
}
}
The main problem right now is when I press any of this buttons, drop down is closed, while I want this button to only toggle selected status.
I tried to use Text instead of buttons but didn't find way to handle tap gestures from them, they look like being disabled by some view modifier.
What is the optimal way to handle it?

on change for a button to present a list?

can we use on change for a button to present a list within the same view ? I am a beginner
struct ViewMe: View {
var body: some View {
Button (action:{
},label:{
Text("Search")
})
// can we do on change here to appear a list
}
}
According to the information you gave me, here is some code that should work with explanations for each part:
struct ContentView: View {
//Your variable. #State makes it reload the view when changed.
#State var listIsShowing = false
var body: some View {
VStack {
//Your Button
Button (action:{
//Sets variable to true, showing list
listIsShowing = true
},label:{
Text("Search")
})
//To put the button at the top
Spacer(minLength: 0)
//if variable that the button changes = true, show list
if listIsShowing {
//Your list
List {
Text("Your")
Text("list")
Text("appears")
Text("When")
Text("you")
Text("click")
Text("the")
Text("button")
}
//Use below code if you want background to match the top section
//.listStyle(.plain)
}
}
}
}

SwiftUI button inactive inside NavigationLink item area

I have a view for a list item that displays some news cards within a navigationLink.
I am supposed to add a like/unlike button within each news card of navigationLink, without being took to NavigationLink.destination page.
It seems like a small button inside a big button.
When you click that small one, execute the small one without executing the bigger one.
(note: the click area is covered by the two buttons, smaller one has the priority)
(In javascript, it seems like something called .stopPropaganda)
This is my code:
var body: some View {
NavigationView {
List {
ForEach(self.newsData.newsList, id:\.self) { articleID in
NavigationLink(destination: NewsDetail(articleID: articleID)) {
HStack {
Text(newsTitle)
Button(action: {
self.news.isBookmarked.toggle()
}) {
if self.news.isBookmarked {
Image(systemName: "bookmark.fill")
} else {
Image(systemName: "bookmark")
}
}
}
}
}
}
}
}
Currently, the button action (like/dislike) will not be performed as whenever the button is pressed, the navigationLink takes you to the destination view.
I have tried this almost same question but it cannot solve this problem.
Is there a way that makes this possible?
Thanks.
as of XCode 12.3, the magic is to add .buttonStyle(PlainButtonStyle()) or BorderlessButtonStyle to the button, when said button is on the same row as a NavigationLink within a List.
Without this particular incantation, the entire list row gets activated when the button is pressed and vice versa (button gets activated when NavigationLink is pressed).
This code does exactly what you want.
struct Artcle {
var text: String
var isBookmarked: Bool = false
}
struct ArticleDetail: View {
var article: Artcle
var body: some View {
Text(article.text)
}
}
struct ArticleCell: View {
var article: Artcle
var toggle: () -> ()
#State var showDetails = false
var body: some View {
HStack {
Text(article.text)
Spacer()
Button(action: {
self.toggle()
}) {
Image(systemName: article.isBookmarked ? "bookmark.fill" : "bookmark").padding()
}
.buttonStyle(BorderlessButtonStyle())
}
.overlay(
NavigationLink(destination: ArticleDetail(article: article), isActive: $showDetails) { EmptyView() }
)
.onTapGesture {
self.showDetails = true
}
}
}
struct ContentView: View {
#State var articles: [Artcle]
init() {
_articles = State(initialValue: (0...10).map { Artcle(text: "Article \($0 + 1)") })
}
func toggleArticle(at index: Int) {
articles[index].isBookmarked.toggle()
}
var body: some View {
NavigationView {
List {
ForEach(Array(self.articles.enumerated()), id:\.offset) { offset, article in
ArticleCell(article: article) {
self.toggleArticle(at: offset)
}
}
}
}
}
}

Catch Tap gesture on Form in SwiftUI

struct ContentView: View {
#State var text = ""
var body: some View {
Form {
Button(action: {
print("Button pressed")
}) {
Text("Button")
}
}.simultaneousGesture(TapGesture().onEnded( { print("tap") }))
}
}
I need both Button' action and tap gesture on Form to be caught, but only print("tap") is executed. For a VStack works fine, but it seems the Form is a little bit special. Any idea ?
if you do it like this, you will get the button tap (but also the form tap). I don't know if this helps you.
#State var text = ""
var body: some View {
Form {
Button(action: {
}) {
Text("Button")
}.onTapGesture {
print("button")
}
}.onTapGesture {
print("form")
} }
}

SwiftUI - Multiple Buttons in a List row

Say I have a List and two buttons in one row, how can I distinguish which button is tapped without the entire row highlighting?
For this sample code, when any one of the buttons in the row is tapped, both button's action callbacks are invoked.
// a simple list with just one row
List {
// both buttons in a HStack so that they appear in a single row
HStack {
Button {
print("button 1 tapped")
} label: {
Text("One")
}
Button {
print("button 2 tapped")
} label: {
Text("Two")
}
}
}
When only one of buttons is tapped once, I see the callbacks for both buttons being called, which is not what I want:
button 1 tapped
button 2 tapped
You need to use BorderlessButtonStyle() or PlainButtonStyle().
List([1, 2, 3], id: \.self) { row in
HStack {
Button(action: { print("Button at \(row)") }) {
Text("Row: \(row) Name: A")
}
.buttonStyle(BorderlessButtonStyle())
Button(action: { print("Button at \(row)") }) {
Text("Row: \(row) Name: B")
}
.buttonStyle(PlainButtonStyle())
}
}
Seems to be a specific issue concerning Button when contained in a List row.
Workaround:
List {
HStack {
Text("One").onTapGesture { print("One") }
Text("Two").onTapGesture { print("Two") }
}
}
This yields the desired output.
You can also use a Group instead of Text to have a sophisticated design for the "buttons".
One of the differences with SwiftUI is that you are not creating specific instances of, for example UIButton, because you might be in a Mac app. With SwiftUI, you are requesting a button type thing.
In this case since you are in a list row, the system gives you a full size, tap anywhere to trigger the action, button. And since you've added two of them, both are triggered when you tap anywhere.
You can add two separate Views and give them a .onTapGesture to have them act essentially as buttons, but you would lose the tap flash of the cell row and any other automatic button like features SwiftUI would give.
List {
HStack {
Text("One").onTapGesture {
print("Button 1 tapped")
}
Spacer()
Text("Two").onTapGesture {
print("Button 2 tapped")
}
}
}
You need to create your own ButtonStyle:
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(.accentColor)
.opacity(configuration.isPressed ? 0.5 : 1.0)
}
}
struct IdentifiableString: Identifiable {
let text: String
var id: String { text }
}
struct Test: View {
var body: some View {
List([
IdentifiableString(text: "Line 1"),
IdentifiableString(text: "Line 2"),
]) {
item in
HStack {
Text("\(item.text)")
Spacer()
Button(action: { print("\(item.text) 1")}) {
Text("Button 1")
}
Button(action: { print("\(item.text) 2")}) {
Text("Button 2")
}
}
}.buttonStyle(MyButtonStyle())
}
}

Resources