Resize a custom Image icon in SwiftUI’s Label

Labels were introduced into SwiftUI as part of the 2020 release of the framework. They allow images to be shown along side text without extra overhead. As mentioned in the documentation they can be text or image only by applying the offered styles. Labels can further be customized by extending the LabelStyle protocol.

When I built the Meal Plan screen of Decisive Wife with UIKit I had the rows displaying the preparation and cook times, but had ‘P’ and ‘C’ as the labels.

Old+Dinner+Idea+row+style.jpg

Since I’m redoing my entire Meal Plan in SwiftUI I figured I can try to enhance the block with some icons. With just the basic Label view it revealed a bug with the current implementation where the icons were not lining up with the text.

VStack(spacing: 5) {
    dinnerIdea.prepTime.map{
        Label($0, image: "icons8-chef-knife")
    }
    dinnerIdea.cookTime.map{
        Label($0, image: "icons8-kitchen")
    }
}
.frame(alignment: .trailing)
1st look.png

A quick Google search produced a simple solution to the alignment issue by creating a custom label style. This solution uses the HStack to wrap the icon and title properties of the label to align them on the same line.

struct CaptionLabelStyle : LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon
            configuration.title
        }
    }
}
...
VStack(spacing: 5) {
    dinnerIdea.prepTime.map{
        Label($0, image: "icons8-chef-knife")
            .labelStyle(CaptionLabelStyle())
    }
    dinnerIdea.cookTime.map{
        Label($0, image: "icons8-kitchen")
            .labelStyle(CaptionLabelStyle())
    }
}
.frame(alignment: .trailing)
Screen Shot 2020-07-26 at 2.11.38 PM.png

Once the alignment issue was fixed, I wanted to make the entire block smaller since it is not the focus of the row. Unfortunately changing the font to caption only effected the text and not the icons.

struct CaptionLabelStyle : LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon
            configuration.title
        }
        .font(.caption)
    }
}
3rd look.png

My next task was to shrink the images to be the approximately the same size of the text. I found out I could apply functions such as .frame() to the configuration properties themselves. With this knowledge I searched through all the possible functions until I found one that worked, which turned out to be .scaleEffect(_:anchor:). I added an extra frame property to further reduce the area the icon and text occupies.

struct CaptionLabelStyle : LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.icon
                .scaleEffect(0.8, anchor: .center)
                .frame(width: 20, height: 20, alignment: .center)
            configuration.title
        }
        .font(.caption)
    }
}
Final Look.png

As of writing this I could not find a better way of solving my problem. With SwiftUI being adopted by more people each day I’m sure someone could come up with a more elegant solution.

Thanks for reading and I hope I was able to help you solve your problems.

Icons were taken from Icons8.

Previous
Previous

Changing the Image Icon Color in a Label