SwiftUI custom button and menu not having same size in navigation bar - button

In my app, I want to customize the appearance of buttons and menus in the navigation bar.
This is what I did:
For the button:
struct BarButtonStyle: ButtonStyle {
private let cornerRadiusWidth:CGFloat = 4.0
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(4)
.foregroundColor(Color.barButtonForeground)
.background(
LinearGradient(gradient: Gradient(colors: [Color.barButtonBackground1, Color.barButtonBackground2]),
startPoint: .top,
endPoint: .bottom)
)
.cornerRadius(cornerRadiusWidth)
}
}
For the menu:
struct BarMenuStyle: MenuStyle {
private let cornerRadiusWidth:CGFloat = 4.0
func makeBody(configuration: Configuration) -> some View {
Menu(configuration)
.padding(4)
.foregroundColor(Color.barButtonForeground)
.background(
LinearGradient(gradient: Gradient(colors: [Color.barButtonBackground1, Color.barButtonBackground2]),
startPoint: .top,
endPoint: .bottom)
)
.cornerRadius(cornerRadiusWidth)
}
}
I used them like this:
// navigation bar trailing
ToolbarItemGroup(placement: .navigationBarTrailing) {
// the menu
Menu {
}
label: {
Image(systemName: displayType.info.image)
}
.menuStyle(BarMenuStyle())
// the button
Button(action: {
}) {
Image(systemName: "list.number")
//.resizable()
}
.buttonStyle(BarButtonStyle())
}
However, the display size is different in the navigation bar (menu at left, button at right):
I have no idea why.
The only workaround is to change the padding for the menu, but it's not perfect.
What did I miss ?

I would try using .frame(maxWidth: .infinity) method on each element (in your case the button and the menu). What that does is it makes each element take the most amount of space possible, when there's multiple elements like that, they are distributed evenly. You can further regulate the size by adding a .frame() to the parent container. Should work like magic.

Related

While using a ForEach nested in a LazyVGrid how can I implement usable play buttons that navigate to a different view?

I have created a scrollable grid view where each icon is supposed to represent a different playlist. I implemented a play button using a navigation link for each playlist in the left corner however, for some reason, the play button is not usable. It does not work in the preview or the simulator. In other words, the play button does not allow me to click it and navigate to another view.
Here is the code I have:
var body: some View {
ZStack {
ScrollView {
LazyVGrid (columns: [GridItem(.fixed(200)), GridItem(.fixed(200))]) {
ForEach(musics) { sub in
ZStack (alignment: .topLeading){
VStack(alignment: .leading) {
Image(sub.albumImage)
.resizable()
.frame(width: 150, height: 150)
.mask(RoundedRectangle(cornerRadius: 20))
Text(sub.albumGen)
.fontWeight(.bold)
.foregroundColor(.white)
Text(sub.songs)
.foregroundColor(.white)
} // END OF VSTACK
NavigationLink {
// Naviagte to a different view!
CategoryView()
} label: {
Image(systemName: "play.circle")
.font(.system(size: 30))
.padding()
}
} // END OF ZSTACK
}// END OF FOR EACH
} // END OF GRID ITEM
} // END OF SCROLL VIEW
} //END OF ZSTACK
}
}
Here is an image of how it looks in the preview and simulator:
I have circled the play buttons because it is hard to see. Usually, when I use a navigation link, the buttons appear blue and are clickable.
How can I edit my code so that the navigation link is active and navigates to a different view?
Embed your View in a NavigationView, which is required for NavigationLink to work.

SwiftUI Button tap area not accurate but in navigationBarItems is right

