1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SwiftUI】ScrollViewで角丸ボーダーが見切れる問題は.strokeBorderで解決する

Last updated at Posted at 2025-09-17

SwiftUIで角丸のボーダーを実装する際、ScrollViewなどで角丸ボーダーViewを呼び出すとボーダーが見切れてしまう問題に遭遇したことはないでしょうか。

角丸ボーダーの基本的な実装

まず、SwiftUIで角丸のボーダーを実装します。
このコードで角丸ボーダーを作ることができます。

.overlay(RoundedRectangle(cornerRadius: 8).stroke(.red, lineWidth: 1))

image.png

うーん!ちゃんと作成できてそうですね🎉

角丸ボーダーの実装については、こちらの記事で詳しく解説されています!

しかし、これをScrollViewやLazyVGridなどで呼び出すとレイアウト崩れが起きてしまうのです。

ボーダーViewが見切れてしまう

早速ボーダーViewをScrollView内で呼び出してみます。

struct ContentView: View {
    var body: some View {
        ScrollView(.horizontal) {
            HStack(spacing: 16) {
                ForEach(0..<10) { _ in
                    ListContentView()
                }
            }
        }
        .scrollIndicators(.hidden)
    }
}

private struct ListContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding(8)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(.red, lineWidth: 1)
            )
    }
}

上記のコードを描画したものがこちらです。

スクリーンショット 2025-09-17 22.12.55.png

少し分かりにくいかもですが、上下のボーダーが見切れてしまっているのが伝わるでしょうか。

解決方法

結論から書きますが.strokeの代わりに.strokeBorderモディファイアを利用することで回避できます。

private struct ListContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding(8)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .strokeBorder(.red, lineWidth: 1) // ここを置き換える
            )
    }
}

スクリーンショット 2025-09-17 22.21.27.png
見切れていた部分が綺麗に表示されていそうです❤️‍🔥

仕組みを理解する

Appleの公式ドキュメントによると、.strokeBorderは以下のような仕組みで動作します。

Returns a view that is the result of insetting self by style.lineWidth / 2, stroking the resulting shape with style, and then filling with the foreground color.

日本語訳:
style.lineWidth / 2分だけselfを内側に縮小し、その結果得られた図形をstyleでストロークし、フォアグラウンドカラーで塗りつぶしたビューを返します。

なるほど、つまり↓ということだと思います。

  1. 例えば半径100の円があるとする
  2. この半径98の円に線幅4の線を描くとする
  3. style.lineWidth / 2分だけselfを内側に縮小 = 円を4 ÷ 2 = 2だけ内側に縮小 → 半径98の円になる
  4. 結果、線は半径内側2pt(97,98)と外側2pt(99,100)の範囲に描画されるので、完全に元の円の内側に収まる!☝️

まとめ

  • .stroke()は境界線を中心として内外に描画するため、はみ出す可能性がある
  • .strokeBorder()は事前に図形を縮小してから描画するため、完全に内側に収まる
  • ScrollViewなどでボーダーが見切れる場合は.strokeBorder()を使用する

余談

この問題は下記のようにボーダー分の.paddingを追加で付与することでも解決はできますが、.strokeBorderの方がよりシンプルで直感的な解決方法と言えるでしょう。

private struct ListContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding(8)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(.red, lineWidth: 1)
            )
            .padding(1) // ボーダーの分余白
    }
}
1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?