import SwiftUI struct SmartFramer: View { var radius: CGFloat = 10 var height: CGFloat = 56 @State private var breathing = false var body: some View { GeometryReader { geo in ZStack { Color.black.opacity(0.32) .mask( Rectangle() .overlay( RoundedRectangle(cornerRadius: radius, style: .continuous) .frame(height: height) .padding(.horizontal, geo.size.width * 0.08) .blendMode(.destinationOut) ) .compositingGroup() ) RoundedRectangle(cornerRadius: radius + 4, style: .continuous) .stroke(Color(red: 0.95, green: 0.78, blue: 0.45), lineWidth: 1.5) .shadow(color: Color(red: 0.95, green: 0.78, blue: 0.45).opacity(0.5), radius: 8) .frame(height: height + 8) .padding(.horizontal, geo.size.width * 0.08 - 4) .opacity(breathing ? 1 : 0.35) cornerMarks(in: geo.size) } .frame(width: geo.size.width, height: geo.size.height) .onAppear { withAnimation(.easeInOut(duration: 2.2).repeatForever(autoreverses: true)) { breathing.toggle() } } } } private func cornerMarks(in size: CGSize) -> some View { let inset = size.width * 0.08 return ZStack { ForEach(Corner.allCases, id: \.self) { corner in CornerMark(corner: corner, radius: radius) .frame(width: 18, height: 18) .position(corner.position(in: size, inset: inset, frameHeight: height)) } } } } private enum Corner: CaseIterable { case tl, tr, bl, br func position(in size: CGSize, inset: CGFloat, frameHeight: CGFloat) -> CGPoint { let centerY = size.height / 2 let top = centerY - frameHeight / 2 let bottom = centerY + frameHeight / 2 switch self { case .tl: return CGPoint(x: inset, y: top) case .tr: return CGPoint(x: size.width - inset, y: top) case .bl: return CGPoint(x: inset, y: bottom) case .br: return CGPoint(x: size.width - inset, y: bottom) } } } private struct CornerMark: View { let corner: Corner let radius: CGFloat var body: some View { Path { p in let r = min(radius, 8) switch corner { case .tl: p.move(to: CGPoint(x: 0, y: 18)) p.addLine(to: CGPoint(x: 0, y: r)) p.addQuadCurve(to: CGPoint(x: r, y: 0), control: CGPoint(x: 0, y: 0)) p.addLine(to: CGPoint(x: 18, y: 0)) case .tr: p.move(to: CGPoint(x: 0, y: 0)) p.addLine(to: CGPoint(x: 18 - r, y: 0)) p.addQuadCurve(to: CGPoint(x: 18, y: r), control: CGPoint(x: 18, y: 0)) p.addLine(to: CGPoint(x: 18, y: 18)) case .bl: p.move(to: CGPoint(x: 0, y: 0)) p.addLine(to: CGPoint(x: 0, y: 18 - r)) p.addQuadCurve(to: CGPoint(x: r, y: 18), control: CGPoint(x: 0, y: 18)) p.addLine(to: CGPoint(x: 18, y: 18)) case .br: p.move(to: CGPoint(x: 0, y: 18)) p.addLine(to: CGPoint(x: 18 - r, y: 18)) p.addQuadCurve(to: CGPoint(x: 18, y: 18 - r), control: CGPoint(x: 18, y: 18)) p.addLine(to: CGPoint(x: 18, y: 0)) } } .stroke(Tj.Palette.paper, style: StrokeStyle(lineWidth: 2.5, lineCap: .round)) } }