🦋

SwiftUI: Buttonを中身がすっぽり収まる正方形に矯正する

2023/09/15に公開

Buttonの中にImageでSF Symbolsのアイコンを入れてボタンを作ることあると思います。
この場合、SF SymbolのサイズによってButtonのサイズが変化してしまうことよくあると思います。


SF SymbolsのサイズによってButtonのサイズが変化する

HStack {
    Button {
    } label: {
        Image(systemName: "chevron.right")
            .font(.title2)
    }
    .border(Color.red)

    Button {
    } label: {
        Image(systemName: "plus")
            .font(.title2)
    }
    .border(Color.red)

    Button {
    } label: {
        Image(systemName: "rectangle.and.pencil.and.ellipsis")
            .font(.title2)
    }
    .border(Color.red)
}

そんな時はLayoutButtonStyleを自作してButtonにModifierをつければ正方形に矯正できます!

struct SquareButtonContainer: Layout {
    private func maxLength(subviews: Subviews) -> CGFloat {
        return subviews.map { subview -> CGFloat in
            let size = subview.sizeThatFits(.unspecified)
            return max(size.width, size.height)
        }
        .max() ?? .zero
    }

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let length = maxLength(subviews: subviews)
        return CGSize(width: length, height: length)
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        guard subviews.count == 1 else { return }

        subviews.forEach { subview in
            subview.place(at: CGPoint(x: bounds.midX, y: bounds.midY),
                          anchor: .center,
                          proposal: proposal)
        }
    }
}

struct SquareButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        SquareButtonContainer {
            configuration.label
                .fixedSize()
                .foregroundColor(.accentColor)
                .opacity(configuration.isPressed ? 0.3 : 1.0)
        }
    }
}

extension ButtonStyle where Self == SquareButtonStyle {
    static var square: SquareButtonStyle {
        return SquareButtonStyle()
    }
}

からの

HStack {
    Button {
    } label: {
        Image(systemName: "chevron.right")
            .font(.title2)
    }
+   .buttonStyle(.square)
    .border(Color.red)

    Button {
    } label: {
        Image(systemName: "plus")
            .font(.title2)
    }
+   .buttonStyle(.square)
    .border(Color.red)

    Button {
    } label: {
        Image(systemName: "rectangle.and.pencil.and.ellipsis")
            .font(.title2)
    }
+   .buttonStyle(.square)
    .border(Color.red)
}

で、


それぞれちょうどSF Symbolsが収まる正方形になっている

当然、中身がTextの時も大丈夫!

Button {
} label: {
    Text("Hello World")
        .font(.title2)
}
.buttonStyle(.square)
.border(Color.red)


文字列がちょうど収まる正方形になっている

Discussion