From 47d6f90dd72f0d0d93ef4b405a2ddf330cfe8a7b Mon Sep 17 00:00:00 2001 From: Simon Whitty Date: Sat, 6 Sep 2025 21:39:33 +1000 Subject: [PATCH] resizable() --- Examples/Sources/GalleryView.swift | 3 +- README.md | 18 +++-- SwiftDraw/Sources/SVG+CoreGraphics.swift | 32 +++++++-- SwiftDraw/Sources/SVGView.swift | 86 +++++++++++++++++++++--- 4 files changed, 117 insertions(+), 22 deletions(-) diff --git a/Examples/Sources/GalleryView.swift b/Examples/Sources/GalleryView.swift index b23b0e2..9894189 100644 --- a/Examples/Sources/GalleryView.swift +++ b/Examples/Sources/GalleryView.swift @@ -61,7 +61,8 @@ struct GalleryView: View { LazyVStack(spacing: 20) { ForEach(images, id: \.self) { image in SVGView(image, bundle: .samples) - .aspectRatio(contentMode: .fit) + .resizable() + .scaledToFit() .padding([.leading, .trailing], 10) } } diff --git a/README.md b/README.md index 510269c..431bd17 100644 --- a/README.md +++ b/README.md @@ -38,16 +38,22 @@ imageView.image = svg.rasterize() // 240x200 ### SwiftUI -Display an image within `SVGView`: +SVGs can be displayed within `SVGView` just like using SwiftUI's built-in `Image`: ```swift -var body: some View { - SVGView("sample.svg") - .aspectRatio(contentMode: .fit) - .padding() -} +SVGView("sample.svg") ``` +By default, SVGs are rendered at their original (intrinsic) size. To make them flexible within layouts, mark them as resizable — exactly like `Image`: + +```swift +SVGView("sample.svg") + .resizable() + .scaledToFit() +``` + +This allows the SVG to scale proportionally to fit within its container. Use `.scaledToFill()` to completely cover the container and use `.resizable(resizingMode: .tile)` to draw the SVG in repeating tiles filling the available space. + When you load by name, SVGView uses an internal cache so repeated lookups are efficient. For more predictable performance (avoiding any cache lookup or parsing), you can pass an already-created SVG instance: diff --git a/SwiftDraw/Sources/SVG+CoreGraphics.swift b/SwiftDraw/Sources/SVG+CoreGraphics.swift index 2ad87b0..3be747f 100644 --- a/SwiftDraw/Sources/SVG+CoreGraphics.swift +++ b/SwiftDraw/Sources/SVG+CoreGraphics.swift @@ -35,22 +35,44 @@ public import Foundation public extension CGContext { - func draw(_ image: SVG, in rect: CGRect? = nil) { - let defaultRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) + func draw(_ svg: SVG, in rect: CGRect? = nil) { + let defaultRect = CGRect(x: 0, y: 0, width: svg.size.width, height: svg.size.height) let renderer = CGRenderer(context: self) saveGState() if let rect = rect, rect != defaultRect { translateBy(x: rect.origin.x, y: rect.origin.y) scaleBy( - x: rect.width / image.size.width, - y: rect.height / image.size.height + x: rect.width / svg.size.width, + y: rect.height / svg.size.height ) } - renderer.perform(image.commands) + renderer.perform(svg.commands) restoreGState() } + + func draw(_ svg: SVG, in rect: CGRect, byTiling: Bool) { + guard byTiling else { + draw(svg, in: rect) + return + } + + let cols = Int(ceil(rect.size.width / svg.size.width)) + let rows = Int(ceil(rect.size.height / svg.size.height)) + + for r in 0.. Self { + var copy = self + copy.resizingMode = resizingMode + return copy + } + + private static func makeCanvas(svg: SVG, resizingMode: ResizingMode) -> some View { + Canvas( + opaque: false, + colorMode: .linear, + rendersAsynchronously: false + ) { ctx, size in + switch resizingMode { + case .tile: + ctx.draw(svg, in: CGRect(origin: .zero, size: size), byTiling: true) + case .stretch: + ctx.draw(svg, in: CGRect(origin: .zero, size: size)) + } } } } @@ -62,10 +97,41 @@ public struct SVGView: View { @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) public extension GraphicsContext { - func draw(_ image: SVG, in rect: CGRect? = nil) { + func draw(_ svg: SVG, in rect: CGRect? = nil) { + withCGContext { + $0.draw(svg, in: rect) + } + } + + func draw(_ svg: SVG, in rect: CGRect, byTiling: Bool) { withCGContext { - $0.draw(image, in: rect) + $0.draw(svg, in: rect, byTiling: byTiling) } } } + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +#Preview { + SVGView(svg: .circle) + + SVGView(svg: .circle) + .resizable(resizingMode: .stretch) + + SVGView(svg: .circle) + .resizable(resizingMode: .tile) +} + +#if DEBUG +private extension SVG { + + static var circle: SVG { + SVG(xml: """ + + + + """)! + } +} +#endif + #endif