while working with a swiftUI playground, I came across an issue with the rendering of the view.shadow() function. where the shadow may be offset from the object it is shadowing by the distance from that object to the containing frame origin. (top left)
the following is an example showing the issue. it can be run as a macOS playground or an iOS playground, the problem is similar. but it may be more visible as an iOS playground.
when I run this, there should be 3 frames, stacked vertically, with a dot and a line rendered in each one. the shadow of the line is drawn offset down and to the right, the equivalent distance as the closest point to the top left origin. for the dot, the effect only seems to occur in the middle of the two frames. I had other graphics in the project I was working on, but they all had elements in the top left corner, therefore they rendered correctly, so I've removed them for simplicity.
the following is an example showing the issue. it can be run as a macOS playground or an iOS playground, the problem is similar. but it may be more visible as an iOS playground.
when I run this, there should be 3 frames, stacked vertically, with a dot and a line rendered in each one. the shadow of the line is drawn offset down and to the right, the equivalent distance as the closest point to the top left origin. for the dot, the effect only seems to occur in the middle of the two frames. I had other graphics in the project I was working on, but they all had elements in the top left corner, therefore they rendered correctly, so I've removed them for simplicity.
Code Block import SwiftUI import PlaygroundSupport struct BaseStart: Shape { var size = 10 var startPoint:InitialBase = .none init(_ initial:InitialBase) { startPoint = initial } func path(in rect: CGRect) -> Path { let size = min(rect.size.width, rect.size.height) * 0.15 var x: CGFloat var y: CGFloat var path = Path() switch startPoint { case .firstBase: x = 3*(rect.size.width/4) y = 3*(rect.size.height/4) case .secondBase : x = 3*(rect.size.width/4) y = (rect.size.height/4) case .thirdBase : x = (rect.size.width/4) y = (rect.size.height/4) default: return path } path.addEllipse(in: CGRect(x: x-size/2, y: y-size/2, width: size, height: size)) return path } } struct BaseRunner: Shape { var runnerStartingBase:InitialBase var advancedBy=0 init(runnerStart:InitialBase, advancedBy:Int){ runnerStartingBase = runnerStart self.advancedBy = advancedBy } func path(in rect:CGRect) -> Path { let width = rect.size.width/4 let height = rect.size.height/4 let firstBase = CGPoint(x: width*3, y: height*3) let secondBase = CGPoint(x: width*3, y: height) let thirdBase = CGPoint(x: width, y: height) let home = CGPoint(x: width, y: height*3) var path = Path() //uncomment the following 2 lines to establish a drawn pixel in the top left corner that "fixes" the problem // path.move(to: CGPoint(x: 0, y: 0)) // path.addLine(to: CGPoint(x:1, y: 1)) switch runnerStartingBase { case .firstBase: path.move(to: firstBase) if advancedBy>0 {path.addLine(to: secondBase)} if advancedBy>1 {path.addLine(to: thirdBase)} if advancedBy>2 {path.addLine(to: home)} case .secondBase: path.move(to: secondBase) if advancedBy>0 {path.addLine(to: thirdBase)} if advancedBy>1 {path.addLine(to: home)} case .thirdBase: path.move(to: thirdBase) if advancedBy>0 {path.addLine(to: home)} case .none: let _ = 0 } return path } } enum InitialBase { case none case firstBase case secondBase case thirdBase } public struct BatterView : View { init(_ start:InitialBase, _ runnerPosition:Int) { initialBase = start self.runnerPosition = runnerPosition } var initialBase:InitialBase = .none var runnerPosition:Int mutating func advanceRunner() { self.runnerPosition += 1 } public var body: some View { ZStack{ BaseRunner(runnerStart: initialBase, advancedBy: runnerPosition) .stroke(lineWidth:3) .foregroundColor(.black) .shadow(color: .green, radius: 1, x: 0.2, y: 0) BaseStart(initialBase) .fill() .foregroundColor(.black) .shadow(color: .green, radius: 1, x: 2, y: 2) //should be noted that the problem exists whether the shadow is done on the individual //shapes or the stack as a whole. //uncommenting the following line and commenting out the above shadows will have similar effect. }//.shadow(color: .green, radius: 2, x: 1, y: 1) } } struct Preview: View { @State private var position = 1 func advanceRunner(){ position += 1 update.toggle() } @State var update = false var body: some View { VStack (alignment: .center, spacing: 1){ BatterView(.thirdBase, position) .frame(minWidth: 44, idealWidth: 88, maxWidth: 88, minHeight: 44, idealHeight: 88, maxHeight: 88, alignment: .center) BatterView(.secondBase, position) .frame(minWidth: 44, idealWidth: 88, maxWidth: 88, minHeight: 44, idealHeight: 88, maxHeight: 88, alignment: .center) .environment(\.colorScheme, .light) BatterView(.firstBase, position) .frame(minWidth: 44, idealWidth: 88, maxWidth: 88, minHeight: 44, idealHeight: 88, maxHeight: 88, alignment: .center) .environment(\.colorScheme, .light) Button(action: advanceRunner){ Text("run!") } } } } PlaygroundPage.current.setLiveView(Preview() )