Reputation: 26223
Is there a way in SwiftUI to union two shapes so that they cast a unified shadow. I have tried various combinations and modifiers but don't seem to be able to achieve the look I am after. Any insight would be much appreciated.
struct CardView: View {
var body: some View {
Group {
RoundedRectangle(cornerRadius: 20)
.fill(Color.orange)
.frame(width: 200, height: 250)
.zIndex(0)
Circle()
.trim(from: 0.5, to: 1)
.fill(Color.orange)
.frame(width: 100, height: 100)
.offset(y: -125)
.zIndex(1)
}.shadow(color: Color.black, radius: 10, x: 0, y: 0)
}
}
This is what I get, and what I am after ...
NOTE: zIndex
was just something I tried, i.e. both shapes having the same zIndex
etc. its also a quick way to reorder things without having to move the shapes within the container.
Upvotes: 10
Views: 1992
Reputation: 2940
Draw the shape:
struct CustomShape: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
let width = rect.size.width
let height = rect.size.height
path.move(to: CGPoint(x: 0.89995*width, y: 0.16667*height))
path.addLine(to: CGPoint(x: 0.75*width, y: 0.16667*height))
path.addCurve(to: CGPoint(x: 0.5*width, y: 0), control1: CGPoint(x: 0.75*width, y: 0.07463*height), control2: CGPoint(x: 0.63805*width, y: 0))
path.addCurve(to: CGPoint(x: 0.25*width, y: 0.16667*height), control1: CGPoint(x: 0.36195*width, y: 0), control2: CGPoint(x: 0.25*width, y: 0.07463*height))
path.addLine(to: CGPoint(x: 0.10005*width, y: 0.16667*height))
path.addCurve(to: CGPoint(x: 0, y: 0.23337*height), control1: CGPoint(x: 0.0448*width, y: 0.16667*height), control2: CGPoint(x: 0, y: 0.19653*height))
path.addLine(to: CGPoint(x: 0, y: 0.93333*height))
path.addCurve(to: CGPoint(x: 0.10005*width, y: 1.00003*height), control1: CGPoint(x: 0, y: 0.97017*height), control2: CGPoint(x: 0.0448*width, y: 1.00003*height))
path.addLine(to: CGPoint(x: 0.89995*width, y: 1.00003*height))
path.addCurve(to: CGPoint(x: width, y: 0.93333*height), control1: CGPoint(x: 0.9552*width, y: 1.00003*height), control2: CGPoint(x: width, y: 0.97017*height))
path.addLine(to: CGPoint(x: width, y: 0.23337*height))
path.addCurve(to: CGPoint(x: 0.89995*width, y: 0.16667*height), control1: CGPoint(x: width, y: 0.19653*height), control2: CGPoint(x: 0.9552*width, y: 0.16667*height))
path.closeSubpath()
return path
}
}
And custom color, shadow and size in your parent view:
CustomShape()
.fill(.orange)
.shadow(color: Color.primary, radius: 10, x: 0, y: 0)
.frame(width: 300, height: 400)
Upvotes: 1
Reputation: 257663
Here is possible solution. Tested with Xcode 11.4 / iOS 13.4
struct CardView: View {
var body: some View {
VStack(spacing: 0) {
Circle()
.trim(from: 0.5, to: 1)
.fill(Color.orange)
.frame(width: 100, height: 100)
.offset(x: 0, y: 50)
RoundedRectangle(cornerRadius: 20)
.fill(Color.orange)
.frame(width: 200, height: 250)
}
.compositingGroup()
.shadow(color: Color.primary, radius: 10, x: 0, y: 0)
}
}
Upvotes: 21
Reputation: 7707
I think the correct answer is @Asperi's .compositingGroup
as documented here (https://developer.apple.com/documentation/swiftui/group/3284789-compositinggroup) but I'd like to leave a different working version because there are some use cases where you may not be able or want to use .compositingGroup
.
So let's look at .background
, you can copy and paste the code to see in the simulator:
import SwiftUI
struct AnyShapedView: View {
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 20)
.fill(Color.orange)
.frame(width: 200, height: 250)
.zIndex(0)
Circle()
.trim(from: 0.5, to: 1)
.fill(Color.orange)
.frame(width: 100, height: 100)
.offset(y: -125)
.zIndex(1)
}
}
}
struct ContentView: View {
var body: some View {
AnyShapedView()
.background(
AnyShapedView()
.shadow(color: Color.black, radius: 10, x: 0, y: 0)
)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The output:
The version above is not the best practice, but nevertheless, try to understand how shapes and cropping work, as in some use cases it might come useful.
Upvotes: 5