diff --git a/SwiftDraw/Sources/CanvasNSView.swift b/SwiftDraw/Sources/CanvasNSView.swift new file mode 100644 index 0000000..897ee2b --- /dev/null +++ b/SwiftDraw/Sources/CanvasNSView.swift @@ -0,0 +1,80 @@ +// +// CanvasNSView.swift +// SwiftDraw +// +// Created by Simon Whitty on 07/9/25. +// Copyright 2025 Simon Whitty +// +// Distributed under the permissive zlib license +// Get the latest version from here: +// +// https://github.com/swhitty/SwiftDraw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#if canImport(AppKit) +import AppKit +import SwiftUI + +@available(macOS, deprecated: 12.0, message: "use SwiftUI.Canvas") +struct CanvasFallbackView: NSViewRepresentable { + + var svg: SVG + var capInsets: EdgeInsets + var resizingMode: SVGView.ResizingMode + + func makeNSView(context: Context) -> CanvasNSView { + let nsView = CanvasNSView() + nsView.wantsLayer = true + nsView.layerContentsRedrawPolicy = .duringViewResize + nsView.layer?.needsDisplayOnBoundsChange = true + return nsView + } + + func updateNSView(_ nsView: CanvasNSView, context: Context) { + nsView.svg = svg + nsView.resizeMode = resizingMode + nsView.capInsets = (capInsets.top, capInsets.leading, capInsets.bottom, capInsets.trailing) + nsView.needsDisplay = true + } +} + +final class CanvasNSView: NSView { + + var svg: SVG? + var resizeMode: SVGView.ResizingMode = .stretch + var capInsets: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) = (0, 0, 0, 0) + + override var isFlipped: Bool { true } + + override func draw(_ dirtyRect: NSRect) { + guard let svg, + let ctx = NSGraphicsContext.current?.cgContext else { return } + + ctx.draw( + svg, + in: bounds, + capInsets: capInsets, + byTiling: resizeMode == .tile + ) + } +} + +#endif diff --git a/SwiftDraw/Sources/CanvasUIView.swift b/SwiftDraw/Sources/CanvasUIView.swift new file mode 100644 index 0000000..e2c7b3b --- /dev/null +++ b/SwiftDraw/Sources/CanvasUIView.swift @@ -0,0 +1,77 @@ +// +// CanvasUIView.swift +// SwiftDraw +// +// Created by Simon Whitty on 07/9/25. +// Copyright 2025 Simon Whitty +// +// Distributed under the permissive zlib license +// Get the latest version from here: +// +// https://github.com/swhitty/SwiftDraw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// + +#if canImport(UIKit) +import UIKit +import SwiftUI + +@available(iOS, deprecated: 15.0, message: "use SwiftUI.Canvas") +struct CanvasFallbackView: UIViewRepresentable { + + var svg: SVG + var capInsets: EdgeInsets + var resizingMode: SVGView.ResizingMode + + func makeUIView(context: Context) -> CanvasUIView { + let uiView = CanvasUIView() + uiView.isOpaque = false + uiView.contentMode = .redraw + return uiView + } + + func updateUIView(_ uiView: CanvasUIView, context: Context) { + uiView.svg = svg + uiView.resizeMode = resizingMode + uiView.capInsets = (capInsets.top, capInsets.leading, capInsets.bottom, capInsets.trailing) + uiView.setNeedsDisplay() + } +} + +final class CanvasUIView: UIView { + + var svg: SVG? + var resizeMode: SVGView.ResizingMode = .stretch + var capInsets: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) = (0, 0, 0, 0) + + override func draw(_ rect: CGRect) { + guard let svg, + let ctx = UIGraphicsGetCurrentContext() else { return } + + ctx.draw( + svg, + in: rect, + capInsets: capInsets, + byTiling: resizeMode == .tile + ) + } +} + +#endif diff --git a/SwiftDraw/Sources/SVGView.swift b/SwiftDraw/Sources/SVGView.swift index 92a2d2a..4d63438 100644 --- a/SwiftDraw/Sources/SVGView.swift +++ b/SwiftDraw/Sources/SVGView.swift @@ -32,7 +32,6 @@ #if canImport(SwiftUI) public import SwiftUI -@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) public struct SVGView: View { public init(_ name: String, bundle: Bundle = .main) { @@ -83,17 +82,26 @@ public struct SVGView: View { return copy } + @ViewBuilder private static func makeCanvas(svg: SVG, capInsets: EdgeInsets = .init(), resizingMode: ResizingMode) -> some View { - Canvas( - opaque: false, - colorMode: .linear, - rendersAsynchronously: false - ) { ctx, size in - ctx.draw( - svg, - in: CGRect(origin: .zero, size: size), + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) { + Canvas( + opaque: false, + colorMode: .linear, + rendersAsynchronously: false + ) { ctx, size in + ctx.draw( + svg, + in: CGRect(origin: .zero, size: size), + capInsets: capInsets, + byTiling: resizingMode == .tile + ) + } + } else { + CanvasFallbackView( + svg: svg, capInsets: capInsets, - byTiling: resizingMode == .tile + resizingMode: resizingMode ) } }