I have a view with a set of toolbar items where I sometimes want to hide some of the items when activating a certain state.
Consider the following SwiftUI code:
#State var hideToolbarItems = false
var body: some View {
NavigationSplitView {
Text("Hello, World!")
.navigationTitle("MyToolbarItems")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
hideToolbarItems.toggle()
} label: {
Text("Toggle")
}
}
ToolbarItemGroup(placement: .navigationBarTrailing) {
if !hideToolbarItems {
Button {
} label: {
Text("Button1")
}
Button {
} label: {
Text("Button2")
}
Button {
} label: {
Text("Button3")
}
}
}
}
} detail: {
}
}
When previewing this it seems to work as expected, the ToolbarItemGroup disappears when hideToolbarItems becomes true and appears again when hideToolbarItems is false. On an iPad device it works almost the same, the ToolbarItemGroup becomes grouped under a "..."-icon until the hideToolbarItems has changed back and forth, then it won't become grouped under a "..."-icon anymore.
But the real problem is that the ToolbarItemGroup won't show at all on an iPhone device. Has anyone solved this problem on iPhone? I can't seem to find a good solution.
I am running Xcode 14.2 and use iOS 16.0.
SwiftUI effects different result by platforms.
ToolbarItemGroup may be compact button only in iPad.
if you want to show buttons as compact anytime, you can use Menu.
ToolbarItemGroup(placement: .navigationBarTrailing) {
if !hideToolbarItems {
Menu {
Button {
} label: {
Text("Button1")
}
Button {
} label: {
Text("Button2")
}
Button {
} label: {
Text("Button3")
}
} label: {
Image(systemName: "ellipsis.circle")
}
}
}
Related
I have text in a scroll-view that I would like to add a context menu on. I also want to close the keyboard when the context menu is open.
Here is some sample code:
struct TestView: View {
var body: some View {
let texts = Array(1...100).map { "Test \($0)" }
ScrollView {
VStack(spacing: 10) {
ForEach(texts, id: \.self) { text in
Text(text)
.frame(maxWidth: .infinity)
.contextMenu {
Button {
UIPasteboard.general.string = text
} label: {
Label("Copy", systemImage: "doc.on.clipboard")
}
}
.simultaneousGesture(LongPressGesture(minimumDuration: 0.5).onEnded { _ in
print("Do something else when the context menu is opened. (i.e. close keyboard)")
})
}
}
}
}
}
This code prevents me from being able to scroll on the scrollview starting from the text though.
I've seen in other answers that adding onTapGesture before the other gestures can fix it, but it doesn't seem to work in this case.
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?
I can't seem to find how to bring a view to the center like a popup and blur background like when you long press a message in the messages app.
I know there is a simple native way to do this but I can't seem to find out how.
You are looking for contextMenu(menuItems:). It shows those buttons you see, and automatically blurs the background content to focus the selected view.
Example:
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack(spacing: 30) {
ForEach(0 ..< 20) { _ in
Text(String(Int.random(in: 1 ... 100000000)))
.padding()
.contextMenu {
Button {
//
} label: {
Label("Reply", systemImage: "arrowshape.turn.up.left")
}
Button {
//
} label: {
Label("Copy", systemImage: "doc.on.doc")
}
Button {
//
} label: {
Label("Translate", systemImage: "arrow.left.arrow.right")
}
Button {
//
} label: {
Label("More…", systemImage: "ellipsis.circle")
}
}
}
}
}
}
}
Result:
I have a swiftUI project and I want to place a circular progressView in the navigation bar doing the following:
.toolbar {
ToolbarItem(placement: ToolbarItemPlacement.navigationBarTrailing) {
Button {
// some action
} label: {
HStack {
ProgressView()
.frame(width: 20, height: 20)
.progressViewStyle(CircularProgressViewStyle())
Text("Save Edits")
}
}
}
}
The "Save Edits" appears but the progressView does not. I'm guessing SwiftUI only allows text or images in the navigationBar as a toolbarItem. Was wondering if there is any other approach to get the circular progressView in the navigationBar. I see this in many apps that want to let the user know something is happening when they press the navigation bar button.
It seems the problem was having an HStack in the button.
Here is the solution in case somebody else had this problem:
.toolbar {
ToolbarItem(placement: ToolbarItemPlacement.navigationBarTrailing) {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
}
ToolbarItem(placement: ToolbarItemPlacement.navigationBarTrailing) {
Button {
// some action
} label: {
Text("Save Edits")
}
}
}
I want to implement a Settings view which can be opened taping on a gear icon button in the navigation tool bar.
This button opens a SwiftUI sheet with on Ok button to validate settings and close the settings window.
It works well if you use it without rotating the iPhone.
But if you rotate the phone when the settings window is opened, the Ok button does not work anymore and the window stays on screen (even if you rotate back the phone).
In the console, an error appears when I rotate the phone. Here is the message:
[Presentation] Attempt to present <…> on <…> (from <…>) which is already presenting
This issue seems to be linked to StackNavigationViewStyle() modifier I use to not have 2 columns on landscape mode.
If I remove the following line, the bug disappears but the layout is no more the one I want.
.navigationViewStyle(StackNavigationViewStyle())
Here is a sample code I wrote to reproduce the problem:
import SwiftUI
struct ContentView: View {
// Size class
#Environment(\.verticalSizeClass) var sizeClassV
#State private var showGearView: Bool = false
var gearButton: some View {
HStack {
Button(action: {
self.showGearView.toggle()
}) {
Image(systemName: "gear")
.imageScale(.large)
.accessibility(label: Text("Settings"))
}
.sheet(isPresented: self.$showGearView, onDismiss: {
}, content: {
gearView()
})
}
}
var body: some View {
return NavigationView {
// if sizeClassV == .regular {
VStack {
Text("Click on Gear and rotate your iPhone: here is the bug when clicking on Ok: the sheet does not collapse!")
.multilineTextAlignment(.center)
.padding(.all)
}
.padding(.all)
.navigationTitle("Bug")
.navigationBarTitleDisplayMode(.inline)
.toolbar(content: { gearButton })
}
// The bug only happens when adding the StackNavigationViewStyle below
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct gearView: View {
#Environment(\.presentationMode) var presentationMode
var OKButton: some View {
HStack {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Text("OK")
}
}
}
var body: some View {
return NavigationView {
VStack {
Form {
Section(header: Text("Settings")) {
Text("No Settings")
}
}
}
.navigationTitle(Text("Settings"))
.toolbar(content: {
ToolbarItem(placement: .primaryAction) {
OKButton
}
})
}
}
}