import SwiftUI /// Custom SwiftUI Shape that draws the characteristic MacBook notch outline. /// Both top and bottom corner radii are animatable, enabling smooth transitions /// between the compact closed state and the expanded open state. /// /// The shape uses quadratic Bezier curves to produce the distinctive /// top-edge cut-ins of the closed notch, and a clean rounded-bottom /// rectangle when open (topCornerRadius approaches 0). struct NotchShape: Shape { /// Radius applied to the top-left and top-right transitions where the notch /// curves away from the screen edge. When close to 0, the top corners become /// sharp and the shape is a rectangle with rounded bottom corners. var topCornerRadius: CGFloat /// Radius applied to the bottom-left and bottom-right inner corners. var bottomCornerRadius: CGFloat // MARK: - Animatable conformance var animatableData: AnimatablePair { get { AnimatablePair(topCornerRadius, bottomCornerRadius) } set { topCornerRadius = newValue.first bottomCornerRadius = newValue.second } } // MARK: - Path func path(in rect: CGRect) -> Path { var path = Path() let minX = rect.minX let maxX = rect.maxX let minY = rect.minY let maxY = rect.maxY let width = rect.width let height = rect.height let topR = min(topCornerRadius, width / 4, height / 2) let botR = min(bottomCornerRadius, width / 4, height / 2) // Start at the top-left corner of the rect path.move(to: CGPoint(x: minX, y: minY)) if topR > 0.5 { // Leave the screen edge horizontally, then turn into the side wall. path.addQuadCurve( to: CGPoint(x: minX + topR, y: minY + topR), control: CGPoint(x: minX + topR, y: minY) ) } else { path.addLine(to: CGPoint(x: minX, y: minY)) } // Left edge down to bottom-left corner area path.addLine(to: CGPoint(x: minX + topR, y: maxY - botR)) // Bottom-left inner corner path.addQuadCurve( to: CGPoint(x: minX + topR + botR, y: maxY), control: CGPoint(x: minX + topR, y: maxY) ) // Bottom edge across path.addLine(to: CGPoint(x: maxX - topR - botR, y: maxY)) // Bottom-right inner corner path.addQuadCurve( to: CGPoint(x: maxX - topR, y: maxY - botR), control: CGPoint(x: maxX - topR, y: maxY) ) // Right edge up to the top-right transition path.addLine(to: CGPoint(x: maxX - topR, y: minY + topR)) if topR > 0.5 { // Mirror the top-left transition. path.addQuadCurve( to: CGPoint(x: maxX, y: minY), control: CGPoint(x: maxX - topR, y: minY) ) } else { path.addLine(to: CGPoint(x: maxX, y: minY)) } path.closeSubpath() return path } } // MARK: - Convenience initializers extension NotchShape { /// Closed-state shape with tight corner radii that mimic the physical notch. static var closed: NotchShape { NotchShape(topCornerRadius: 6, bottomCornerRadius: 14) } /// Open-state shape: no top-edge cut-ins, just rounded bottom corners. /// topCornerRadius is near-zero so the top becomes effectively flat and the panel /// extends flush to the top edge of the screen. static var opened: NotchShape { NotchShape(topCornerRadius: 0, bottomCornerRadius: 24) } }