Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
Initial Commit
  • Loading branch information
Matt54 committed Feb 27, 2022
1 parent f3b131f commit e855eeb
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 8 deletions.
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import PackageDescription

let package = Package(
name: "ResizableVector",
platforms: [
.macOS("11.0"), .iOS(.v13), .tvOS(.v13)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# ResizableVector

A description of this package.
A cross-platform SwiftUI View that resizes images using Cocoa or UIKit.

This solves an existing SwiftUI Image View issue where vector images have artifacts when significantly scaled.

Link to related issues:
https://stackoverflow.com/questions/61164005/why-do-pdfs-resized-in-swiftui-getting-sharp-edges
https://stackoverflow.com/questions/57969823/xcode-11-pdf-image-assets-preserve-vector-data-not-working-in-swiftui

## Installation

To add ResizableVector to your Xcode project, use the Swift Package Manager

#### Swift Package Manager

- File > Swift Packages > Add Package Dependency
- Add `https://github.com/Matt54/ResizableVector.git`

### Developments

Pull requests are welcome :)
6 changes: 6 additions & 0 deletions Sources/ResizableVector/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "example.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
adapted from work by Erica Sadun found here:
https://gist.github.com/erica/ec3e2a4a8526e3fc3ba1fc95a0d53083#file-crossplatformdefines-swift
*/

// MARK: Cocoa

#if os(OSX)
import Cocoa

typealias GraphicsImageRenderer = MacGraphicsImageRenderer
typealias GraphicsImageRendererFormat = MacGraphicsImageRendererFormat
typealias GraphicsImageRendererContext = MacGraphicsImageRendererContext

class MacGraphicsImageRendererFormat: NSObject {
var opaque: Bool = false
var prefersExtendedRange: Bool = false
var scale: CGFloat = 2.0
var bounds: CGRect = .zero
}

class MacGraphicsImageRendererContext: NSObject {
var format: GraphicsImageRendererFormat
override init() {
self.format = GraphicsImageRendererFormat()
super.init()
}
}

class MacGraphicsImageRenderer: NSObject {
let format: GraphicsImageRendererFormat
let bounds: CGRect

init(bounds: CGRect, format: GraphicsImageRendererFormat) {
(self.bounds, self.format) = (bounds, format)
self.format.bounds = self.bounds
super.init()
}

convenience init(size: CGSize) {
self.init(bounds: CGRect(origin: .zero, size: size), format: GraphicsImageRendererFormat())
}

func image(actions: @escaping (GraphicsImageRendererContext) -> Void) -> NSImage {
let image = NSImage(size: format.bounds.size, flipped: false) {
(_: NSRect) -> Bool in

let imageContext = GraphicsImageRendererContext()
imageContext.format = self.format
actions(imageContext)

return true
}
return image
}
}

// MARK: UIKit

#else
import UIKit

typealias GraphicsImageRenderer = UIGraphicsImageRenderer
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// MakeImageAbstractor.swift
// Created by Matt Pfeiffer on 2/27/22.

import SwiftUI

class MakeImageAbstractor {
static func makeResizedImage(name: String, bundle: Bundle? = nil, frame: CGSize, keepAspectRatio: Bool) -> Image? {
guard let platformImage = PlatformImage.makePlatformImage(named: name, in: bundle) else { return nil }
let resizeTo = keepAspectRatio ? MakeImageAbstractor.getSizeWithAspectRatio(image: platformImage, newSize: frame) : frame
return Image(platformImage: platformImage.resized(to: resizeTo))
}

fileprivate static func getSizeWithAspectRatio(image: PlatformImage, newSize: CGSize) -> CGSize {
var scaledSize = newSize
var scaleFactor: CGFloat
if image.size.width > image.size.height {
scaleFactor = image.size.width / image.size.height
scaledSize.width = newSize.height
scaledSize.height = newSize.height / scaleFactor
} else {
scaleFactor = image.size.height / image.size.width
scaledSize.height = newSize.width
scaledSize.width = newSize.width / scaleFactor
}
return scaledSize
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// PlatformImage.swift
// Created by Matt Pfeiffer on 2/27/22.

import SwiftUI

extension PlatformImage {
func resized(to size: CGSize) -> PlatformImage {
return GraphicsImageRenderer(size: size).image { _ in
self.draw(in: CGRect(origin: .zero, size: size))
}
}
}

// MARK: Cocoa

#if os(OSX)
import Cocoa

typealias PlatformImage = NSImage

extension PlatformImage {
static func makePlatformImage(named: String, in bundle: Bundle?) -> PlatformImage? {
if let bundle = bundle {
return bundle.image(forResource: named)
} else {
return PlatformImage(named: named)
}
}
}

extension Image {
init(platformImage: PlatformImage) {
self.init(nsImage: platformImage)
}
}

// MARK: UIKit

#else
import UIKit

typealias PlatformImage = UIImage

extension PlatformImage {
static func makePlatformImage(named: String, in bundle: Bundle?) -> PlatformImage? {
return PlatformImage(named: named, in: bundle, with: nil)
}
}

extension Image {
init(platformImage: PlatformImage) {
self.init(uiImage: platformImage)
}
}
#endif
84 changes: 81 additions & 3 deletions Sources/ResizableVector/ResizableVector.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,84 @@
public struct ResizableVector {
public private(set) var text = "Hello, World!"
// ResizableVector.swift
// Created by Matt Pfeiffer on 2/27/22.

public init() {
import SwiftUI

public struct ResizableVector: View {
var name: String
var bundle: Bundle?
var keepAspectRatio: Bool = false

private var aspectRatio: CGFloat?

public init(_ name: String) {
self.name = name
}

public init(_ name: String, bundle: Bundle?) {
self.name = name
self.bundle = bundle
}

public init(_ name: String, keepAspectRatio: Bool) {
self.name = name
self.keepAspectRatio = keepAspectRatio
aspectRatio = keepAspectRatio ? getAspectRatio(name, bundle: bundle) : nil
}

public init(_ name: String, bundle: Bundle?, keepAspectRatio: Bool) {
self.name = name
self.bundle = bundle
self.keepAspectRatio = keepAspectRatio
aspectRatio = keepAspectRatio ? getAspectRatio(name, bundle: bundle) : nil
}

private func getAspectRatio(_ name: String, bundle: Bundle?) -> CGFloat? {
guard let image = PlatformImage.makePlatformImage(named: name, in: nil) else { return nil }
let width = image.size.width
let height = image.size.height
return width/height
}

public var body: some View {
Group {
if keepAspectRatio {
Color.clear
.aspectRatio(aspectRatio, contentMode: .fit)
} else {
Color.clear
}
}
.overlay (
GeometryReader {
geometry in
MakeImageAbstractor.makeResizedImage(name: name,
bundle: bundle,
frame: geometry.size,
keepAspectRatio: keepAspectRatio)
}
)
}
}

struct ResizableVector_Previews: PreviewProvider {
static var previews: some View {
HStack {
VStack{
Text("ResizeableVector()")
ResizableVector("example", bundle: Bundle.module, keepAspectRatio: true)
.frame(width: 200)
.background(Color.green)
}

VStack{
Text("Image()")
Image("example", bundle: Bundle.module)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200)
.background(Color.green)
}
}
.previewLayout(.fixed(width: 850, height: 400))
}
}
5 changes: 1 addition & 4 deletions Tests/ResizableVectorTests/ResizableVectorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import XCTest

final class ResizableVectorTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(ResizableVector().text, "Hello, World!")
XCTAssertEqual(true, true)
}
}

0 comments on commit e855eeb

Please sign in to comment.