├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── .travis.yml ├── BSZoomGridScrollView.podspec ├── BSZoomGridScrollView ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── BSZoomGridScrollView.swift │ ├── BSZoomGridScrollView │ ├── Extension │ │ ├── UIControl+Extension.swift │ │ ├── UIImage+Extension.swift │ │ └── UIViewController+Extension.swift │ ├── Function │ │ └── Functions.swift │ └── UIKit │ │ ├── BSZoomGridBaseViewController.swift │ │ └── BSZoomGridUIScrollView.swift │ ├── BSZoomGridScrollViewController.swift │ └── Enumeration.swift ├── Example ├── BSZoomGridScrollView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── BSZoomGridScrollView-Example.xcscheme │ │ └── SwiftUIExample.xcscheme ├── BSZoomGridScrollView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── BSZoomGridScrollView │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── s0.imageset │ │ │ ├── Contents.json │ │ │ └── s0.jpeg │ │ ├── s1.imageset │ │ │ ├── Contents.json │ │ │ └── s1.jpeg │ │ ├── s10.imageset │ │ │ ├── Contents.json │ │ │ └── s10.jpeg │ │ ├── s11.imageset │ │ │ ├── Contents.json │ │ │ └── s11.jpeg │ │ ├── s12.imageset │ │ │ ├── Contents.json │ │ │ └── s12.jpeg │ │ ├── s13.imageset │ │ │ ├── Contents.json │ │ │ └── s13.jpeg │ │ ├── s14.imageset │ │ │ ├── Contents.json │ │ │ └── s14.jpeg │ │ ├── s15.imageset │ │ │ ├── Contents.json │ │ │ └── s15.jpeg │ │ ├── s16.imageset │ │ │ ├── Contents.json │ │ │ └── s16.jpeg │ │ ├── s17.imageset │ │ │ ├── Contents.json │ │ │ └── s17.jpeg │ │ ├── s18.imageset │ │ │ ├── Contents.json │ │ │ └── s18.jpeg │ │ ├── s19.imageset │ │ │ ├── Contents.json │ │ │ └── s19.jpeg │ │ ├── s2.imageset │ │ │ ├── Contents.json │ │ │ └── s2.jpeg │ │ ├── s20.imageset │ │ │ ├── Contents.json │ │ │ └── s20.jpeg │ │ ├── s21.imageset │ │ │ ├── Contents.json │ │ │ └── s21.jpeg │ │ ├── s22.imageset │ │ │ ├── Contents.json │ │ │ └── s22.jpeg │ │ ├── s23.imageset │ │ │ ├── Contents.json │ │ │ └── s23.jpeg │ │ ├── s24.imageset │ │ │ ├── Contents.json │ │ │ └── s24.jpeg │ │ ├── s25.imageset │ │ │ ├── Contents.json │ │ │ └── s25.jpg │ │ ├── s26.imageset │ │ │ ├── Contents.json │ │ │ └── s26.JPG │ │ ├── s27.imageset │ │ │ ├── Contents.json │ │ │ └── s27.jpg │ │ ├── s28.imageset │ │ │ ├── Contents.json │ │ │ └── s28.jpg │ │ ├── s29.imageset │ │ │ ├── Contents.json │ │ │ └── s29.jpg │ │ ├── s3.imageset │ │ │ ├── Contents.json │ │ │ └── s3.jpeg │ │ ├── s4.imageset │ │ │ ├── Contents.json │ │ │ └── s4.jpeg │ │ ├── s5.imageset │ │ │ ├── Contents.json │ │ │ └── s5.jpeg │ │ ├── s6.imageset │ │ │ ├── Contents.json │ │ │ └── s6.jpeg │ │ ├── s7.imageset │ │ │ ├── Contents.json │ │ │ └── s7.jpeg │ │ ├── s8.imageset │ │ │ ├── Contents.json │ │ │ └── s8.jpeg │ │ └── s9.imageset │ │ │ ├── Contents.json │ │ │ └── s9.jpeg │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── ContentView.swift │ ├── Info.plist │ ├── ShowingSelectedImageView.swift │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Local Podspecs │ │ └── BSZoomGridScrollView.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Target Support Files │ │ ├── BSZoomGridScrollView │ │ ├── BSZoomGridScrollView-Info.plist │ │ ├── BSZoomGridScrollView-dummy.m │ │ ├── BSZoomGridScrollView-prefix.pch │ │ ├── BSZoomGridScrollView-umbrella.h │ │ ├── BSZoomGridScrollView.modulemap │ │ └── BSZoomGridScrollView.xcconfig │ │ ├── Pods-BSZoomGridScrollView_Example │ │ ├── Pods-BSZoomGridScrollView_Example-Info.plist │ │ ├── Pods-BSZoomGridScrollView_Example-acknowledgements.markdown │ │ ├── Pods-BSZoomGridScrollView_Example-acknowledgements.plist │ │ ├── Pods-BSZoomGridScrollView_Example-dummy.m │ │ ├── Pods-BSZoomGridScrollView_Example-frameworks.sh │ │ ├── Pods-BSZoomGridScrollView_Example-umbrella.h │ │ ├── Pods-BSZoomGridScrollView_Example.debug.xcconfig │ │ ├── Pods-BSZoomGridScrollView_Example.modulemap │ │ └── Pods-BSZoomGridScrollView_Example.release.xcconfig │ │ └── Pods-SwiftUIExample │ │ ├── Pods-SwiftUIExample-Info.plist │ │ ├── Pods-SwiftUIExample-acknowledgements.markdown │ │ ├── Pods-SwiftUIExample-acknowledgements.plist │ │ ├── Pods-SwiftUIExample-dummy.m │ │ ├── Pods-SwiftUIExample-frameworks.sh │ │ ├── Pods-SwiftUIExample-umbrella.h │ │ ├── Pods-SwiftUIExample.debug.xcconfig │ │ ├── Pods-SwiftUIExample.modulemap │ │ └── Pods-SwiftUIExample.release.xcconfig ├── SwiftUIExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── iconBeerList.imageset │ │ │ ├── Contents.json │ │ │ └── iconBeerList.png │ │ ├── iconRandom.imageset │ │ │ ├── Contents.json │ │ │ └── iconRandom.png │ │ ├── iconSearchID.imageset │ │ │ ├── Contents.json │ │ │ └── iconSearchID.png │ │ └── placeHolderNoBeer.imageset │ │ │ ├── Contents.json │ │ │ └── placeHolderNoBeer.png │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── CombineNetworkError.swift │ ├── Common │ │ ├── DataStructure │ │ │ └── OrderedSet.swift │ │ ├── ImageLoader │ │ │ └── ImageLoader.swift │ │ ├── Network │ │ │ └── CombineNetwork.swift │ │ ├── Protocol │ │ │ ├── FetchItemUrlMakeable.swift │ │ │ ├── Fetchable.swift │ │ │ └── ImageLoadable.swift │ │ └── UIImage+Extension.swift │ ├── Components │ │ ├── Grid │ │ │ └── GridView.swift │ │ └── ItemList │ │ │ ├── Model │ │ │ └── ItemListModel.swift │ │ │ ├── View │ │ │ ├── ItemList.swift │ │ │ └── ItemRow.swift │ │ │ └── ViewModel │ │ │ └── ItemListViewModel.swift │ ├── ContentView.swift │ ├── Functions.swift │ ├── GenericItem.swift │ ├── Info.plist │ ├── Photo.swift │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── SceneDelegate.swift │ └── URL+Extension.swift ├── SwiftUIExampleTests │ ├── Info.plist │ └── SwiftUIExampleTests.swift ├── SwiftUIExampleUITests │ ├── Info.plist │ └── SwiftUIExampleUITests.swift ├── Tests │ ├── Info.plist │ └── Tests.swift └── build │ └── XCBuildData │ ├── 0c29229faba1b04b484d4f36d61eb1b6-desc.xcbuild │ ├── 0c29229faba1b04b484d4f36d61eb1b6-manifest.xcbuild │ └── BuildDescriptionCacheIndex-73dec89a04f8f8ce5460e588f22190f8 ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── BSZoomGridScrollView │ └── Classes │ ├── .gitkeep │ ├── BSZoomGridScrollView.swift │ ├── BSZoomGridScrollView │ ├── Extension │ │ ├── UIControl+Extension.swift │ │ ├── UIImage+Extension.swift │ │ └── UIViewController+Extension.swift │ ├── Function │ │ └── Functions.swift │ └── UIKit │ │ ├── BSZoomGridBaseViewController.swift │ │ └── BSZoomGridUIScrollView.swift │ ├── BSZoomGridScrollViewController.swift │ └── Enumeration.swift ├── Tests ├── BSZoomGridScrollViewTests │ ├── BSZoomGridScrollViewTests.swift │ └── XCTestManifests.swift └── LinuxMain.swift └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/BSZoomGridScrollView.xcworkspace -scheme BSZoomGridScrollView-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /BSZoomGridScrollView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint BSZoomGridScrollView.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'BSZoomGridScrollView' 11 | s.version = '0.2.0' 12 | s.summary = 'Tracking your touch, BSZoomGridScrollView zooms in subviews in the grid-style UIScrollView' 13 | 14 | s.description = 'Boooooring grid-scrollview that is only good at zooming series of views continuously, tracking your touch..' 15 | 16 | s.homepage = 'https://github.com/boraseoksoon/BSZoomGridScrollView' 17 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 18 | s.license = { :type => 'MIT', :file => 'LICENSE' } 19 | s.author = { 'boraseoksoon' => 'boraseoksoon@gmail.com' } 20 | s.source = { :git => 'https://github.com/boraseoksoon/BSZoomGridScrollView.git', :tag => s.version.to_s } 21 | 22 | s.social_media_url = 'https://twitter.com/boraseoksoon' 23 | 24 | s.swift_version = '5.1' 25 | 26 | s.ios.deployment_target = '13.0' 27 | 28 | s.source_files = 'BSZoomGridScrollView/Classes/**/*' 29 | 30 | s.frameworks = 'UIKit' 31 | end 32 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/BSZoomGridScrollView/Assets/.gitkeep -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/BSZoomGridScrollView/Classes/.gitkeep -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridScrollView.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import SwiftUI 29 | import UIKit 30 | 31 | /// 32 | /// UIKit -> SwiftUI Coordinator 33 | /// 34 | public struct BSZoomGridScrollView { 35 | // MARK: - Initializer 36 | /// 37 | public init(itemsToZoom: [Any], 38 | powerOfZoomBounce: ZoomBounceRatio, 39 | numberOfColumns: Int = 0, 40 | numberOfRows: Int = 0, 41 | scrollEnableButtonTintColor: UIColor = .black, 42 | scrollEnableButtonBackgroundColor: UIColor = .white, 43 | isBeingDraggingOnItem: ((_: UIImage) -> Void)?, 44 | didTapOnItem: ((_: UIImage) -> Void)?, 45 | didLongPressItem: ((_: UIImage) -> Void)?, 46 | didFinishDraggingOnItem: ((_: UIImage) -> Void)?) { 47 | self.didTapOnItem = didTapOnItem 48 | self.didLongPressItem = didLongPressItem 49 | self.didFinishDraggingOnItem = didFinishDraggingOnItem 50 | self.isBeingDraggingOnItem = isBeingDraggingOnItem 51 | 52 | self.itemsToZoom = itemsToZoom 53 | self.powerOfZoomBounce = powerOfZoomBounce 54 | self.scrollEnableButtonTintColor = scrollEnableButtonTintColor 55 | self.scrollEnableButtonBackgroundColor = scrollEnableButtonBackgroundColor 56 | 57 | self.numberOfColumns = numberOfColumns 58 | self.numberOfRows = numberOfRows 59 | } 60 | 61 | // MARK: - Instance Variables 62 | /// 63 | private let itemsToZoom: [Any] 64 | private let powerOfZoomBounce: ZoomBounceRatio 65 | private let numberOfColumns: Int 66 | private let numberOfRows: Int 67 | private let scrollEnableButtonTintColor: UIColor 68 | private let scrollEnableButtonBackgroundColor: UIColor 69 | private let isBeingDraggingOnItem: ((_: UIImage) -> Void)? 70 | private let didTapOnItem: ((_: UIImage) -> Void)? 71 | private let didLongPressItem: ((_: UIImage) -> Void)? 72 | private let didFinishDraggingOnItem: ((_: UIImage) -> Void)? 73 | } 74 | 75 | // MARK: - Public Methods 76 | /// 77 | extension BSZoomGridScrollView: UIViewControllerRepresentable { 78 | 79 | public func makeCoordinator() -> Coordinator { 80 | Coordinator(self) 81 | } 82 | 83 | public func makeUIViewController(context: Context) -> BSZoomGridScrollViewController { 84 | let scrollViewController = BSZoomGridScrollViewController(itemsToZoom: itemsToZoom, 85 | powerOfZoomBounce: self.powerOfZoomBounce, 86 | numberOfColumns: self.numberOfColumns, 87 | numberOfRows: self.numberOfRows, 88 | scrollEnableButtonTintColor: self.scrollEnableButtonTintColor, 89 | scrollEnableButtonBackgroundColor: self.scrollEnableButtonBackgroundColor, 90 | isBeingDraggingOnItem: self.isBeingDraggingOnItem, 91 | didTapOnItem:self.didTapOnItem, 92 | didLongPressItem:self.didLongPressItem, 93 | didFinishDraggingOnItem:self.didFinishDraggingOnItem) 94 | return scrollViewController 95 | } 96 | 97 | public func updateUIViewController(_ vc: BSZoomGridScrollViewController, context: Context) { 98 | vc.refresh(self.itemsToZoom) 99 | } 100 | 101 | public class Coordinator: NSObject { 102 | private var parent: BSZoomGridScrollView 103 | 104 | public init(_ pageViewController: BSZoomGridScrollView) { 105 | self.parent = pageViewController 106 | } 107 | } 108 | } 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIControl+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/19. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | #if os(iOS) 10 | import UIKit 11 | 12 | @objc private class ClosureBox: NSObject { 13 | let closure: ()->() 14 | 15 | init (_ closure: @escaping ()->()) { 16 | self.closure = closure 17 | } 18 | 19 | @objc func invoke () { 20 | closure() 21 | } 22 | } 23 | 24 | extension UIControl { 25 | func take(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping ()->()) { 26 | let container = ClosureBox(closure) 27 | addTarget(container, action: #selector(ClosureBox.invoke), for: controlEvents) 28 | objc_setAssociatedObject(self, "[\(arc4random())]", container, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) 29 | } 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIImage+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extension.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/18. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | #if os(iOS) 10 | import UIKit 11 | 12 | extension UIImage { 13 | func resized(to size: CGSize) -> UIImage { 14 | return UIGraphicsImageRenderer(size: size).image { _ in 15 | draw(in: CGRect(origin: .zero, size: size)) 16 | } 17 | } 18 | } 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIViewController+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Extension.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Seoksoon Jang on 2019/11/19. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | func showToast(message : String, font: UIFont) { 12 | let toast = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, 13 | y: self.view.frame.size.height-(self.view.frame.size.height * 0.3), 14 | width: 150, 15 | height: 50)) 16 | toast.backgroundColor = UIColor.black.withAlphaComponent(0.8) 17 | toast.textColor = UIColor.white 18 | toast.font = font 19 | toast.textAlignment = .center; 20 | toast.text = message 21 | toast.alpha = 1.0 22 | toast.layer.cornerRadius = 15; 23 | toast.clipsToBounds = true 24 | 25 | self.view.addSubview(toast) 26 | 27 | UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: { 28 | toast.alpha = 0.0 29 | }, completion: { isCompleted in 30 | if isCompleted { 31 | toast.removeFromSuperview() 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView/Function/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | // MARK: - Grand Central Dispatch Wrapper - 31 | func MainQ(completion: @escaping () -> Void) { 32 | DispatchQueue.main.async { 33 | completion() 34 | } 35 | } 36 | 37 | func GlobalQ(threadName: String = Bundle.main.bundleIdentifier ?? "BSZoomGridScrollView", 38 | completion: @escaping () -> Void) { 39 | DispatchQueue.global().async { 40 | completion() 41 | } 42 | } 43 | 44 | let serialQueue = DispatchQueue(label: Bundle.main.bundleIdentifier ?? "BSZoomGridScrollView") 45 | func SerialQ(completion: @escaping () -> Void) { 46 | serialQueue.async { 47 | completion() 48 | } 49 | } 50 | 51 | func Delay(_ delaySeconds: Double, completion: @escaping () -> Void) -> Void { 52 | DispatchQueue.main.asyncAfter(deadline: .now() + delaySeconds) { 53 | completion() 54 | } 55 | } 56 | 57 | #if os(iOS) 58 | import UIKit 59 | // MARK: - Utility 60 | /// 61 | func GetRootViewController() -> UIViewController? { 62 | let keyWindow = UIApplication.shared.connectedScenes 63 | .filter({$0.activationState == .foregroundActive}) 64 | .map({$0 as? UIWindowScene}) 65 | .compactMap({$0}) 66 | .first?.windows 67 | .filter({$0.isKeyWindow}).first 68 | return keyWindow?.rootViewController 69 | } 70 | 71 | func RandomEmoji() -> String { 72 | let range = 0x1F300...0x1F3F0 73 | let index = Int(arc4random_uniform(UInt32(range.count))) 74 | let ord = range.lowerBound + index 75 | guard let scalar = UnicodeScalar(ord) else { return "❓" } 76 | return String(scalar) 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollView/UIKit/BSZoomGridBaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridBaseViewController.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import UIKit 29 | 30 | public class BSZoomGridBaseViewController: UIViewController { 31 | // MARK: - Initializers 32 | /// Using constuctor Dependency Injection, all initialization should be done in the constructor. 33 | /// 34 | /// - Parameters: 35 | /// - parentView: a parent view to add scrollView as subview 36 | /// - itemsToZoom: item array in grid to be displayed such as [UIImage] 37 | /// if item numbers are not enough to fill the grid, it will be repeated until grid is fully drawn. 38 | /// - powerOfZoomBounce: a value to be able to choose from enum four enumeration types 39 | /// - numberOfRows: number of row to be applied in a row. 40 | /// - didLongPressItem: closure that will indicates which UIImage is decided to be chosen, by a long touch. 41 | /// - didFinishDraggingOnItem: closure that will indicates 42 | /// which UIImage is decided to be chosen, by a end of pan gesture touch. 43 | /// - Returns: Initializer 44 | public init(itemsToZoom: [Any], 45 | powerOfZoomBounce: ZoomBounceRatio, 46 | numberOfColumns: Int = 0, 47 | numberOfRows: Int = 0, 48 | scrollEnableButtonTintColor: UIColor = .black, 49 | scrollEnableButtonBackgroundColor: UIColor = .white, 50 | isBeingDraggingOnItem: ((_: UIImage) -> Void)?, 51 | didTapOnItem: ((_: UIImage) -> Void)?, 52 | didLongPressItem: ((_: UIImage) -> Void)?, 53 | didFinishDraggingOnItem: ((_: UIImage) -> Void)?) { 54 | /// Closures 55 | self.didTapOnItem = didTapOnItem 56 | self.didLongPressItem = didLongPressItem 57 | self.didFinishDraggingOnItem = didFinishDraggingOnItem 58 | self.isBeingDraggingOnItem = isBeingDraggingOnItem 59 | 60 | self.itemsToZoom = itemsToZoom 61 | self.powerOfZoomBounce = powerOfZoomBounce 62 | 63 | super.init(nibName: nil, bundle: nil) 64 | 65 | self.numberOfColumns = numberOfColumns 66 | self.numberOfRows = numberOfRows 67 | self.scrollEnableButtonTintColor = scrollEnableButtonTintColor 68 | self.scrollEnableButtonBackgroundColor = scrollEnableButtonBackgroundColor 69 | } 70 | 71 | required public init?(coder: NSCoder) { 72 | fatalError(""" 73 | init(coder:) has not been implemented.\n 74 | Creating BSZoomGridScrollViewController programmatically is only allowed. 75 | """) 76 | } 77 | 78 | // MARK: - Private Instance Variables 79 | /// private accessor goes here. 80 | internal private(set) var didLongPressItem: ((_: UIImage) -> Void)? 81 | internal private(set) var didFinishDraggingOnItem: ((_: UIImage) -> Void)? 82 | internal private(set) var didTapOnItem: ((_: UIImage) -> Void)? 83 | internal private(set) var isBeingDraggingOnItem: ((_: UIImage) -> Void)? 84 | 85 | internal private(set) var itemsToZoom: [Any] 86 | 87 | internal private(set) var powerOfZoomBounce: ZoomBounceRatio 88 | 89 | private var _scrollEnableButtonBackgroundColor: UIColor = .black 90 | internal var scrollEnableButtonBackgroundColor: UIColor { 91 | get { 92 | return _scrollEnableButtonBackgroundColor 93 | } 94 | set { 95 | _scrollEnableButtonBackgroundColor = newValue 96 | } 97 | } 98 | 99 | private var _scrollEnableButtonTintColor: UIColor = .black 100 | internal var scrollEnableButtonTintColor: UIColor { 101 | get { 102 | return _scrollEnableButtonTintColor 103 | } 104 | set { 105 | _scrollEnableButtonTintColor = newValue 106 | } 107 | } 108 | 109 | private var _numberOfColumns: CGFloat = 70.0 110 | internal private(set) var numberOfColumns: Int { 111 | get { 112 | return Int(_numberOfColumns) 113 | } 114 | set { 115 | _numberOfColumns = CGFloat(newValue) 116 | } 117 | } 118 | 119 | private var _numberOfRows: CGFloat = 30.0 120 | internal private(set) var numberOfRows: Int { 121 | get { 122 | return Int(_numberOfRows) 123 | } 124 | set { 125 | _numberOfRows = CGFloat(newValue) 126 | } 127 | } 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/BSZoomGridScrollViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridScrollViewController.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import UIKit 29 | 30 | /// 31 | /// 😁 #Step1: Inherits BSZoomGridBaseViewController in order to inject constructor including instances variables, 32 | /// Actually, it does not do any important tasks. 33 | /// So, you can ignore inheritance. By doing so, however, currently you can't change configuration. 34 | public final class BSZoomGridScrollViewController: BSZoomGridBaseViewController { 35 | /// 36 | /// 🤔 #Step2: These buttons are not a mandatory, 37 | /// but it will help us scroll up and down in the grid scroll view. 38 | 39 | // MARK: - Instance Variables 40 | /// 41 | 42 | /// To programmtically disable scroll view, 43 | /// target & action is wrapped by closure. 44 | /// FYI: UIControl+Extension 45 | private lazy var disableOrNotButton: UIButton = { [unowned self] in 46 | let b = UIButton() 47 | b.frame = CGRect(x: UIScreen.main.bounds.width - (UIScreen.main.bounds.width * 0.25), 48 | y: UIScreen.main.bounds.height - (UIScreen.main.bounds.height * 0.25), 49 | width: BSZoomGridUIScrollView.ICON_WIDTH, 50 | height: BSZoomGridUIScrollView.ICON_WIDTH) 51 | 52 | b.setBackgroundImage(UIImage(systemName: "circle"), 53 | for: .normal) 54 | 55 | b.setBackgroundImage(UIImage(systemName: "circle.fill"), 56 | for: .highlighted) 57 | 58 | b.tintColor = self.scrollEnableButtonTintColor 59 | b.backgroundColor = self.scrollEnableButtonBackgroundColor 60 | 61 | b.layer.cornerRadius = b.frame.size.width / 2.0 62 | b.clipsToBounds = true 63 | 64 | b.take(for: .touchUpInside) { [unowned self] in 65 | self.zoomGridScrollView.isScrollEnabled = !self.zoomGridScrollView.isScrollEnabled 66 | 67 | self.showToast( 68 | message: self.zoomGridScrollView.isScrollEnabled 69 | ? "Scroll enabled. \(RandomEmoji())" : "Scroll locked. \(RandomEmoji())", 70 | font: .boldSystemFont(ofSize: 16) 71 | ) 72 | } 73 | 74 | return b 75 | }() 76 | 77 | /// 78 | /// 😊 #Step3: Init BSZoomGridUIScrollView, ready to use! 79 | private lazy var zoomGridScrollView: BSZoomGridUIScrollView = { [unowned self] in 80 | return BSZoomGridUIScrollView(parentView: self.view, 81 | itemsToZoom: self.itemsToZoom, 82 | powerOfZoomBounce: self.powerOfZoomBounce, 83 | numberOfColumns: self.numberOfColumns, 84 | numberOfRows: self.numberOfRows, 85 | isBeingDraggingOnItem: self.isBeingDraggingOnItem, 86 | didTapOnItem: self.didTapOnItem, 87 | didLongPressItem: self.didLongPressItem, 88 | didFinishDraggingOnItem: self.didFinishDraggingOnItem) 89 | }() 90 | 91 | // MARK: - View LifeCycle Methods 92 | /// 93 | override public func viewDidLoad() { 94 | super.viewDidLoad() 95 | 96 | /// 97 | /// 😎 # Step4: That's it. well done! 98 | /// Add all views created so far. 99 | self.view.addSubview(self.zoomGridScrollView) 100 | self.view.addSubview(self.disableOrNotButton) 101 | } 102 | 103 | } 104 | 105 | // MARK: - Public Methods 106 | /// 107 | extension BSZoomGridScrollViewController { 108 | /// Refresh and redraw array items in the grid 109 | /// 110 | /// - Parameters: 111 | /// - compositingOperation: The compositing operation of creating image. 112 | /// - alpha: The alpha should be used for image. 113 | /// - backgroundColor: The background color for the output image. 114 | /// - Returns: An image with compositing operation applied. 115 | /// 116 | /// - Note: This method only works for CG-based image. For any non-CG-based image, `base` itself is returned. 117 | 118 | public func refresh(_ itemsToZoom: [Any]) -> Void { 119 | self.zoomGridScrollView.refresh(itemsToZoom) 120 | } 121 | } 122 | #endif 123 | -------------------------------------------------------------------------------- /BSZoomGridScrollView/Classes/Enumeration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Enumeration.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | // MARK: - Enumeration 30 | /// it will decide bounce animation ratio. it should be only used and injected in the constructor. 31 | public enum ZoomBounceRatio { 32 | case weak 33 | case regular 34 | case strong 35 | case crazy 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView.xcodeproj/xcshareddata/xcschemes/BSZoomGridScrollView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView.xcodeproj/xcshareddata/xcschemes/SwiftUIExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s0.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s0.imageset/s0.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s0.imageset/s0.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s1.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s1.imageset/s1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s1.imageset/s1.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s10.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s10.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s10.imageset/s10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s10.imageset/s10.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s11.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s11.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s11.imageset/s11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s11.imageset/s11.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s12.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s12.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s12.imageset/s12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s12.imageset/s12.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s13.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s13.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s13.imageset/s13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s13.imageset/s13.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s14.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s14.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s14.imageset/s14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s14.imageset/s14.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s15.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s15.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s15.imageset/s15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s15.imageset/s15.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s16.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s16.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s16.imageset/s16.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s16.imageset/s16.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s17.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s17.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s17.imageset/s17.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s17.imageset/s17.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s18.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s18.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s18.imageset/s18.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s18.imageset/s18.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s19.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s19.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s19.imageset/s19.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s19.imageset/s19.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s2.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s2.imageset/s2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s2.imageset/s2.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s20.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s20.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s20.imageset/s20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s20.imageset/s20.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s21.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s21.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s21.imageset/s21.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s21.imageset/s21.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s22.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s22.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s22.imageset/s22.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s22.imageset/s22.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s23.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s23.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s23.imageset/s23.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s23.imageset/s23.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s24.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s24.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s24.imageset/s24.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s24.imageset/s24.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s25.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s25.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s25.imageset/s25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s25.imageset/s25.jpg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s26.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s26.JPG", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s26.imageset/s26.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s26.imageset/s26.JPG -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s27.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s27.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s27.imageset/s27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s27.imageset/s27.jpg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s28.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s28.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s28.imageset/s28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s28.imageset/s28.jpg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s29.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s29.jpg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s29.imageset/s29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s29.imageset/s29.jpg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s3.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s3.imageset/s3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s3.imageset/s3.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s4.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s4.imageset/s4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s4.imageset/s4.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s5.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s5.imageset/s5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s5.imageset/s5.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s6.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s6.imageset/s6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s6.imageset/s6.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s7.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s7.imageset/s7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s7.imageset/s7.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s8.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s8.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s8.imageset/s8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s8.imageset/s8.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s9.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "s9.jpeg", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Assets.xcassets/s9.imageset/s9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/BSZoomGridScrollView/Assets.xcassets/s9.imageset/s9.jpeg -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/18. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | /// To use BSZoomGridScrollView in a SwiftUI project, 10 | /// Please, Follow steps written in the comments with Icon that will help you pay attention like 😀. 11 | 12 | import SwiftUI 13 | 14 | /// 🥳 # Step1: let's import! 15 | import BSZoomGridScrollView 16 | 17 | struct ContentView: View { 18 | @State var showSelectedImageView = false 19 | @State var selectedImage: UIImage? 20 | 21 | var itemsToZoom: [Any] = { 22 | var images = [UIImage]() 23 | for i in 0...29 { 24 | images.append(UIImage(named: "s\(i)") ?? UIImage()) 25 | } 26 | return images 27 | }() 28 | 29 | var body: some View { 30 | /// 😊 # Step2. That's it. completed! 31 | BSZoomGridScrollView(itemsToZoom: itemsToZoom, 32 | powerOfZoomBounce: .regular, 33 | scrollEnableButtonTintColor: .blue, 34 | scrollEnableButtonBackgroundColor: .black, 35 | isBeingDraggingOnItem:{ selectedImage in 36 | /// 37 | }, 38 | didTapOnItem: { tapImage in 39 | /// 40 | }, 41 | didLongPressItem: { selectedImage in 42 | /// Grab an image user end up choosing. 43 | print("on long press : ", selectedImage) 44 | self.selectedImage = selectedImage 45 | 46 | /// Present! 47 | self.showSelectedImageView.toggle() 48 | }, 49 | didFinishDraggingOnItem: { selectedImage in 50 | /// Grab an image user end up choosing. 51 | print("on drag finish : ", selectedImage) 52 | }) 53 | .edgesIgnoringSafeArea(.all) 54 | .sheet(isPresented:self.$showSelectedImageView) { 55 | /// The example view showing a picked up image. 56 | ShowingSelectedImageView(selectedImage: self.selectedImage) 57 | } 58 | } 59 | } 60 | 61 | struct ContentView_Previews: PreviewProvider { 62 | static var previews: some View { 63 | ContentView() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/ShowingSelectedImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShowingSelectedImageView.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/19. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ShowingSelectedImageView: View { 12 | @State var selectedImage: UIImage? 13 | var body: some View { 14 | ZStack { 15 | Rectangle() 16 | .foregroundColor(Color.black) 17 | .frame(width: UIScreen.main.bounds.width, 18 | height: UIScreen.main.bounds.height) 19 | 20 | Spacer() 21 | 22 | Image(uiImage: selectedImage ?? UIImage()) 23 | .edgesIgnoringSafeArea(.all) 24 | 25 | Spacer() 26 | } 27 | } 28 | } 29 | 30 | struct ShowingSelectedImageView_Previews: PreviewProvider { 31 | static var previews: some View { 32 | ShowingSelectedImageView(selectedImage: UIImage()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/BSZoomGridScrollView/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | /// 10 | /// To use BSZoomGridScrollView, 11 | /// Please, Follow steps written in the comments with icon like 😀. 12 | /// 13 | 14 | import SwiftUI 15 | import UIKit 16 | 17 | /// 18 | // 😚 #Step1: import BSZoomGridScrollView! 19 | /// 20 | import BSZoomGridScrollView 21 | 22 | class ViewController: UIViewController { 23 | /// 24 | // prepare any item array to feed to BSZoomGridScrollViewController. 25 | /// 26 | private var itemsToZoom: [Any] = { 27 | return (0...1500).compactMap { _ in UIImage(systemName: "smiley") } 28 | }() 29 | 30 | 31 | /// 32 | // 😋 #Step2: declare BSZoomGridScrollView 33 | /// 34 | private lazy var zoomGridScrollViewController: BSZoomGridScrollViewController = { [unowned self] in 35 | /// 36 | /// It can be used on both SwiftUI and UIKit. 37 | /// To see how it works on SwiftUI, 38 | /// please refer to comments in SwiftUI directory -> ContentView.swift 39 | /// 40 | return BSZoomGridScrollViewController(itemsToZoom: self.itemsToZoom, 41 | powerOfZoomBounce: .regular, 42 | scrollEnableButtonTintColor: .black, 43 | scrollEnableButtonBackgroundColor: .white, 44 | isBeingDraggingOnItem:{ [unowned self] selectedImage in 45 | /// 46 | }, 47 | didTapOnItem: { [unowned self] tapImage in 48 | /// 49 | }, 50 | didLongPressItem: { [unowned self] selectedImage in 51 | /// 52 | }, 53 | didFinishDraggingOnItem: { [unowned self] selectedImage in 54 | /// 55 | }) 56 | }() 57 | 58 | /// 59 | // 😁 #Step3: Present it! 60 | /// 61 | @IBAction func goToBSZoomGridScrollView(_ sender: Any) { 62 | /// 63 | // 😎 That's all. well done. 64 | /// 65 | 66 | self.present(UINavigationController(rootViewController: zoomGridScrollViewController), 67 | animated: true, 68 | completion: nil) 69 | } 70 | 71 | /// 72 | // MARK: - ViewController LifeCycle Methods 73 | /// 74 | override func viewDidLoad() { 75 | super.viewDidLoad() 76 | } 77 | 78 | override func didReceiveMemoryWarning() { 79 | super.didReceiveMemoryWarning() 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'BSZoomGridScrollView_Example' do 4 | platform :ios, '13.0' 5 | pod 'BSZoomGridScrollView', :path => '../' 6 | 7 | end 8 | 9 | 10 | target 'SwiftUIExample' do 11 | platform :ios, '13.0' 12 | pod 'BSZoomGridScrollView', :path => '../' 13 | 14 | end 15 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BSZoomGridScrollView (0.1.4) 3 | 4 | DEPENDENCIES: 5 | - BSZoomGridScrollView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | BSZoomGridScrollView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | BSZoomGridScrollView: 2ad694576a0e12dafa0e35c2f23e897ae591535a 13 | 14 | PODFILE CHECKSUM: a0b49d6ddff3a37ca5d2ad80461a1f49ed496298 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/BSZoomGridScrollView.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BSZoomGridScrollView", 3 | "version": "0.1.4", 4 | "summary": "Tracking your touch, BSZoomGridScrollView zooms in subviews in the grid-style UIScrollView", 5 | "description": "Boooooring grid-scrollview that is only good at zooming series of views continuously, tracking your touch..", 6 | "homepage": "https://github.com/boraseoksoon/BSZoomGridScrollView", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "boraseoksoon": "boraseoksoon@gmail.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/boraseoksoon/BSZoomGridScrollView.git", 16 | "tag": "0.1.4" 17 | }, 18 | "social_media_url": "https://twitter.com/boraseoksoon", 19 | "swift_versions": "5.1", 20 | "platforms": { 21 | "ios": "13.0" 22 | }, 23 | "source_files": "BSZoomGridScrollView/Classes/**/*", 24 | "frameworks": "UIKit", 25 | "swift_version": "5.1" 26 | } 27 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - BSZoomGridScrollView (0.1.4) 3 | 4 | DEPENDENCIES: 5 | - BSZoomGridScrollView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | BSZoomGridScrollView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | BSZoomGridScrollView: 2ad694576a0e12dafa0e35c2f23e897ae591535a 13 | 14 | PODFILE CHECKSUM: a0b49d6ddff3a37ca5d2ad80461a1f49ed496298 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_BSZoomGridScrollView : NSObject 3 | @end 4 | @implementation PodsDummy_BSZoomGridScrollView 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double BSZoomGridScrollViewVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char BSZoomGridScrollViewVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView.modulemap: -------------------------------------------------------------------------------- 1 | framework module BSZoomGridScrollView { 2 | umbrella header "BSZoomGridScrollView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/BSZoomGridScrollView/BSZoomGridScrollView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -framework "UIKit" 4 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## BSZoomGridScrollView 5 | 6 | Copyright (c) 2019 boraseoksoon 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2019 boraseoksoon <boraseoksoon@gmail.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | BSZoomGridScrollView 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_BSZoomGridScrollView_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_BSZoomGridScrollView_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_BSZoomGridScrollView_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_BSZoomGridScrollView_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "BSZoomGridScrollView" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_BSZoomGridScrollView_Example { 2 | umbrella header "Pods-BSZoomGridScrollView_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-BSZoomGridScrollView_Example/Pods-BSZoomGridScrollView_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "BSZoomGridScrollView" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## BSZoomGridScrollView 5 | 6 | Copyright (c) 2019 boraseoksoon 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2019 boraseoksoon <boraseoksoon@gmail.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | BSZoomGridScrollView 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_SwiftUIExample : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_SwiftUIExample 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework" 165 | fi 166 | if [[ "$CONFIGURATION" == "Release" ]]; then 167 | install_framework "${BUILT_PRODUCTS_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework" 168 | fi 169 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 170 | wait 171 | fi 172 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_SwiftUIExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_SwiftUIExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "BSZoomGridScrollView" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_SwiftUIExample { 2 | umbrella header "Pods-SwiftUIExample-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-SwiftUIExample/Pods-SwiftUIExample.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BSZoomGridScrollView/BSZoomGridScrollView.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "BSZoomGridScrollView" -framework "UIKit" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | // MARK: UISceneSession Lifecycle 22 | 23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | 29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 30 | // Called when the user discards a scene session. 31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconBeerList.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "iconBeerList.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconBeerList.imageset/iconBeerList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/SwiftUIExample/Assets.xcassets/iconBeerList.imageset/iconBeerList.png -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconRandom.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "iconRandom.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconRandom.imageset/iconRandom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/SwiftUIExample/Assets.xcassets/iconRandom.imageset/iconRandom.png -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconSearchID.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "iconSearchID.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/iconSearchID.imageset/iconSearchID.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/SwiftUIExample/Assets.xcassets/iconSearchID.imageset/iconSearchID.png -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/placeHolderNoBeer.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "placeHolderNoBeer.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/Assets.xcassets/placeHolderNoBeer.imageset/placeHolderNoBeer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/SwiftUIExample/Assets.xcassets/placeHolderNoBeer.imageset/placeHolderNoBeer.png -------------------------------------------------------------------------------- /Example/SwiftUIExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/CombineNetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombineNetworkError.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum CombineNetworkError: Error { 12 | case error(String) 13 | case unknown(String) 14 | 15 | var message: String? { 16 | switch self { 17 | case .error(let msg): 18 | return msg 19 | case let .unknown(msg): 20 | return msg 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/DataStructure/OrderedSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedSet.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// An ordered set is an ordered collection of instances of `Element` in which 12 | /// uniqueness of the objects is guaranteed. 13 | public struct OrderedSet: Equatable, Collection { 14 | public typealias Element = E 15 | public typealias Index = Int 16 | 17 | #if swift(>=4.1.50) 18 | public typealias Indices = Range 19 | #else 20 | public typealias Indices = CountableRange 21 | #endif 22 | 23 | private var array: [Element] 24 | private var set: Set 25 | 26 | /// Creates an empty ordered set. 27 | public init() { 28 | self.array = [] 29 | self.set = Set() 30 | } 31 | 32 | /// Creates an ordered set with the contents of `array`. 33 | /// 34 | /// If an element occurs more than once in `element`, only the first one 35 | /// will be included. 36 | public init(_ array: [Element]) { 37 | self.init() 38 | for element in array { 39 | append(element) 40 | } 41 | } 42 | 43 | // MARK: Working with an ordered set 44 | /// The number of elements the ordered set stores. 45 | public var count: Int { return array.count } 46 | 47 | /// Returns `true` if the set is empty. 48 | public var isEmpty: Bool { return array.isEmpty } 49 | 50 | /// Returns the contents of the set as an array. 51 | public var contents: [Element] { return array } 52 | 53 | /// Returns `true` if the ordered set contains `member`. 54 | public func contains(_ member: Element) -> Bool { 55 | return set.contains(member) 56 | } 57 | 58 | /// Adds an element to the ordered set. 59 | /// 60 | /// If it already contains the element, then the set is unchanged. 61 | /// 62 | /// - returns: True if the item was inserted. 63 | @discardableResult 64 | public mutating func append(_ newElement: Element) -> Bool { 65 | let inserted = set.insert(newElement).inserted 66 | if inserted { 67 | array.append(newElement) 68 | } 69 | return inserted 70 | } 71 | 72 | /// Remove and return the element at the beginning of the ordered set. 73 | public mutating func removeFirst() -> Element { 74 | let firstElement = array.removeFirst() 75 | set.remove(firstElement) 76 | return firstElement 77 | } 78 | 79 | /// Remove and return the element at the end of the ordered set. 80 | public mutating func removeLast() -> Element { 81 | let lastElement = array.removeLast() 82 | set.remove(lastElement) 83 | return lastElement 84 | } 85 | 86 | /// Remove all elements. 87 | public mutating func removeAll(keepingCapacity keepCapacity: Bool) { 88 | array.removeAll(keepingCapacity: keepCapacity) 89 | set.removeAll(keepingCapacity: keepCapacity) 90 | } 91 | } 92 | 93 | extension OrderedSet: ExpressibleByArrayLiteral { 94 | /// Create an instance initialized with `elements`. 95 | /// 96 | /// If an element occurs more than once in `element`, only the first one 97 | /// will be included. 98 | public init(arrayLiteral elements: Element...) { 99 | self.init(elements) 100 | } 101 | } 102 | 103 | extension OrderedSet: RandomAccessCollection { 104 | public var startIndex: Int { return contents.startIndex } 105 | public var endIndex: Int { return contents.endIndex } 106 | public subscript(index: Int) -> Element { 107 | return contents[index] 108 | } 109 | } 110 | 111 | public func == (lhs: OrderedSet, rhs: OrderedSet) -> Bool { 112 | return lhs.contents == rhs.contents 113 | } 114 | 115 | extension OrderedSet: Hashable where Element: Hashable { } 116 | 117 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/ImageLoader/ImageLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageLoader.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import Combine 10 | import SwiftUI 11 | 12 | final class ImageLoader: ObservableObject { 13 | @Published private(set) var image: UIImage? = nil 14 | 15 | public lazy var objectWillChange: AnyPublisher = { 16 | Empty().eraseToAnyPublisher() 17 | }() 18 | 19 | private let loadable: ImageLoadable 20 | private var cancellable: AnyCancellable? 21 | 22 | init(loadable: ImageLoadable, id: Int) { 23 | self.loadable = loadable 24 | 25 | self.objectWillChange = $image.handleEvents( 26 | receiveSubscription: { [weak self] sub in 27 | self?.load(id: id) 28 | }, 29 | receiveCancel: { [weak self] in 30 | self?.cancellable?.cancel() 31 | } 32 | ).eraseToAnyPublisher() 33 | } 34 | } 35 | 36 | // MARK: - Private methods 37 | extension ImageLoader { 38 | private func load(id: Int) { 39 | guard image == nil else { 40 | return 41 | } 42 | 43 | cancellable = loadable 44 | .loadImage(id: id) 45 | .receive(on: DispatchQueue.main) 46 | .sink( 47 | receiveCompletion: { _ in }, 48 | receiveValue: { [weak self] image in 49 | self?.image = image 50 | } 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/Network/CombineNetwork.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | class CombineNetwork { 13 | private let session: URLSession 14 | 15 | init(session: URLSession = .shared) { 16 | self.session = session 17 | } 18 | } 19 | 20 | // MARK: - Fetchable 21 | extension CombineNetwork: Fetchable { 22 | func fetchItems(page: Int?) -> AnyPublisher<[Photo], CombineNetworkError> { 23 | guard let url = makeFetchItemsComponents(page: page).url else { 24 | return makeErrorPublisher(msg: "invalid Url") 25 | } 26 | 27 | return session.dataTaskPublisher(for: URLRequest(url: url)) 28 | .mapError { .error($0.localizedDescription) } 29 | .flatMap { response in 30 | Just(response.data) 31 | .decode(type: [Photo].self, decoder: JSONDecoder()) 32 | .mapError { .error($0.localizedDescription) } 33 | } 34 | .eraseToAnyPublisher() 35 | } 36 | } 37 | 38 | // MARK: - FetchItemUrlMakeable 39 | extension CombineNetwork: FetchItemUrlMakeable { 40 | struct apiUrl { 41 | static let scheme = "https" 42 | static let host = "picsum.photos" 43 | static let path = "/v2/list" 44 | } 45 | 46 | func makeFetchItemsComponents(page: Int?) -> URLComponents { 47 | var components = URLComponents() 48 | components.scheme = apiUrl.scheme 49 | components.host = apiUrl.host 50 | components.path = apiUrl.path 51 | 52 | if let page = page { 53 | components.queryItems = [ 54 | URLQueryItem(name: "page", value: "\(page)"), 55 | URLQueryItem(name: "limit", value: "200") 56 | ] 57 | } else { 58 | components.queryItems = [ 59 | URLQueryItem(name: "limit", value: "200") 60 | ] 61 | } 62 | 63 | return components 64 | } 65 | } 66 | 67 | // MARK: - Private Methods 68 | extension CombineNetwork { 69 | private func makeErrorPublisher(msg: String) -> AnyPublisher<[Photo], CombineNetworkError> { 70 | Fail(error: CombineNetworkError.error(msg)).eraseToAnyPublisher() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/Protocol/FetchItemUrlMakeable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchItemUrlMakeable.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | protocol FetchItemUrlMakeable { 13 | func makeFetchItemsComponents(page: Int?) -> URLComponents 14 | } 15 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/Protocol/Fetchable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Fetchable.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | protocol Fetchable { 13 | func fetchItems(page: Int?) -> AnyPublisher<[Photo], CombineNetworkError> 14 | } 15 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/Protocol/ImageLoadable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageLoadable.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | import Combine 12 | 13 | protocol ImageLoadable { 14 | func loadImage(id: Int) -> AnyPublisher 15 | } 16 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Common/UIImage+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extension.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | import Combine 12 | 13 | extension UIImage: ImageLoadable { 14 | func loadImage(id: Int) -> AnyPublisher { 15 | return Just(self) 16 | .setFailureType(to: Error.self) 17 | .eraseToAnyPublisher() 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Components/Grid/GridView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridView.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | /// 🥳 # Step1: let's import! 11 | import BSZoomGridScrollView 12 | 13 | struct GridView: View { 14 | @ObservedObject var viewModel: ItemListViewModel 15 | 16 | @State var showSelectedImageView = false 17 | @State var selectedImage: UIImage? 18 | 19 | init(viewModel: ItemListViewModel) { 20 | self.viewModel = viewModel 21 | } 22 | 23 | /// from VM 24 | func itemsToZoom() -> [UIImage] { 25 | return self.viewModel.images 26 | } 27 | 28 | // generated static images 29 | var itemsToZoom2: [Any] = { 30 | var images = [UIImage]() 31 | for i in 0...1000 { 32 | images.append(UIImage(systemName: "smiley") ?? UIImage()) 33 | } 34 | return images 35 | }() 36 | 37 | var body: some View { 38 | /// 😊 # Step2. That's it. completed! 39 | BSZoomGridScrollView(itemsToZoom: itemsToZoom(), 40 | powerOfZoomBounce: .regular, 41 | scrollEnableButtonTintColor: .blue, 42 | scrollEnableButtonBackgroundColor: .black, 43 | isBeingDraggingOnItem:{ draggingImage in 44 | /// 45 | }, 46 | didTapOnItem: { tapImage in 47 | /// 48 | }, 49 | didLongPressItem: { longPressedImage in 50 | /// 51 | }, 52 | didFinishDraggingOnItem: { draggedFinalImage in 53 | /// 54 | }) 55 | .edgesIgnoringSafeArea(.all) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Components/ItemList/Model/ItemListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListModel.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | struct ItemListModel { 13 | let network: Fetchable 14 | 15 | init(network: Fetchable) { 16 | self.network = network 17 | } 18 | } 19 | 20 | // MARK: - Public Methods 21 | extension ItemListModel { 22 | func fetchItems(page: Int?) -> AnyPublisher<[Photo], CombineNetworkError> { 23 | return network.fetchItems(page: page) 24 | } 25 | 26 | func getPage(items: [Photo], id: Int?) -> Int? { 27 | let lastRowCount = items.count 28 | let lastIndex = items.firstIndex { id == Int($0.id) } 29 | let page = lastRowCount / 75 + 1 30 | 31 | guard (74...) ~= lastRowCount, 32 | lastRowCount - 1 == lastIndex else { 33 | return nil 34 | } 35 | 36 | return page 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Components/ItemList/View/ItemList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemList.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ItemList: View { 12 | @ObservedObject var viewModel: ItemListViewModel 13 | 14 | init(viewModel: ItemListViewModel) { 15 | self.viewModel = viewModel 16 | } 17 | 18 | var body: some View { 19 | NavigationView { 20 | listView 21 | } 22 | .alert(isPresented: $viewModel.showingAlert) { 23 | Alert(title: Text(viewModel.errorMessage)) 24 | } 25 | } 26 | } 27 | 28 | // MARK: - Views 29 | extension ItemList { 30 | var listView: some View { 31 | List(viewModel.items, id: \.id) { item in 32 | ItemRow(item: item).onAppear { 33 | self.viewModel.appearItem(id:item.id) 34 | } 35 | } 36 | .navigationBarTitle(Text("Photos")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Components/ItemList/View/ItemRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemRow.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | struct ItemRow: View { 13 | var item: Photo 14 | @ObservedObject var imageLoader: ImageLoader 15 | 16 | init(item: Photo) { 17 | self.item = item 18 | 19 | self.imageLoader = ImageLoader( 20 | loadable: URL(string: item.imageURL ?? "") ?? UIImage(systemName: "xmark") ?? UIImage(), 21 | id: item.id 22 | ) 23 | } 24 | 25 | var body: some View { 26 | VStack { 27 | itemRow 28 | } 29 | } 30 | } 31 | 32 | // MARK: - Views 33 | extension ItemRow { 34 | var itemRow: some View { 35 | HStack { 36 | Image(uiImage: imageLoader.image ?? UIImage(systemName: "xmark") ?? UIImage()) 37 | .resizable() 38 | .clipShape(Circle()) 39 | .frame(width: 120.0, height: 120.0, alignment: .center) 40 | .overlay(Circle().stroke(Color.white,lineWidth:2).shadow(radius: 10)) 41 | 42 | VStack(alignment: .leading) { 43 | Text("Author: \(item.name ?? "unknown author")") 44 | .font(Font.system(size: 14, weight: .regular, design: .default)) 45 | .foregroundColor(.blue) 46 | .padding([.bottom], 10) 47 | 48 | Text("Size: \(item.description ?? "")") 49 | .font(Font.system(size: 12, weight: .light, design: .default)) 50 | .foregroundColor(.gray) 51 | .lineLimit(3) 52 | } 53 | .padding([.leading], 5) 54 | } 55 | .padding([.top, .bottom], 20) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Components/ItemList/ViewModel/ItemListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemListViewModel.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by boraseoksoon on 11/18/2019. 6 | // Copyright (c) 2019 boraseoksoon. All rights reserved. 7 | // 8 | 9 | import Combine 10 | import SwiftUI 11 | 12 | class ItemListViewModel: ObservableObject { 13 | @Published var items: [Photo] = [] 14 | @Published var showingAlert: Bool = false 15 | @Published var errorMessage: String = "" 16 | 17 | private let itemId = PassthroughSubject() 18 | private var cancellables = Set() 19 | 20 | private var _images: [UIImage] = [] 21 | public var images: [UIImage] { 22 | get { 23 | return _images 24 | } 25 | set { 26 | _images = newValue 27 | } 28 | } 29 | 30 | init(model: ItemListModel = ItemListModel(network: CombineNetwork())) { 31 | self.bind(to: model) 32 | } 33 | } 34 | 35 | // MARK: - Public methods 36 | extension ItemListViewModel { 37 | public func appearItem(id: Int?) -> Void { 38 | guard let id = id else { return } 39 | self.itemId.send(id) 40 | } 41 | } 42 | 43 | // MARK: - Private methods 44 | extension ItemListViewModel { 45 | private func bind(to model: ItemListModel) { 46 | itemId 47 | .map { model.getPage(items: self.items, id: $0) } 48 | .filter { $0 != nil } 49 | .eraseToAnyPublisher() 50 | .prepend(nil) 51 | .flatMap(model.fetchItems) 52 | .receive(on: DispatchQueue.main) 53 | .sink( 54 | receiveCompletion: { 55 | guard case .failure(let error) = $0 else { return } 56 | self.items = [] 57 | self.showingAlert = true 58 | self.errorMessage = error.message ?? "Unknown error" 59 | }, 60 | receiveValue: { items in 61 | self.items += items 62 | 63 | self.downloadImages(from:items) 64 | } 65 | ) 66 | .store(in: &cancellables) 67 | } 68 | 69 | private func downloadImages(from items: [Photo]) -> Void { 70 | DispatchQueue.global().async { 71 | items.forEach { item in 72 | URL(string: item.imageURL ?? "")? 73 | .loadImage(id: item.id) 74 | .receive(on: DispatchQueue.main) 75 | .sink( 76 | receiveCompletion: { _ in }, 77 | receiveValue: { [weak self] image in 78 | DispatchQueue.main.async { 79 | self?.images.append(image) 80 | } 81 | } 82 | ) 83 | .store(in: &self.cancellables) 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | struct ContentView: View { 13 | @ObservedObject var ItemListViewModel: ItemListViewModel 14 | 15 | init(ItemListViewModel: ItemListViewModel) { 16 | self.ItemListViewModel = ItemListViewModel 17 | } 18 | 19 | var body: some View { 20 | TabView { 21 | ItemList(viewModel: ItemListViewModel) 22 | .tabItem { 23 | Image(systemName:"safari") 24 | Text("Photos") 25 | } 26 | 27 | GridView(viewModel: ItemListViewModel) 28 | .tabItem { 29 | Image(systemName: "grid") 30 | Text("Grid") 31 | } 32 | } 33 | .edgesIgnoringSafeArea(.top) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | func ReplaceUrlResolution(from url: String, 13 | targetWidth: String, 14 | targetHeight: String) -> String { 15 | var copyUrl = url 16 | let components = copyUrl 17 | .components(separatedBy: "id/") 18 | 19 | if components.count > 1 { 20 | let subComponents = components[1] 21 | .components(separatedBy:"/") 22 | 23 | if subComponents.count >= 3 { 24 | let sourceWidth = subComponents[1] 25 | let sourceHeight = subComponents[2] 26 | 27 | copyUrl = copyUrl 28 | .replacingOccurrences(of: sourceWidth, 29 | with: targetWidth) 30 | copyUrl = copyUrl 31 | .replacingOccurrences(of: sourceHeight, 32 | with: targetHeight) 33 | 34 | } else { 35 | return url 36 | } 37 | } else { 38 | return url 39 | } 40 | 41 | return copyUrl 42 | } 43 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/GenericItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenericItem.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol GenericItem { 12 | var id: Int { get } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Photo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Photo.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Combine 11 | 12 | struct Photo: GenericItem, Codable, Identifiable { 13 | let id: Int 14 | let author: String 15 | let width: Int 16 | let height: Int 17 | let url: String 18 | let download_url: String 19 | 20 | let name: String? 21 | let imageURL: String? 22 | let description: String? 23 | 24 | enum CodingKeys: String, CodingKey { 25 | case id, author, width, height, url, download_url 26 | } 27 | 28 | init(from decoder: Decoder) throws { 29 | let values = try decoder.container(keyedBy: CodingKeys.self) 30 | 31 | let id = try values.decode(String.self, forKey: .id) 32 | self.id = Int(id) ?? -1 33 | 34 | self.author = try values.decode(String.self, forKey: .author) 35 | self.width = try values.decode(Int.self, forKey: .width) 36 | self.height = try values.decode(Int.self, forKey: .height) 37 | self.url = try values.decode(String.self, forKey: .url) 38 | self.download_url = try values.decode(String.self, forKey: .download_url) 39 | 40 | self.name = self.author 41 | self.imageURL = ReplaceUrlResolution(from: self.download_url, 42 | targetWidth: "120", 43 | targetHeight: "120") 44 | 45 | self.description = "\(String(self.width)) * \(String(self.height))" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/SwiftUIExample/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | guard let windowScene = scene as? UIWindowScene else { return } 18 | 19 | let contentView = ContentView( 20 | ItemListViewModel: ItemListViewModel() 21 | ) 22 | 23 | // Use a UIHostingController as window root view controller 24 | let window = UIWindow(windowScene: windowScene) 25 | window.rootViewController = UIHostingController(rootView: contentView) 26 | window.makeKeyAndVisible() 27 | self.window = window 28 | } 29 | 30 | 31 | func sceneDidDisconnect(_ scene: UIScene) { 32 | // Called as the scene is being released by the system. 33 | // This occurs shortly after the scene enters the background, or when its session is discarded. 34 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 35 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 36 | } 37 | 38 | func sceneDidBecomeActive(_ scene: UIScene) { 39 | // Called when the scene has moved from an inactive state to an active state. 40 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 41 | } 42 | 43 | func sceneWillResignActive(_ scene: UIScene) { 44 | // Called when the scene will move from an active state to an inactive state. 45 | // This may occur due to temporary interruptions (ex. an incoming phone call). 46 | } 47 | 48 | func sceneWillEnterForeground(_ scene: UIScene) { 49 | // Called as the scene transitions from the background to the foreground. 50 | // Use this method to undo the changes made on entering the background. 51 | } 52 | 53 | func sceneDidEnterBackground(_ scene: UIScene) { 54 | // Called as the scene transitions from the foreground to the background. 55 | // Use this method to save data, release shared resources, and store enough scene-specific state information 56 | // to restore the scene back to its current state. 57 | } 58 | 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /Example/SwiftUIExample/URL+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL+Extension.swift 3 | // SwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | import Combine 12 | 13 | extension URL: ImageLoadable { 14 | enum ImageLoadingError: Error { 15 | case incorrectData 16 | } 17 | 18 | func loadImage(id: Int) -> AnyPublisher { 19 | if let retrieveImage = self.retrieveImage(forKey: "\(id)") { 20 | return retrieveImage.loadImage(id: id) 21 | } 22 | 23 | return URLSession 24 | .shared 25 | .dataTaskPublisher(for: self) 26 | .tryMap { data, _ in 27 | guard let image = UIImage(data: data) else { 28 | throw ImageLoadingError.incorrectData 29 | } 30 | 31 | self.store(image: image, forKey: "\(id)") 32 | return image 33 | } 34 | .eraseToAnyPublisher() 35 | } 36 | 37 | private func store(image: UIImage, forKey key: String) { 38 | if let pngRepresentation = image.pngData() { 39 | UserDefaults.standard.set(pngRepresentation, forKey: key) 40 | } 41 | } 42 | 43 | private func retrieveImage(forKey key: String) -> UIImage? { 44 | guard let imageData = UserDefaults.standard.object(forKey: key) as? Data, 45 | let image = UIImage(data: imageData) else { 46 | return nil 47 | } 48 | return image 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Example/SwiftUIExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/SwiftUIExampleTests/SwiftUIExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIExampleTests.swift 3 | // SwiftUIExampleTests 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftUIExample 11 | 12 | class SwiftUIExampleTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Example/SwiftUIExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/SwiftUIExampleUITests/SwiftUIExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIExampleUITests.swift 3 | // SwiftUIExampleUITests 4 | // 5 | // Created by Seoksoon Jang on 2019/12/02. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftUIExampleUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 20 | } 21 | 22 | override func tearDown() { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | } 25 | 26 | func testExample() { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use recording to get started writing UI tests. 32 | // Use XCTAssert and related functions to verify your tests produce the correct results. 33 | } 34 | 35 | func testLaunchPerformance() { 36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 37 | // This measures how long it takes to launch your application. 38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 39 | XCUIApplication().launch() 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import BSZoomGridScrollView 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Example/build/XCBuildData/0c29229faba1b04b484d4f36d61eb1b6-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/build/XCBuildData/0c29229faba1b04b484d4f36d61eb1b6-desc.xcbuild -------------------------------------------------------------------------------- /Example/build/XCBuildData/BuildDescriptionCacheIndex-73dec89a04f8f8ce5460e588f22190f8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Example/build/XCBuildData/BuildDescriptionCacheIndex-73dec89a04f8f8ce5460e588f22190f8 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 boraseoksoon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | 5 | import PackageDescription 6 | 7 | let package = Package( 8 | name: "BSZoomGridScrollView", 9 | platforms: [ 10 | .iOS(.v13), 11 | ], 12 | 13 | products: [ 14 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 15 | .library( 16 | name: "BSZoomGridScrollView", 17 | targets: ["BSZoomGridScrollView"]), 18 | ], 19 | dependencies: [ 20 | // Dependencies declare other packages that this package depends on. 21 | // .package(url: /* package url */, from: "1.0.0"), 22 | ], 23 | targets: [ 24 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 25 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 26 | .target( 27 | name: "BSZoomGridScrollView", 28 | dependencies: []), 29 | .testTarget( 30 | name: "BSZoomGridScrollViewTests", 31 | dependencies: ["BSZoomGridScrollView"]), 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BSZoomGridScrollView 2 | 3 | BSZoomGridScrollView is a powerful, pure swift iOS UI framework that provides the awesome grid scrollview containing your image array that are able to zoom, tracking your touch area.
4 | 5 | Screenshots 6 | ----------- 7 | 8 | [Click to watch how it works in youtube](https://youtu.be/QTB7GqZL-L8) 9 | 10 |
11 | 12 | ![Alt Text](https://media.giphy.com/media/gF23Vtbn9bCTosr1yn/giphy.gif) 13 | 14 | ![BSZoomGridScrollView Screenshot](https://firebasestorage.googleapis.com/v0/b/boraseoksoon-ff7d3.appspot.com/o/BSZoomGridScrollView%2Fs4.png?alt=media&token=111d96f0-317c-49f0-b115-fb1592ab6299) 15 | 16 | 17 | At a Glance 18 | ----------- 19 | 20 | ```swift 21 | 22 | var body: some View { 23 | /// 😊 That's it. 24 | BSZoomGridScrollView(imagesToZoom: imagesToZoom, 25 | powerOfZoomBounce: .regular, 26 | numberOfColumns: 200, 27 | numberOfRows: 10, 28 | didLongPressItem: { selectedImage in 29 | print("on long press : ", selectedImage) 30 | /// Grab an image user end up choosing. 31 | self.selectedImage = selectedImage 32 | 33 | /// Present! 34 | self.showSelectedImageView.toggle() 35 | }, 36 | didFinishDraggingOnItem: { selectedImage in 37 | print("on drag finish : ", selectedImage) 38 | }) 39 | .edgesIgnoringSafeArea(.all) 40 | .sheet(isPresented:self.$showSelectedImageView) { 41 | /// The example view showing a picked up image. 42 | ShowingSelectedImageView(selectedImage: self.selectedImage) 43 | } 44 | } 45 | ``` 46 | 47 | ## Features 48 | 49 | - [x] Designed for SwiftUI, SwiftUI 100% is supported. 50 | - [x] Complex grid ScrollView UI is provided out of box. 51 | - [x] Tracking user touch area on the grid scrollview, Zooming items is done out of box. 52 | - [x] BSZoomGridScrollView will return an image selected by a user, detected by the internal long press and pan gesture inside out of box. 53 | - [x] Grid UI can be styled for number of columns, rows, zoom effect and images you would like to input to show in the grid. 54 | 55 |
56 | 57 | ## Example 58 | 59 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 60 | It includes examples for UIKit as well as SwiftUI. 61 | 62 | ## Requirements 63 | 64 | - iOS 13.0 or later 65 | - Swift 5.0 or later 66 | - Xcode 11.0 or later 67 | 68 | 69 | Getting Started 70 | --------------- 71 | 72 | * SwiftUI 73 | 74 | ```Swift 75 | import SwiftUI 76 | 77 | /// 🥳 # Step1: let's import! 78 | import BSZoomGridScrollView 79 | 80 | struct ContentView: View { 81 | var itemsToZoom: [UIImage] = { 82 | var images = [UIImage]() 83 | for i in 0...29 { 84 | images.append(UIImage(named: "yourImage\(i)") ?? UIImage()) 85 | } 86 | return images 87 | }() 88 | 89 | var body: some View { 90 | /// 😊 # Step2. That's it. completed! 91 | BSZoomGridScrollView(itemsToZoom: itemsToZoom, 92 | powerOfZoomBounce: .regular, 93 | isBeingDraggingOnItem:{ selectedImage in 94 | /// 95 | }, 96 | didLongPressItem: { selectedImage in 97 | /// Grab an image user ends up choosing. 98 | }, 99 | didFinishDraggingOnItem: { selectedImage in 100 | /// Grab an image user ends up choosing. 101 | }) 102 | .edgesIgnoringSafeArea(.all) 103 | } 104 | } 105 | ``` 106 | 107 | * UIKit 108 | ```Swift 109 | /// 110 | /// To use BSZoomGridScrollView, 111 | /// Please, Follow steps written in the comments with icon like 😀. 112 | /// 113 | 114 | import SwiftUI 115 | import UIKit 116 | 117 | /// 118 | // 😚 #Step1: import BSZoomGridScrollView! 119 | /// 120 | import BSZoomGridScrollView 121 | 122 | class ViewController: UIViewController { 123 | 124 | /// 125 | // 😋 #Step2: declare BSZoomGridScrollView 126 | /// 127 | private lazy var zoomGridScrollViewController: BSZoomGridScrollViewController = { [unowned self] in 128 | /// 129 | /// It can be used on both SwiftUI and UIKit. 130 | /// To see how it works on SwiftUI, 131 | /// please refer to comments in SwiftUI directory -> ContentView.swift 132 | /// 133 | return BSZoomGridScrollViewController(itemsToZoom: self.itemsToZoom, 134 | powerOfZoomBounce: .regular, 135 | scrollEnableButtonTintColor: .black, 136 | scrollEnableButtonBackgroundColor: .white, 137 | isBeingDraggingOnItem:{ [unowned self] selectedImage in 138 | /// 139 | }, 140 | didLongPressItem: { [unowned self] selectedImage in 141 | /// Grab an image user end up choosing. 142 | }, 143 | didFinishDraggingOnItem: { [unowned self] selectedImage in 144 | /// on drag finished 145 | }) 146 | }() 147 | 148 | /// 149 | // prepare any item array to feed to BSZoomGridScrollViewController. 150 | /// 151 | private var itemsToZoom: [Any] = { 152 | var images = [UIImage]() 153 | for i in 0...29 { 154 | images.append(UIImage(named: "s\(i)") ?? UIImage()) 155 | } 156 | return images 157 | }() 158 | 159 | /// 160 | // 😁 #Step3: Present it! 161 | /// 162 | @IBAction func goToBSZoomGridScrollView(_ sender: Any) { 163 | /// 164 | // 😎 That's all. well done. 165 | /// 166 | 167 | self.present(UINavigationController(rootViewController: zoomGridScrollViewController), 168 | animated: true, 169 | completion: nil) 170 | } 171 | 172 | /// 173 | // MARK: - ViewController LifeCycle Methods 174 | /// 175 | override func viewDidLoad() { 176 | super.viewDidLoad() 177 | } 178 | 179 | override func didReceiveMemoryWarning() { 180 | super.didReceiveMemoryWarning() 181 | } 182 | } 183 | 184 | ``` 185 | 186 | ## Installation 187 | 188 | There are four ways to use BSZoomGridScrollView in your project: 189 | - using CocoaPods 190 | - using Swift Package Manager 191 | - manual install (build frameworks or embed Xcode Project) 192 | 193 | ### Installation with CocoaPods 194 | 195 | [CocoaPods](http://cocoapods.org/) is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries in your projects. See the [Get Started](http://cocoapods.org/#get_started) section for more details. 196 | 197 | #### Podfile 198 | 199 | First, 200 | ```ruby 201 | pod 'BSZoomGridScrollView' 202 | ``` 203 | then in your root project, 204 | ```ruby 205 | pod install 206 | ``` 207 | 208 | ### Installation with Swift Package Manager (Xcode 11+) 209 | 210 | [Swift Package Manager](https://swift.org/package-manager/) (SwiftPM) is a tool for managing the distribution of Swift code as well as C-family dependency. From Xcode 11, SwiftPM got natively integrated with Xcode. 211 | 212 | BSZoomGridScrollView support SwiftPM from version 5.1.0. To use SwiftPM, you should use Xcode 11 to open your project. Click `File` -> `Swift Packages` -> `Add Package Dependency`, enter [BSZoomGridScrollView repo's URL](https://github.com/boraseoksoon/BSZoomGridScrollView). Or you can login Xcode with your GitHub account and just type `BSZoomGridScrollView` to search. 213 | 214 | After select the package, you can choose the dependency type (tagged version, branch or commit). Then Xcode will setup all the stuff for you. 215 | 216 | If you're a framework author and use BSZoomGridScrollView as a dependency, update your `Package.swift` file: 217 | 218 | ```swift 219 | let package = Package( 220 | dependencies: [ 221 | .package(url: "https://github.com/boraseoksoon/BSZoomGridScrollView", from: "0.1.1") 222 | ], 223 | // ... 224 | ) 225 | ``` 226 | ## Disclamer 227 | 228 | It was done for my fun. 229 | 230 | ## Author 231 | 232 | boraseoksoon@gmail.com 233 | 234 | ## License 235 | 236 | BSZoomGridScrollView is available under the MIT license. See the LICENSE file for more info. 237 | 238 | 239 | ## References 240 | 241 | [Observable](https://apps.apple.com/us/app/observable/id1488022000?ls=1) : 242 | Photos browsing iOS app where you can download the photos for free as you like. 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boraseoksoon/BSZoomGridScrollView/11f2b1de1882bd7f41fc0128db8e0bbbff0ab5f5/Sources/BSZoomGridScrollView/Classes/.gitkeep -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridScrollView.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import SwiftUI 29 | import UIKit 30 | 31 | /// 32 | /// UIKit -> SwiftUI Coordinator 33 | /// 34 | public struct BSZoomGridScrollView { 35 | // MARK: - Initializer 36 | /// 37 | public init(itemsToZoom: [Any], 38 | powerOfZoomBounce: ZoomBounceRatio, 39 | numberOfColumns: Int = 0, 40 | numberOfRows: Int = 0, 41 | scrollEnableButtonTintColor: UIColor = .black, 42 | scrollEnableButtonBackgroundColor: UIColor = .white, 43 | isBeingDraggingOnItem: ((_: UIImage) -> Void)?, 44 | didTapOnItem: ((_: UIImage) -> Void)?, 45 | didLongPressItem: ((_: UIImage) -> Void)?, 46 | didFinishDraggingOnItem: ((_: UIImage) -> Void)?) { 47 | self.didTapOnItem = didTapOnItem 48 | self.didLongPressItem = didLongPressItem 49 | self.didFinishDraggingOnItem = didFinishDraggingOnItem 50 | self.isBeingDraggingOnItem = isBeingDraggingOnItem 51 | 52 | self.itemsToZoom = itemsToZoom 53 | self.powerOfZoomBounce = powerOfZoomBounce 54 | self.scrollEnableButtonTintColor = scrollEnableButtonTintColor 55 | self.scrollEnableButtonBackgroundColor = scrollEnableButtonBackgroundColor 56 | 57 | self.numberOfColumns = numberOfColumns 58 | self.numberOfRows = numberOfRows 59 | } 60 | 61 | // MARK: - Instance Variables 62 | /// 63 | private let itemsToZoom: [Any] 64 | private let powerOfZoomBounce: ZoomBounceRatio 65 | private let numberOfColumns: Int 66 | private let numberOfRows: Int 67 | private let scrollEnableButtonTintColor: UIColor 68 | private let scrollEnableButtonBackgroundColor: UIColor 69 | private let isBeingDraggingOnItem: ((_: UIImage) -> Void)? 70 | private let didTapOnItem: ((_: UIImage) -> Void)? 71 | private let didLongPressItem: ((_: UIImage) -> Void)? 72 | private let didFinishDraggingOnItem: ((_: UIImage) -> Void)? 73 | } 74 | 75 | // MARK: - Public Methods 76 | /// 77 | extension BSZoomGridScrollView: UIViewControllerRepresentable { 78 | 79 | public func makeCoordinator() -> Coordinator { 80 | Coordinator(self) 81 | } 82 | 83 | public func makeUIViewController(context: Context) -> BSZoomGridScrollViewController { 84 | let scrollViewController = BSZoomGridScrollViewController(itemsToZoom: itemsToZoom, 85 | powerOfZoomBounce: self.powerOfZoomBounce, 86 | numberOfColumns: self.numberOfColumns, 87 | numberOfRows: self.numberOfRows, 88 | scrollEnableButtonTintColor: self.scrollEnableButtonTintColor, 89 | scrollEnableButtonBackgroundColor: self.scrollEnableButtonBackgroundColor, 90 | isBeingDraggingOnItem: self.isBeingDraggingOnItem, 91 | didTapOnItem:self.didTapOnItem, 92 | didLongPressItem:self.didLongPressItem, 93 | didFinishDraggingOnItem:self.didFinishDraggingOnItem) 94 | return scrollViewController 95 | } 96 | 97 | public func updateUIViewController(_ vc: BSZoomGridScrollViewController, context: Context) { 98 | vc.refresh(self.itemsToZoom) 99 | } 100 | 101 | public class Coordinator: NSObject { 102 | private var parent: BSZoomGridScrollView 103 | 104 | public init(_ pageViewController: BSZoomGridScrollView) { 105 | self.parent = pageViewController 106 | } 107 | } 108 | } 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIControl+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/19. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | #if os(iOS) 10 | import UIKit 11 | 12 | @objc private class ClosureBox: NSObject { 13 | let closure: ()->() 14 | 15 | init (_ closure: @escaping ()->()) { 16 | self.closure = closure 17 | } 18 | 19 | @objc func invoke () { 20 | closure() 21 | } 22 | } 23 | 24 | extension UIControl { 25 | func take(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping ()->()) { 26 | let container = ClosureBox(closure) 27 | addTarget(container, action: #selector(ClosureBox.invoke), for: controlEvents) 28 | objc_setAssociatedObject(self, "[\(arc4random())]", container, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) 29 | } 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIImage+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Extension.swift 3 | // BSZoomGridScrollViewSwiftUIExample 4 | // 5 | // Created by Seoksoon Jang on 2019/11/18. 6 | // Copyright © 2019 Seoksoon Jang. All rights reserved. 7 | // 8 | 9 | #if os(iOS) 10 | import UIKit 11 | 12 | extension UIImage { 13 | func resized(to size: CGSize) -> UIImage { 14 | return UIGraphicsImageRenderer(size: size).image { _ in 15 | draw(in: CGRect(origin: .zero, size: size)) 16 | } 17 | } 18 | } 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView/Extension/UIViewController+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Extension.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Seoksoon Jang on 2019/11/19. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | func showToast(message : String, font: UIFont) { 12 | let toast = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 75, 13 | y: self.view.frame.size.height-(self.view.frame.size.height * 0.3), 14 | width: 150, 15 | height: 50)) 16 | toast.backgroundColor = UIColor.black.withAlphaComponent(0.8) 17 | toast.textColor = UIColor.white 18 | toast.font = font 19 | toast.textAlignment = .center; 20 | toast.text = message 21 | toast.alpha = 1.0 22 | toast.layer.cornerRadius = 15; 23 | toast.clipsToBounds = true 24 | 25 | self.view.addSubview(toast) 26 | 27 | UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: { 28 | toast.alpha = 0.0 29 | }, completion: { isCompleted in 30 | if isCompleted { 31 | toast.removeFromSuperview() 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView/Function/Functions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Functions.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | // MARK: - Grand Central Dispatch Wrapper - 31 | func MainQ(completion: @escaping () -> Void) { 32 | DispatchQueue.main.async { 33 | completion() 34 | } 35 | } 36 | 37 | func GlobalQ(threadName: String = Bundle.main.bundleIdentifier ?? "BSZoomGridScrollView", 38 | completion: @escaping () -> Void) { 39 | DispatchQueue.global().async { 40 | completion() 41 | } 42 | } 43 | 44 | let serialQueue = DispatchQueue(label: Bundle.main.bundleIdentifier ?? "BSZoomGridScrollView") 45 | func SerialQ(completion: @escaping () -> Void) { 46 | serialQueue.async { 47 | completion() 48 | } 49 | } 50 | 51 | func Delay(_ delaySeconds: Double, completion: @escaping () -> Void) -> Void { 52 | DispatchQueue.main.asyncAfter(deadline: .now() + delaySeconds) { 53 | completion() 54 | } 55 | } 56 | 57 | #if os(iOS) 58 | import UIKit 59 | // MARK: - Utility 60 | /// 61 | func GetRootViewController() -> UIViewController? { 62 | let keyWindow = UIApplication.shared.connectedScenes 63 | .filter({$0.activationState == .foregroundActive}) 64 | .map({$0 as? UIWindowScene}) 65 | .compactMap({$0}) 66 | .first?.windows 67 | .filter({$0.isKeyWindow}).first 68 | return keyWindow?.rootViewController 69 | } 70 | 71 | func RandomEmoji() -> String { 72 | let range = 0x1F300...0x1F3F0 73 | let index = Int(arc4random_uniform(UInt32(range.count))) 74 | let ord = range.lowerBound + index 75 | guard let scalar = UnicodeScalar(ord) else { return "❓" } 76 | return String(scalar) 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollView/UIKit/BSZoomGridBaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridBaseViewController.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import UIKit 29 | 30 | public class BSZoomGridBaseViewController: UIViewController { 31 | // MARK: - Initializers 32 | /// Using constuctor Dependency Injection, all initialization should be done in the constructor. 33 | /// 34 | /// - Parameters: 35 | /// - parentView: a parent view to add scrollView as subview 36 | /// - itemsToZoom: item array in grid to be displayed such as [UIImage] 37 | /// if item numbers are not enough to fill the grid, it will be repeated until grid is fully drawn. 38 | /// - powerOfZoomBounce: a value to be able to choose from enum four enumeration types 39 | /// - numberOfRows: number of row to be applied in a row. 40 | /// - didLongPressItem: closure that will indicates which UIImage is decided to be chosen, by a long touch. 41 | /// - didFinishDraggingOnItem: closure that will indicates 42 | /// which UIImage is decided to be chosen, by a end of pan gesture touch. 43 | /// - Returns: Initializer 44 | public init(itemsToZoom: [Any], 45 | powerOfZoomBounce: ZoomBounceRatio, 46 | numberOfColumns: Int = 0, 47 | numberOfRows: Int = 0, 48 | scrollEnableButtonTintColor: UIColor = .black, 49 | scrollEnableButtonBackgroundColor: UIColor = .white, 50 | isBeingDraggingOnItem: ((_: UIImage) -> Void)?, 51 | didTapOnItem: ((_: UIImage) -> Void)?, 52 | didLongPressItem: ((_: UIImage) -> Void)?, 53 | didFinishDraggingOnItem: ((_: UIImage) -> Void)?) { 54 | /// Closures 55 | self.didTapOnItem = didTapOnItem 56 | self.didLongPressItem = didLongPressItem 57 | self.didFinishDraggingOnItem = didFinishDraggingOnItem 58 | self.isBeingDraggingOnItem = isBeingDraggingOnItem 59 | 60 | self.itemsToZoom = itemsToZoom 61 | self.powerOfZoomBounce = powerOfZoomBounce 62 | 63 | super.init(nibName: nil, bundle: nil) 64 | 65 | self.numberOfColumns = numberOfColumns 66 | self.numberOfRows = numberOfRows 67 | self.scrollEnableButtonTintColor = scrollEnableButtonTintColor 68 | self.scrollEnableButtonBackgroundColor = scrollEnableButtonBackgroundColor 69 | } 70 | 71 | required public init?(coder: NSCoder) { 72 | fatalError(""" 73 | init(coder:) has not been implemented.\n 74 | Creating BSZoomGridScrollViewController programmatically is only allowed. 75 | """) 76 | } 77 | 78 | // MARK: - Private Instance Variables 79 | /// private accessor goes here. 80 | internal private(set) var didLongPressItem: ((_: UIImage) -> Void)? 81 | internal private(set) var didFinishDraggingOnItem: ((_: UIImage) -> Void)? 82 | internal private(set) var didTapOnItem: ((_: UIImage) -> Void)? 83 | internal private(set) var isBeingDraggingOnItem: ((_: UIImage) -> Void)? 84 | 85 | internal private(set) var itemsToZoom: [Any] 86 | 87 | internal private(set) var powerOfZoomBounce: ZoomBounceRatio 88 | 89 | private var _scrollEnableButtonBackgroundColor: UIColor = .black 90 | internal var scrollEnableButtonBackgroundColor: UIColor { 91 | get { 92 | return _scrollEnableButtonBackgroundColor 93 | } 94 | set { 95 | _scrollEnableButtonBackgroundColor = newValue 96 | } 97 | } 98 | 99 | private var _scrollEnableButtonTintColor: UIColor = .black 100 | internal var scrollEnableButtonTintColor: UIColor { 101 | get { 102 | return _scrollEnableButtonTintColor 103 | } 104 | set { 105 | _scrollEnableButtonTintColor = newValue 106 | } 107 | } 108 | 109 | private var _numberOfColumns: CGFloat = 70.0 110 | internal private(set) var numberOfColumns: Int { 111 | get { 112 | return Int(_numberOfColumns) 113 | } 114 | set { 115 | _numberOfColumns = CGFloat(newValue) 116 | } 117 | } 118 | 119 | private var _numberOfRows: CGFloat = 30.0 120 | internal private(set) var numberOfRows: Int { 121 | get { 122 | return Int(_numberOfRows) 123 | } 124 | set { 125 | _numberOfRows = CGFloat(newValue) 126 | } 127 | } 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/BSZoomGridScrollViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BSZoomGridScrollViewController.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(iOS) 28 | import UIKit 29 | 30 | /// 31 | /// 😁 #Step1: Inherits BSZoomGridBaseViewController in order to inject constructor including instances variables, 32 | /// Actually, it does not do any important tasks. 33 | /// So, you can ignore inheritance. By doing so, however, currently you can't change configuration. 34 | public final class BSZoomGridScrollViewController: BSZoomGridBaseViewController { 35 | /// 36 | /// 🤔 #Step2: These buttons are not a mandatory, 37 | /// but it will help us scroll up and down in the grid scroll view. 38 | 39 | // MARK: - Instance Variables 40 | /// 41 | 42 | /// To programmtically disable scroll view, 43 | /// target & action is wrapped by closure. 44 | /// FYI: UIControl+Extension 45 | private lazy var disableOrNotButton: UIButton = { [unowned self] in 46 | let b = UIButton() 47 | b.frame = CGRect(x: UIScreen.main.bounds.width - (UIScreen.main.bounds.width * 0.25), 48 | y: UIScreen.main.bounds.height - (UIScreen.main.bounds.height * 0.25), 49 | width: BSZoomGridUIScrollView.ICON_WIDTH, 50 | height: BSZoomGridUIScrollView.ICON_WIDTH) 51 | 52 | b.setBackgroundImage(UIImage(systemName: "circle"), 53 | for: .normal) 54 | 55 | b.setBackgroundImage(UIImage(systemName: "circle.fill"), 56 | for: .highlighted) 57 | 58 | b.tintColor = self.scrollEnableButtonTintColor 59 | b.backgroundColor = self.scrollEnableButtonBackgroundColor 60 | 61 | b.layer.cornerRadius = b.frame.size.width / 2.0 62 | b.clipsToBounds = true 63 | 64 | b.take(for: .touchUpInside) { [unowned self] in 65 | self.zoomGridScrollView.isScrollEnabled = !self.zoomGridScrollView.isScrollEnabled 66 | 67 | self.showToast( 68 | message: self.zoomGridScrollView.isScrollEnabled 69 | ? "Scroll enabled. \(RandomEmoji())" : "Scroll locked. \(RandomEmoji())", 70 | font: .boldSystemFont(ofSize: 16) 71 | ) 72 | } 73 | 74 | return b 75 | }() 76 | 77 | /// 78 | /// 😊 #Step3: Init BSZoomGridUIScrollView, ready to use! 79 | private lazy var zoomGridScrollView: BSZoomGridUIScrollView = { [unowned self] in 80 | return BSZoomGridUIScrollView(parentView: self.view, 81 | itemsToZoom: self.itemsToZoom, 82 | powerOfZoomBounce: self.powerOfZoomBounce, 83 | numberOfColumns: self.numberOfColumns, 84 | numberOfRows: self.numberOfRows, 85 | isBeingDraggingOnItem: self.isBeingDraggingOnItem, 86 | didTapOnItem: self.didTapOnItem, 87 | didLongPressItem: self.didLongPressItem, 88 | didFinishDraggingOnItem: self.didFinishDraggingOnItem) 89 | }() 90 | 91 | // MARK: - View LifeCycle Methods 92 | /// 93 | override public func viewDidLoad() { 94 | super.viewDidLoad() 95 | 96 | /// 97 | /// 😎 # Step4: That's it. well done! 98 | /// Add all views created so far. 99 | self.view.addSubview(self.zoomGridScrollView) 100 | self.view.addSubview(self.disableOrNotButton) 101 | } 102 | 103 | } 104 | 105 | // MARK: - Public Methods 106 | /// 107 | extension BSZoomGridScrollViewController { 108 | /// Refresh and redraw array items in the grid 109 | /// 110 | /// - Parameters: 111 | /// - compositingOperation: The compositing operation of creating image. 112 | /// - alpha: The alpha should be used for image. 113 | /// - backgroundColor: The background color for the output image. 114 | /// - Returns: An image with compositing operation applied. 115 | /// 116 | /// - Note: This method only works for CG-based image. For any non-CG-based image, `base` itself is returned. 117 | 118 | public func refresh(_ itemsToZoom: [Any]) -> Void { 119 | self.zoomGridScrollView.refresh(itemsToZoom) 120 | } 121 | } 122 | #endif 123 | -------------------------------------------------------------------------------- /Sources/BSZoomGridScrollView/Classes/Enumeration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Enumeration.swift 3 | // BSZoomGridScrollView 4 | // 5 | // Created by Jang seoksoon on 2019/11/19. 6 | // 7 | // Copyright (c) 2019 Jang seoksoon 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | // MARK: - Enumeration 30 | /// it will decide bounce animation ratio. it should be only used and injected in the constructor. 31 | public enum ZoomBounceRatio { 32 | case weak 33 | case regular 34 | case strong 35 | case crazy 36 | } 37 | 38 | -------------------------------------------------------------------------------- /Tests/BSZoomGridScrollViewTests/BSZoomGridScrollViewTests.swift: -------------------------------------------------------------------------------- 1 | //import XCTest 2 | //@testable import BSZoomGridScrollView 3 | // 4 | //final class BSZoomGridScrollViewTests: XCTestCase { 5 | // func testExample() { 6 | // // This is an example of a functional test case. 7 | // // Use XCTAssert and related functions to verify your tests produce the correct 8 | // // results. 9 | // XCTAssertEqual(BSZoomGridScrollView().text, "Hello, World!") 10 | // } 11 | // 12 | // static var allTests = [ 13 | // ("testExample", testExample), 14 | // ] 15 | //} 16 | -------------------------------------------------------------------------------- /Tests/BSZoomGridScrollViewTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(BSZoomGridScrollViewTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import BSZoomGridScrollViewTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += BSZoomGridScrollViewTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------