diff --git a/Markdown/Zoomable.gif b/Markdown/Zoomable.gif
new file mode 100644
index 0000000..1abe7ff
Binary files /dev/null and b/Markdown/Zoomable.gif differ
diff --git a/Package.resolved b/Package.resolved
new file mode 100644
index 0000000..36cd233
--- /dev/null
+++ b/Package.resolved
@@ -0,0 +1,25 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "SDWebImage",
+ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git",
+ "state": {
+ "branch": null,
+ "revision": "2c53f531f1bedd253f55d85105409c28ed4a922c",
+ "version": "5.12.3"
+ }
+ },
+ {
+ "package": "SDWebImageSwiftUI",
+ "repositoryURL": "https://github.com/SDWebImage/SDWebImageSwiftUI.git",
+ "state": {
+ "branch": null,
+ "revision": "cd8625b7cf11a97698e180d28bb7d5d357196678",
+ "version": "2.0.2"
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..edac85e
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,32 @@
+// swift-tools-version:5.5
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "Zoomable",
+ platforms: [
+ .iOS(.v13),
+ .macOS(.v11)
+ ],
+ products: [
+ // Products define the executables and libraries a package produces, and make them visible to other packages.
+ .library(
+ name: "Zoomable",
+ targets: ["Zoomable"]),
+ ],
+ dependencies: [
+ .package(url: "https://github.com/SDWebImage/SDWebImage.git", .branch("master")),
+ .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", .branch("master"))
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "Zoomable",
+ dependencies: ["SDWebImage", "SDWebImageSwiftUI"]),
+ .testTarget(
+ name: "ZoomableTests",
+ dependencies: ["Zoomable"]),
+ ]
+)
diff --git a/README.md b/README.md
index 181ab0a..3e69541 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,51 @@
-# Zoomable
\ No newline at end of file
+# **Zoomable**
+It is a container that allows you to zoom in and out of an image using only SwiftUI.
+
+[![Platforms](https://img.shields.io/badge/Platforms-iOS%20%7C%20macOS-blue?style=flat-square)](https://developer.apple.com/macOS)
+[![iOS](https://img.shields.io/badge/iOS-13.0-blue.svg)](https://developer.apple.com/iOS)
+[![macOS](https://img.shields.io/badge/macOS-11.0-blue.svg)](https://developer.apple.com/macOS)
+[![instagram](https://img.shields.io/badge/instagram-@dev.fabula-orange.svg?style=flat-square)](https://www.instagram.com/dev.fabula)
+[![SPM](https://img.shields.io/badge/SPM-compatible-red?style=flat-square)](https://developer.apple.com/documentation/swift_packages/package/)
+[![MIT](https://img.shields.io/badge/licenses-MIT-red.svg)](https://opensource.org/licenses/MIT)
+
+## Screenshot
+
+
+## Example Usages
+1. ZoomableView
+ ```swift
+ ZoomableView(size: CGSize(width: 300, height: 300), min: 1.0, max: 6.0, showsIndicators: true) {
+ Image("image")
+ .resizable()
+ .scaledToFit()
+ .background(Color.black)
+ .clipped()
+ }
+ ```
+
+
+3. ZoomableImageView
+ ```swift
+ ZoomableImageView(url: url, min: 1.0, max: 3.0, showsIndicators: true) {
+ Text("ZoomableImageView")
+ .padding()
+ .background(Color.black.opacity(0.5))
+ .cornerRadius(8)
+ .foregroundColor(Color.white)
+ }
+ ```
+
+## Dependencies
+```
+dependencies: [
+ .package(url: "https://github.com/SDWebImage/SDWebImage.git", .branch("master")),
+ .package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", .branch("master"))
+]
+```
+
+## Contact
+instagram : [@dev.fabula](https://www.instagram.com/dev.fabula)
+email : [dev.fabula@gmail.com](mailto:dev.fabula@gmail.com)
+
+## License
+ZoomableView is available under the MIT license. See the [LICENSE](LICENSE) file for more info.
diff --git a/Sources/Zoomable/ZoomableImageView.swift b/Sources/Zoomable/ZoomableImageView.swift
new file mode 100644
index 0000000..bd223ad
--- /dev/null
+++ b/Sources/Zoomable/ZoomableImageView.swift
@@ -0,0 +1,76 @@
+//
+// ZoomableImageView.swift
+//
+//
+// Created by jasu on 2022/01/27.
+// Copyright (c) 2022 jasu All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is furnished
+// to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+import SwiftUI
+import SDWebImageSwiftUI
+
+public struct ZoomableImageView: View where Content: View {
+
+ private var url: URL
+ private var min: CGFloat = 1.0
+ private var max: CGFloat = 3.0
+ private var showsIndicators: Bool = false
+ @ViewBuilder private var overlay: () -> Content
+
+ @State private var imageSize: CGSize = .zero
+
+ /**
+ Initializes an `ZoomableImageView`
+ - parameter url : The image url.
+ - parameter min : The minimum value that can be zoom out.
+ - parameter max : The maximum value that can be zoom in.
+ - parameter showsIndicators : A value that indicates whether the scroll view displays the scrollable component of the content offset, in a way that’s suitable for the platform.
+ - parameter overlay : The ZoomableImageView view’s overlay.
+ */
+ public init(url: URL,
+ min: CGFloat = 1.0,
+ max: CGFloat = 3.0,
+ showsIndicators: Bool = false,
+ @ViewBuilder overlay: @escaping () -> Content) {
+ self.url = url
+ self.min = min
+ self.max = max
+ self.showsIndicators = showsIndicators
+ self.overlay = overlay
+ }
+
+ public var body: some View {
+ GeometryReader { proxy in
+ ZoomableView(size: imageSize, min: self.min, max: self.max, showsIndicators: self.showsIndicators) {
+ WebImage(url: url)
+ .resizable()
+ .onSuccess(perform: { image, _, _ in
+ DispatchQueue.main.async {
+ self.imageSize = CGSize(width: proxy.size.width, height: proxy.size.width * (image.size.height / image.size.width))
+ }
+ })
+ .indicator(.activity)
+ .scaledToFit()
+ .clipShape(Rectangle())
+ .overlay(self.overlay())
+ }
+ }
+ }
+}
diff --git a/Sources/Zoomable/ZoomableModifier.swift b/Sources/Zoomable/ZoomableModifier.swift
new file mode 100644
index 0000000..4663bea
--- /dev/null
+++ b/Sources/Zoomable/ZoomableModifier.swift
@@ -0,0 +1,103 @@
+//
+// ZoomableModifier.swift
+// ZoomableView
+//
+// Created by jasu on 2022/01/26.
+// Copyright (c) 2022 jasu All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is furnished
+// to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+import SwiftUI
+
+public struct ZoomableModifier: ViewModifier {
+
+ private enum ZoomState {
+ case inactive
+ case active(scale: CGFloat)
+
+ var scale: CGFloat {
+ switch self {
+ case .active(let scale):
+ return scale
+ default: return 1.0
+ }
+ }
+ }
+
+ private var contentSize: CGSize
+ private var min: CGFloat = 1.0
+ private var max: CGFloat = 3.0
+ private var showsIndicators: Bool = false
+
+ @GestureState private var zoomState = ZoomState.inactive
+ @State private var currentScale: CGFloat = 1.0
+
+ /**
+ Initializes an `ZoomableModifier`
+ - parameter contentSize : The content size of the views.
+ - parameter min : The minimum value that can be zoom out.
+ - parameter max : The maximum value that can be zoom in.
+ - parameter showsIndicators : A value that indicates whether the scroll view displays the scrollable component of the content offset, in a way that’s suitable for the platform.
+ */
+ public init(contentSize: CGSize,
+ min: CGFloat = 1.0,
+ max: CGFloat = 3.0,
+ showsIndicators: Bool = false) {
+ self.contentSize = contentSize
+ self.min = min
+ self.max = max
+ self.showsIndicators = showsIndicators
+ }
+
+ var scale: CGFloat {
+ return currentScale * zoomState.scale
+ }
+
+ var zoomGesture: some Gesture {
+ MagnificationGesture()
+ .updating($zoomState) { value, state, transaction in
+ state = .active(scale: value)
+ }
+ .onEnded { value in
+ var new = self.currentScale * value
+ if new <= min { new = min }
+ if new >= max { new = max }
+ self.currentScale = new
+ }
+ }
+
+ var doubleTapGesture: some Gesture {
+ TapGesture(count: 2).onEnded {
+ if scale <= min { currentScale = max } else
+ if scale >= max { currentScale = min } else {
+ currentScale = ((max - min) * 0.5 + min) < scale ? max : min
+ }
+ }
+ }
+
+ public func body(content: Content) -> some View {
+ ScrollView([.horizontal, .vertical], showsIndicators: showsIndicators) {
+ content
+ .frame(width: contentSize.width * scale, height: contentSize.height * scale, alignment: .center)
+ .scaleEffect(scale, anchor: .center)
+ }
+ .gesture(ExclusiveGesture(zoomGesture, doubleTapGesture))
+ .animation(.easeInOut, value: scale)
+ }
+}
diff --git a/Sources/Zoomable/ZoomableView.swift b/Sources/Zoomable/ZoomableView.swift
new file mode 100644
index 0000000..011abff
--- /dev/null
+++ b/Sources/Zoomable/ZoomableView.swift
@@ -0,0 +1,62 @@
+//
+// ZoomableView.swift
+// ZoomableView
+//
+// Created by jasu on 2022/01/25.
+// Copyright (c) 2022 jasu All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is furnished
+// to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+import SwiftUI
+
+public struct ZoomableView: View where Content: View {
+
+ private var size: CGSize
+ private var min: CGFloat = 1.0
+ private var max: CGFloat = 3.0
+ private var showsIndicators: Bool = false
+ @ViewBuilder private var content: () -> Content
+
+ /**
+ Initializes an `ZoomableView`
+ - parameter size : The content size of the views.
+ - parameter min : The minimum value that can be zoom out.
+ - parameter max : The maximum value that can be zoom in.
+ - parameter showsIndicators : A value that indicates whether the scroll view displays the scrollable component of the content offset, in a way that’s suitable for the platform.
+ - parameter content : The ZoomableView view’s content.
+ */
+ public init(size: CGSize,
+ min: CGFloat = 1.0,
+ max: CGFloat = 3.0,
+ showsIndicators: Bool = false,
+ @ViewBuilder content: @escaping () -> Content) {
+ self.size = size
+ self.min = min
+ self.max = max
+ self.showsIndicators = showsIndicators
+ self.content = content
+ }
+
+ public var body: some View {
+ content()
+ .frame(width: size.width, height: size.height, alignment: .center)
+ .contentShape(Rectangle())
+ .modifier(ZoomableModifier(contentSize: self.size, min: min, max: max, showsIndicators: showsIndicators))
+ }
+}
diff --git a/Tests/ZoomableTests/ZoomableTests.swift b/Tests/ZoomableTests/ZoomableTests.swift
new file mode 100644
index 0000000..2a73747
--- /dev/null
+++ b/Tests/ZoomableTests/ZoomableTests.swift
@@ -0,0 +1,11 @@
+import XCTest
+@testable import Zoomable
+
+final class ZoomableTests: 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(Zoomable().text, "Hello, World!")
+ }
+}
diff --git a/ZoomableExample/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/ZoomableExample/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/ZoomableExample/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ZoomableExample/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json b/ZoomableExample/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..c136eaf
--- /dev/null
+++ b/ZoomableExample/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,148 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ZoomableExample/Shared/Assets.xcassets/Contents.json b/ZoomableExample/Shared/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/ZoomableExample/Shared/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/ZoomableExample/Shared/Assets.xcassets/bany.imageset/Contents.json b/ZoomableExample/Shared/Assets.xcassets/bany.imageset/Contents.json
new file mode 100644
index 0000000..489a433
--- /dev/null
+++ b/ZoomableExample/Shared/Assets.xcassets/bany.imageset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ {
+ "filename" : "bany.jpg",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ },
+ "properties" : {
+ "template-rendering-intent" : "original"
+ }
+}
diff --git a/ZoomableExample/Shared/Assets.xcassets/bany.imageset/bany.jpg b/ZoomableExample/Shared/Assets.xcassets/bany.imageset/bany.jpg
new file mode 100644
index 0000000..a399ec3
Binary files /dev/null and b/ZoomableExample/Shared/Assets.xcassets/bany.imageset/bany.jpg differ
diff --git a/ZoomableExample/Shared/ContentView.swift b/ZoomableExample/Shared/ContentView.swift
new file mode 100644
index 0000000..ab7f3c2
--- /dev/null
+++ b/ZoomableExample/Shared/ContentView.swift
@@ -0,0 +1,85 @@
+//
+// ContentView.swift
+// ZoomableExample
+//
+// Created by jasu on 2022/01/28.
+// Copyright (c) 2022 jasu All rights reserved.
+//
+
+import SwiftUI
+import Zoomable
+
+struct ContentView: View {
+
+ private let url = URL(string: "https://images.unsplash.com/photo-1641130663904-d6cb4772fad5?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1432&q=80")!
+
+ @State private var selection: Int = 0
+
+ private var content: some View {
+ GeometryReader { proxy in
+ TabView(selection: $selection) {
+ ZoomableView(size: CGSize(width: proxy.size.width, height: proxy.size.width * (2/3)), min: 1.0, max: 6.0, showsIndicators: true) {
+ Image("bany")
+ .resizable()
+ .scaledToFit()
+ .background(Color.black)
+ .clipped()
+
+ }
+ .frame(width: proxy.size.width, height: proxy.size.width * (2/3))
+ .overlay(
+ Rectangle()
+ .fill(Color.clear)
+ .border(.black, width: 1)
+ )
+ .tabItem {
+ Image(systemName: "0.square.fill")
+ Text("ZoomableView")
+ }
+ .tag(0)
+
+ ZoomableImageView(url: url, min: 1.0, max: 3.0, showsIndicators: true) {
+ Text("ZoomableImageView")
+ .padding()
+ .background(Color.black.opacity(0.5))
+ .cornerRadius(8)
+ .foregroundColor(Color.white)
+ }
+ .overlay(
+ Rectangle()
+ .fill(Color.clear)
+ .border(.black, width: 1)
+ )
+ .tabItem {
+ Image(systemName: "1.square.fill")
+ Text("ZoomableImageView")
+ }
+ .tag(1)
+ }
+ }
+ }
+ var body: some View {
+#if os(iOS)
+ NavigationView {
+ content
+ .navigationTitle(Text(selection == 0 ? "ZoomableView" : "ZoomableImageView"))
+ .navigationBarTitleDisplayMode(.inline)
+ .padding()
+ }
+ .navigationViewStyle(.stack)
+#else
+ ZStack {
+ content
+ .padding()
+ }
+
+#endif
+ }
+}
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ }
+}
+
diff --git a/ZoomableExample/Shared/ZoomableExampleApp.swift b/ZoomableExample/Shared/ZoomableExampleApp.swift
new file mode 100644
index 0000000..7221b9f
--- /dev/null
+++ b/ZoomableExample/Shared/ZoomableExampleApp.swift
@@ -0,0 +1,18 @@
+//
+// ZoomableExampleApp.swift
+// ZoomableExample
+//
+// Created by jasu on 2022/01/28.
+// Copyright (c) 2022 jasu All rights reserved.
+//
+
+import SwiftUI
+
+@main
+struct ZoomableExampleApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/ZoomableExample/ZoomableExample.xcodeproj/project.pbxproj b/ZoomableExample/ZoomableExample.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..4281077
--- /dev/null
+++ b/ZoomableExample/ZoomableExample.xcodeproj/project.pbxproj
@@ -0,0 +1,496 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 55;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 4B83362C27A3906A006A8AC3 /* ZoomableExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83361C27A39069006A8AC3 /* ZoomableExampleApp.swift */; };
+ 4B83362D27A3906A006A8AC3 /* ZoomableExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83361C27A39069006A8AC3 /* ZoomableExampleApp.swift */; };
+ 4B83362E27A3906A006A8AC3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83361D27A39069006A8AC3 /* ContentView.swift */; };
+ 4B83362F27A3906A006A8AC3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83361D27A39069006A8AC3 /* ContentView.swift */; };
+ 4B83363027A3906A006A8AC3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B83361E27A3906A006A8AC3 /* Assets.xcassets */; };
+ 4B83363127A3906A006A8AC3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B83361E27A3906A006A8AC3 /* Assets.xcassets */; };
+ 4B83363E27A391E3006A8AC3 /* Zoomable in Frameworks */ = {isa = PBXBuildFile; productRef = 4B83363D27A391E3006A8AC3 /* Zoomable */; };
+ 4B83364027A391E7006A8AC3 /* Zoomable in Frameworks */ = {isa = PBXBuildFile; productRef = 4B83363F27A391E7006A8AC3 /* Zoomable */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 4B83361C27A39069006A8AC3 /* ZoomableExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomableExampleApp.swift; sourceTree = ""; };
+ 4B83361D27A39069006A8AC3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 4B83361E27A3906A006A8AC3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 4B83362327A3906A006A8AC3 /* ZoomableExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZoomableExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4B83362927A3906A006A8AC3 /* ZoomableExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZoomableExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 4B83362B27A3906A006A8AC3 /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; };
+ 4B83363B27A3907B006A8AC3 /* Zoomable */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Zoomable; path = ..; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 4B83362027A3906A006A8AC3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83363E27A391E3006A8AC3 /* Zoomable in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4B83362627A3906A006A8AC3 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83364027A391E7006A8AC3 /* Zoomable in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 4B83361627A39069006A8AC3 = {
+ isa = PBXGroup;
+ children = (
+ 4B83363A27A3907B006A8AC3 /* Packages */,
+ 4B83361B27A39069006A8AC3 /* Shared */,
+ 4B83362A27A3906A006A8AC3 /* macOS */,
+ 4B83362427A3906A006A8AC3 /* Products */,
+ 4B83363C27A391E3006A8AC3 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 4B83361B27A39069006A8AC3 /* Shared */ = {
+ isa = PBXGroup;
+ children = (
+ 4B83361C27A39069006A8AC3 /* ZoomableExampleApp.swift */,
+ 4B83361D27A39069006A8AC3 /* ContentView.swift */,
+ 4B83361E27A3906A006A8AC3 /* Assets.xcassets */,
+ );
+ path = Shared;
+ sourceTree = "";
+ };
+ 4B83362427A3906A006A8AC3 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 4B83362327A3906A006A8AC3 /* ZoomableExample.app */,
+ 4B83362927A3906A006A8AC3 /* ZoomableExample.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 4B83362A27A3906A006A8AC3 /* macOS */ = {
+ isa = PBXGroup;
+ children = (
+ 4B83362B27A3906A006A8AC3 /* macOS.entitlements */,
+ );
+ path = macOS;
+ sourceTree = "";
+ };
+ 4B83363A27A3907B006A8AC3 /* Packages */ = {
+ isa = PBXGroup;
+ children = (
+ 4B83363B27A3907B006A8AC3 /* Zoomable */,
+ );
+ name = Packages;
+ sourceTree = "";
+ };
+ 4B83363C27A391E3006A8AC3 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 4B83362227A3906A006A8AC3 /* ZoomableExample (iOS) */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4B83363427A3906A006A8AC3 /* Build configuration list for PBXNativeTarget "ZoomableExample (iOS)" */;
+ buildPhases = (
+ 4B83361F27A3906A006A8AC3 /* Sources */,
+ 4B83362027A3906A006A8AC3 /* Frameworks */,
+ 4B83362127A3906A006A8AC3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "ZoomableExample (iOS)";
+ packageProductDependencies = (
+ 4B83363D27A391E3006A8AC3 /* Zoomable */,
+ );
+ productName = "ZoomableExample (iOS)";
+ productReference = 4B83362327A3906A006A8AC3 /* ZoomableExample.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 4B83362827A3906A006A8AC3 /* ZoomableExample (macOS) */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4B83363727A3906A006A8AC3 /* Build configuration list for PBXNativeTarget "ZoomableExample (macOS)" */;
+ buildPhases = (
+ 4B83362527A3906A006A8AC3 /* Sources */,
+ 4B83362627A3906A006A8AC3 /* Frameworks */,
+ 4B83362727A3906A006A8AC3 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "ZoomableExample (macOS)";
+ packageProductDependencies = (
+ 4B83363F27A391E7006A8AC3 /* Zoomable */,
+ );
+ productName = "ZoomableExample (macOS)";
+ productReference = 4B83362927A3906A006A8AC3 /* ZoomableExample.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 4B83361727A39069006A8AC3 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1320;
+ LastUpgradeCheck = 1320;
+ TargetAttributes = {
+ 4B83362227A3906A006A8AC3 = {
+ CreatedOnToolsVersion = 13.2.1;
+ };
+ 4B83362827A3906A006A8AC3 = {
+ CreatedOnToolsVersion = 13.2.1;
+ };
+ };
+ };
+ buildConfigurationList = 4B83361A27A39069006A8AC3 /* Build configuration list for PBXProject "ZoomableExample" */;
+ compatibilityVersion = "Xcode 13.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 4B83361627A39069006A8AC3;
+ productRefGroup = 4B83362427A3906A006A8AC3 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 4B83362227A3906A006A8AC3 /* ZoomableExample (iOS) */,
+ 4B83362827A3906A006A8AC3 /* ZoomableExample (macOS) */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 4B83362127A3906A006A8AC3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83363027A3906A006A8AC3 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4B83362727A3906A006A8AC3 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83363127A3906A006A8AC3 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 4B83361F27A3906A006A8AC3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83362E27A3906A006A8AC3 /* ContentView.swift in Sources */,
+ 4B83362C27A3906A006A8AC3 /* ZoomableExampleApp.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 4B83362527A3906A006A8AC3 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 4B83362F27A3906A006A8AC3 /* ContentView.swift in Sources */,
+ 4B83362D27A3906A006A8AC3 /* ZoomableExampleApp.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 4B83363227A3906A006A8AC3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 4B83363327A3906A006A8AC3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 4B83363527A3906A006A8AC3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = SM6445X39C;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.devstore.FabulaLab.ZoomableExample;
+ PRODUCT_NAME = ZoomableExample;
+ SDKROOT = iphoneos;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 4B83363627A3906A006A8AC3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = SM6445X39C;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.2;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.devstore.FabulaLab.ZoomableExample;
+ PRODUCT_NAME = ZoomableExample;
+ SDKROOT = iphoneos;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 4B83363827A3906A006A8AC3 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = SM6445X39C;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 12.1;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.devstore.FabulaLab.ZoomableExample;
+ PRODUCT_NAME = ZoomableExample;
+ SDKROOT = macosx;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 4B83363927A3906A006A8AC3 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = SM6445X39C;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 12.1;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.devstore.FabulaLab.ZoomableExample;
+ PRODUCT_NAME = ZoomableExample;
+ SDKROOT = macosx;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4B83361A27A39069006A8AC3 /* Build configuration list for PBXProject "ZoomableExample" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4B83363227A3906A006A8AC3 /* Debug */,
+ 4B83363327A3906A006A8AC3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4B83363427A3906A006A8AC3 /* Build configuration list for PBXNativeTarget "ZoomableExample (iOS)" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4B83363527A3906A006A8AC3 /* Debug */,
+ 4B83363627A3906A006A8AC3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4B83363727A3906A006A8AC3 /* Build configuration list for PBXNativeTarget "ZoomableExample (macOS)" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4B83363827A3906A006A8AC3 /* Debug */,
+ 4B83363927A3906A006A8AC3 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 4B83363D27A391E3006A8AC3 /* Zoomable */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Zoomable;
+ };
+ 4B83363F27A391E7006A8AC3 /* Zoomable */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Zoomable;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 4B83361727A39069006A8AC3 /* Project object */;
+}
diff --git a/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..9f2a5f0
--- /dev/null
+++ b/ZoomableExample/ZoomableExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,25 @@
+{
+ "object": {
+ "pins": [
+ {
+ "package": "SDWebImage",
+ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git",
+ "state": {
+ "branch": "master",
+ "revision": "2c53f531f1bedd253f55d85105409c28ed4a922c",
+ "version": null
+ }
+ },
+ {
+ "package": "SDWebImageSwiftUI",
+ "repositoryURL": "https://github.com/SDWebImage/SDWebImageSwiftUI.git",
+ "state": {
+ "branch": "master",
+ "revision": "336d3f6d3b53729c1f274bf6b75f4532ada116eb",
+ "version": null
+ }
+ }
+ ]
+ },
+ "version": 1
+}
diff --git a/ZoomableExample/ZoomableExample.xcodeproj/xcshareddata/xcschemes/Zoomable.xcscheme b/ZoomableExample/ZoomableExample.xcodeproj/xcshareddata/xcschemes/Zoomable.xcscheme
new file mode 100644
index 0000000..e3b8c0a
--- /dev/null
+++ b/ZoomableExample/ZoomableExample.xcodeproj/xcshareddata/xcschemes/Zoomable.xcscheme
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ZoomableExample/macOS/macOS.entitlements b/ZoomableExample/macOS/macOS.entitlements
new file mode 100644
index 0000000..625af03
--- /dev/null
+++ b/ZoomableExample/macOS/macOS.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-only
+
+ com.apple.security.network.client
+
+
+