Here use the same button view in navigationbaritem and ContentView.
have idea to solve this problem?
https://imgur.com/a/jCxfVSa
struct myButton: View {
var body: some View {
Button(action: { print("qwe\(Int.random(in: 1...100))") }) {
Image( "play")
.resizable()
.scaledToFit()
.background(Color.red)
}
.frame(width: 40, height: 40, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
}
}
struct ContentView: View {
var body: some View {
NavigationView {
myButton()
.navigationBarItems(leading:
myButton()
)
.navigationTitle("Title")
.navigationBarTitleDisplayMode(.inline)
}
}
enter image description here
Natively in SwiftUI you can't use shaping on toolbar icons
(you can, but sometimes navigation bar icons loses clipshapes (after navigationLink, .sheet, .alert....)
(on SwiftUI 2 prefer to use .toolbar{}, no .navigationBarItems() )
But, you can use SwiftUIX library on GitHub

swiftui open view from menu button

I am trying to do something very basic, but I have been through the official tutorials, and through dozens of stack overflow posts. I am trying to open an activity from a menu button. I do want the standard navigation back arrow when I do this.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
Text("Hello World!")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {
Activities()
}) {
Label("Activities", systemImage: "doc")
}
"Activities()" is my view. When I do this nothing happens. I have read in other posts that you cannot have a navigation link in a menu either, which would be fine if that worked. How do I programmatically open up a view so the back arrow works?
Thank you
In this case where you want to open a view but can't use a NavigationLink directly you can use it in another place and activate it programmatically from the button via a State property:
#State private var isShowingDetailView = false
var body: some View {
NavigationView {
VStack(alignment: .leading) {
NavigationLink(destination: Activities(), isActive: $isShowingDetailView) {
EmptyView()
}
Text("Hello World!")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Menu {
Button(action: {
isShowingDetailView = true
}) {
Label("Activities", systemImage: "doc")
}
}
}
}
}
}

SwiftUI Why doesn't the Rectangle created in my code fill up the whole page?

I'm trying to figure out layout mechanisms in SwiftUI, and while its fairly straightforward, I don't know what I'm missing here.
According to me, the following code should fill the device's entire screen with the colour green, but for some reason, it leaves a small gap at the bottom of the screen. This is not the safe area as the gap is left even in older devices such as the iPhone SE.
I know I can achieve the same result just by changing the screen colour to green; as I said I'm trying to figure out layout in SwiftUI.
I'd really appreciate any help.
struct ContentView: View {
var body: some View {
VStack {
Green()
.edgesIgnoringSafeArea(.all)
}
}
}
struct Green: View {
let hh = UIScreen.main.bounds.height
let ww = UIScreen.main.bounds.width
var body: some View {
ZStack {
Rectangle()
.fill(Color.green)
.frame(width: ww, height: hh, alignment: .center)
}
}
}
This is due to used UIScreen restriction, instead it needs to use
struct Green: View {
var body: some View {
Rectangle()
.fill(Color.green)
}
}
and area filled completely, `cause detected dynamically. Tested with Xcode 11.4.
Simply use Rectangle() inside your ZStack. Here is your ContentView:
struct ContentView: View {
var body: some View {
ZStack {
// This is your green background
Rectangle()
.fill(Color.green)
.edgesIgnoringSafeArea(.all)
// Use other UI stuffs here
VStack {
Text("Use other UI components here")
}
}
}
}
Result:
You can achieve this using ZStack. add Color direct as a component
struct ContentView: View {
var body: some View {
ZStack {
Color.green
.edgesIgnoringSafeArea(.all)
Text("Use other UI components here")
}
}
}

How to set image to button in SwiftUI?

I want to add a button with an image (on the left), as follows:
Probably, it seems to be similar to setImage(_:for:) when it comes to UIButton.
What I have done so far is:
Button(action: {}) {
Text("Add To Cart")
.foregroundColor(.black)
.padding(.all, 10.0)
}
.background(Color.gray)
.cornerRadius(5.0)
And the result is pretty close to the required one:
However, I couldn't find any property/method in SwiftUI Button related to setting an image.
How to resolve it?
You could implement it as:
Button(action: {}) {
HStack(alignment: .center, spacing: 5.0) {
Image("cart")
.padding(.leading, 10.0)
Text("Add to Cart")
.foregroundColor(.black)
.padding(.all, 10.0)
}
}
.background(Color.gray)
.cornerRadius(5.0)
Passing HStack instead of Text would do the trick.
To clarify, adding an image next to the button label is not directly related to the button anymore! When creating a button using init(action:label:) initializer, we are passing () -> Label as the second parameter; If you trace the type of Label, you'll see that it is basically a View, so you are not forced to return a Text type for it (as mentioned in the question's code snippet).
Obviously, you could add the image to the right of the label by adding Text before the Image. Also, if you are aiming to add the text on top of the image (or vice-versa), you could simply replace the HStack with a VStack.
Furthermore, more reusable code
You could make your code to be more reusable by creating a custom body View for the button. Also, you could create a custom ViewModifier:
import SwiftUI
// Custom View
struct CustomButtonBody: View {
private var iconName: String
private var title: String
init(iconName: String = "cart", title: String) {
self.iconName = iconName
self.title = title
}
var body: some View {
HStack(alignment: .center, spacing: 5.0) {
Image(iconName)
.padding(.leading, 10.0)
Text(title)
.foregroundColor(.black)
.padding(.all, 10.0)
}
}
}
// Custom Modifier
struct CustomButtonModifier: ViewModifier {
func body(content: Content) -> some View {
return content
.background(Color.gray)
.cornerRadius(5.0)
}
}
thus:
Button(action: {}) {
CustomButtonBody(title: "Add to Cart")
}
.modifier(CustomButtonModifier())
Why don't you just
Button(action: {}) {
Label("Add to cart", systemImage: "cart")
}

Resources