Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Does the SpriteView of an SKScene have layers? Unable to get magnifying glass view to work with scene.

I'm trying to make a magnifying glass that shows up when the user presses a button and follows the user's finger as it's dragged across the screen.

I came across a UIKit-based solution (https://github.com/niczyja/MagnifyingGlass-Swift), but when implemented in my SKScene, only the crosshairs are shown. Through experimentation I've found that magnifiedView?.layer.render(in: context) in:

   public override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        context.translateBy(x: radius, y: radius)
        context.scaleBy(x: scale, y: scale)
        context.translateBy(x: -magnifiedPoint.x, y: -magnifiedPoint.y)
        
        removeFromSuperview() 
        magnifiedView?.layer.render(in: context) 
        magnifiedView?.addSubview(self)
    }

can be removed without altering the situation, suggesting that line is not working as it should. But this is where I hit a brick wall. The view below is shown but not offset or magnified, and any attempt to add something to context results in a black magnifying glass.

Does anyone know why this is? I don't think it's an issue with the code, so I'm suspecting its something specific to SpriteKit or SKScene, likely related to how CALayers work.

Any pointers would be greatly appreciated.

.

.

.

Full code below:

import UIKit

public class MagnifyingGlassView: UIView {
    public weak var magnifiedView: UIView? = nil {
        didSet {
            removeFromSuperview()
            magnifiedView?.addSubview(self)
        }
    }
    
    public var magnifiedPoint: CGPoint = .zero {
        didSet {
            center = .init(x: magnifiedPoint.x + offset.x, y: magnifiedPoint.y + offset.y)
        }
    }
    
    public var offset: CGPoint = .zero
    
    public var radius: CGFloat = 50 {
        didSet {
            frame = .init(origin: frame.origin, size: .init(width: radius * 2, height: radius * 2))
            layer.cornerRadius = radius
            crosshair.path = crosshairPath(for: radius)
        }
    }
    
    public var scale: CGFloat = 2
    
    public var borderColor: UIColor = .lightGray {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }
    
    public var borderWidth: CGFloat = 3 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }
    
    public var showsCrosshair = true {
        didSet {
            crosshair.isHidden = !showsCrosshair
        }
    }
    
    public var crosshairColor: UIColor = .lightGray {
        didSet {
            crosshair.strokeColor = crosshairColor.cgColor
        }
    }
    
    public var crosshairWidth: CGFloat = 5 {
        didSet {
            crosshair.lineWidth = crosshairWidth
        }
    }
    
    private let crosshair: CAShapeLayer = CAShapeLayer()
    
    public convenience init(offset: CGPoint = .zero, radius: CGFloat = 50, scale: CGFloat = 2, borderColor: UIColor = .lightGray, borderWidth: CGFloat = 3, showsCrosshair: Bool = true, crosshairColor: UIColor = .lightGray, crosshairWidth: CGFloat = 0.5) {
        self.init(frame: .zero)
        layer.masksToBounds = true
        layer.addSublayer(crosshair)
        
        defer {
            self.offset = offset
            self.radius = radius
            self.scale = scale
            self.borderColor = borderColor
            self.borderWidth = borderWidth
            self.showsCrosshair = showsCrosshair
            self.crosshairColor = crosshairColor
            self.crosshairWidth = crosshairWidth
        }
    }
    
    public func magnify(at point: CGPoint) {
        guard magnifiedView != nil else { return }
        magnifiedPoint = point
        layer.setNeedsDisplay()
    }
    
    private func crosshairPath(for radius: CGFloat) -> CGPath {
        let path = CGMutablePath()
        path.move(to: .init(x: radius, y: 0))
        path.addLine(to: .init(x: radius, y: bounds.height))
        path.move(to: .init(x: 0, y: radius))
        path.addLine(to: .init(x: bounds.width, y: radius))
        return path
    }
    
    public override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        context.translateBy(x: radius, y: radius)
        context.scaleBy(x: scale, y: scale)
        context.translateBy(x: -magnifiedPoint.x, y: -magnifiedPoint.y)
        
        removeFromSuperview() 
        magnifiedView?.layer.render(in: context) 
        //If above disabled, no change
        //Possible that nothing's being rendered into context
        //Could it be that SKScene view has no layer?

        magnifiedView?.addSubview(self)
    }
}
Does the SpriteView of an SKScene have layers? Unable to get magnifying glass view to work with scene.
 
 
Q