SwiftUI の ScrollView を CustomShape で clip すると内部の View まで clip される
下記のように ScrollView に対して clippedShae(_:)
で CustomShape を適用して clip すると、 ScrollView 内の View まで clip されてしまって View が途中で切れてしまう。
Rectangle
等の組み込みの Shape や、List に対してであれば問題は生じない。
Stack Overflow にも同様の投稿があって、自分は確認していないけど iOS 13 では再現しないとのこと。
CustomShape で clip | clippedShae(_:) 無し(正常パターン) |
---|---|
struct ExampleView: View { struct CustomShape: Shape { func path(in rect: CGRect) -> Path { let bezierPath = UIBezierPath( roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 20.0, height: 20.0) ) let cgPath = bezierPath.cgPath return Path(cgPath) } } var body: some View { ScrollView { ForEach(0..<100) { element in Text("\(element)") } .frame(maxWidth: .infinity) .background(Color.blue) .padding() } .background(Color.green) .clipShape(CustomShape()) // ScrollView に対して CustomShape で clip } }
これは、CustomShape 内の Path の init によっても再現の有無が異なっていて、各 init における今回の現象の再現有無は下記の通りだった。 もしかしたら callback や CGPath を引数にとる init は CGPath の作り方によっても挙動が異なるかも。
- OK
init(_ rect: CGRect)
,init(roundedRect rect: CGRect, cornerSize: CGSize, style: RoundedCornerStyle = .circular)
init(roundedRect rect: CGRect, cornerRadius: CGFloat, style: RoundedCornerStyle = .circular)
- NG
init(_ callback: (inout Path) -> ())
init(_ path: CGPath)
init(_ path: CGMutablePath)
init(ellipseIn rect: CGRect)
解決法
ZStack 等を使って ScrollView の背面の View をおいて、ScrollView は透明にして背面の View に CustomShape を適用することで解決した。 ただ、この方法は View のレイアウト次第では使えないかも。
var body: some View { ZStack { Color.green .clipShape(CustomShape()) // 背面の View に CustomShape を適用 ScrollView { ForEach(0..<100) { element in Text("\(element)") } .frame(maxWidth: .infinity) .background(Color.blue) .padding() } } }