Making an Image navigationBarButton actually usable


link1. Problem

SF Symbols is great for adding iconography, buttons and some images to your app. When using it to add a button for the navigation bar, however, the default settings result in the button being just a bit too small to be useful:

Problem: Tappable area of button is too small

link2. Solution

link2.1. ImageModifier

Fixing this (and making it reusable) just takes a ViewModifier. As we're dealing with an image, however, we actually need to first build an ImageModifier. This answer on StackOverflow goes into detail about how and why, and here's the code:

ImageModifier.swift
1// See: https://stackoverflow.com/a/59534345/7735299

2

3import Foundation

4import SwiftUI

5

6protocol ImageModifier {

7 /// `Body` is derived from `View`

8 associatedtype Body: View

9

10 /// Modify an image by applying any modifications into `some View`

11 func body(image: Image) -> Self.Body

12}

13

14extension Image {

15 func modifier<M>(_ modifier: M) -> some View where M: ImageModifier {

16 modifier.body(image: self)

17 }

18}

19

We need to build our own ImageModifier because the default ViewModifier only takes

1func body(content: View) -> some View

a View but the .resizable() modifier is only available on Image itself.

link2.2. BarButtonModifier

Now, we can write our modifier to make the bar button more tappable.

We do that by making the frame wider .frame(width: 25) and add some padding on the side of the button .padding(side == .leading ? .trailing : .leading, 20). We flip the side so a button on the trailing side of the navigation bar gets padding applied on the leading edge.

BarButtonModifier.swift
1struct BarButtonModifier: ImageModifier {

2 var side: Edge.Set = .trailing

3

4 func body(image: Image) -> some View {

5 image

6 .resizable()

7 .scaledToFit()

8 .frame(width: 25)

9 .padding(side == .leading ? .trailing : .leading, 20)

10 }

11}

With the BarButtonModifier done, we can finally apply it to our Image:

1.navigationBarItems(trailing:

2 Button(action: { self.viewModel.showNewContact = true }) {

3 Image(systemName: "person.crop.circle.badge.plus")

4 .modifier(BarButtonModifier())

5 }

6)

Now, our button has a way larger area, making it easier to tap:

Solution: Button with bigger footprint

Consideration I have also tried to .offset the button with .padding(.horizontal, 20) instead, but the padding outside the navigationBarItems container is still getting clipped this way. If you have found a better way to deal with this issue, please let me know!

That's it! Thank you for reading this post! Feel free to reach out to me on hey@timweiss.net! I'd love to hear what you think of this post!

1. Problem2. Solution2.1. ImageModifier2.2. BarButtonModifier

Home

Swift Stuffchevron_right
Cloud Computingchevron_right