()
201 | visiblePages
202 | .filter { $0.captionView != nil }
203 | .forEach { captionViews.insert($0.captionView) }
204 | return captionViews
205 | }
206 |
207 | func setControlsHidden(hidden: Bool) {
208 | let captionViews = getCaptionViews()
209 | let alpha: CGFloat = hidden ? 0.0 : 1.0
210 |
211 | UIView.animate(withDuration: 0.35,
212 | animations: { () -> Void in
213 | captionViews.forEach { $0.alpha = alpha }
214 | }, completion: nil)
215 | }
216 | }
217 |
218 | private extension SKPagingScrollView {
219 | func frameForPageAtIndex(_ index: Int) -> CGRect {
220 | var pageFrame = bounds
221 | pageFrame.size.width -= (2 * sideMargin)
222 | pageFrame.origin.x = (bounds.size.width * CGFloat(index)) + sideMargin
223 | return pageFrame
224 | }
225 |
226 | func createCaptionView(_ index: Int) -> SKCaptionView? {
227 | if let delegate = self.browser?.delegate, let ownCaptionView = delegate.captionViewForPhotoAtIndex?(index: index) {
228 | return ownCaptionView
229 | }
230 | guard let photo = browser?.photoAtIndex(index), photo.caption != nil else {
231 | return nil
232 | }
233 | return SKCaptionView(photo: photo)
234 | }
235 |
236 | func getFirstIndex() -> Int {
237 | let firstIndex = Int(floor((bounds.minX + sideMargin * 2) / bounds.width))
238 | if firstIndex < 0 {
239 | return 0
240 | }
241 | if firstIndex > numberOfPhotos - 1 {
242 | return numberOfPhotos - 1
243 | }
244 | return firstIndex
245 | }
246 |
247 | func getLastIndex() -> Int {
248 | let lastIndex = Int(floor((bounds.maxX - sideMargin * 2 - 1) / bounds.width))
249 | if lastIndex < 0 {
250 | return 0
251 | }
252 | if lastIndex > numberOfPhotos - 1 {
253 | return numberOfPhotos - 1
254 | }
255 | return lastIndex
256 | }
257 | }
258 |
259 |
--------------------------------------------------------------------------------
/SKPhotoBrowser/SKZoomingScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SKZoomingScrollView.swift
3 | // SKViewExample
4 | //
5 | // Created by suzuki_keihsi on 2015/10/01.
6 | // Copyright © 2015 suzuki_keishi. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | open class SKZoomingScrollView: UIScrollView {
12 | var captionView: SKCaptionView!
13 | var photo: SKPhotoProtocol! {
14 | didSet {
15 | imageView.image = nil
16 | if photo != nil && photo.underlyingImage != nil {
17 | displayImage(complete: true)
18 | return
19 | }
20 | if photo != nil {
21 | displayImage(complete: false)
22 | }
23 | }
24 | }
25 |
26 | fileprivate weak var browser: SKPhotoBrowser?
27 |
28 | fileprivate(set) var imageView: SKDetectingImageView!
29 | fileprivate var tapView: SKDetectingView!
30 | fileprivate var indicatorView: SKIndicatorView!
31 |
32 | required public init?(coder aDecoder: NSCoder) {
33 | super.init(coder: aDecoder)
34 | setup()
35 | }
36 |
37 | override init(frame: CGRect) {
38 | super.init(frame: frame)
39 | setup()
40 | }
41 |
42 | convenience init(frame: CGRect, browser: SKPhotoBrowser) {
43 | self.init(frame: frame)
44 | self.browser = browser
45 | setup()
46 | }
47 |
48 | deinit {
49 | browser = nil
50 | }
51 |
52 | func setup() {
53 | // tap
54 | tapView = SKDetectingView(frame: bounds)
55 | tapView.delegate = self
56 | tapView.backgroundColor = .clear
57 | tapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
58 | addSubview(tapView)
59 |
60 | // image
61 | imageView = SKDetectingImageView(frame: frame)
62 | imageView.delegate = self
63 | imageView.contentMode = .bottom
64 | imageView.backgroundColor = .clear
65 | addSubview(imageView)
66 |
67 | // indicator
68 | indicatorView = SKIndicatorView(frame: frame)
69 | addSubview(indicatorView)
70 |
71 | // self
72 | backgroundColor = .clear
73 | delegate = self
74 | showsHorizontalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
75 | showsVerticalScrollIndicator = SKPhotoBrowserOptions.displayVerticalScrollIndicator
76 | autoresizingMask = [.flexibleWidth, .flexibleTopMargin, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin]
77 | }
78 |
79 | // MARK: - override
80 |
81 | open override func layoutSubviews() {
82 | tapView.frame = bounds
83 | indicatorView.frame = bounds
84 |
85 | super.layoutSubviews()
86 |
87 | let boundsSize = bounds.size
88 | var frameToCenter = imageView.frame
89 |
90 | // horizon
91 | if frameToCenter.size.width < boundsSize.width {
92 | frameToCenter.origin.x = floor((boundsSize.width - frameToCenter.size.width) / 2)
93 | } else {
94 | frameToCenter.origin.x = 0
95 | }
96 | // vertical
97 | if frameToCenter.size.height < boundsSize.height {
98 | frameToCenter.origin.y = floor((boundsSize.height - frameToCenter.size.height) / 2)
99 | } else {
100 | frameToCenter.origin.y = 0
101 | }
102 |
103 | // Center
104 | if !imageView.frame.equalTo(frameToCenter) {
105 | imageView.frame = frameToCenter
106 | }
107 | }
108 |
109 | open func setMaxMinZoomScalesForCurrentBounds() {
110 | maximumZoomScale = 1
111 | minimumZoomScale = 1
112 | zoomScale = 1
113 |
114 | guard let imageView = imageView else {
115 | return
116 | }
117 |
118 | let boundsSize = bounds.size
119 | let imageSize = imageView.frame.size
120 |
121 | let xScale = boundsSize.width / imageSize.width
122 | let yScale = boundsSize.height / imageSize.height
123 | var minScale: CGFloat = min(xScale.isNormal ? xScale : 1.0, yScale.isNormal ? yScale : 1.0)
124 | var maxScale: CGFloat = 1.0
125 |
126 | let scale = max(SKMesurement.screenScale, 2.0)
127 | let deviceScreenWidth = SKMesurement.screenWidth * scale // width in pixels. scale needs to remove if to use the old algorithm
128 | let deviceScreenHeight = SKMesurement.screenHeight * scale // height in pixels. scale needs to remove if to use the old algorithm
129 |
130 | if SKPhotoBrowserOptions.longPhotoWidthMatchScreen && imageView.frame.height >= imageView.frame.width {
131 | minScale = 1.0
132 | maxScale = 2.5
133 | } else if imageView.frame.width < deviceScreenWidth {
134 | // I think that we should to get coefficient between device screen width and image width and assign it to maxScale. I made two mode that we will get the same result for different device orientations.
135 | if UIApplication.shared.statusBarOrientation.isPortrait {
136 | maxScale = deviceScreenHeight / imageView.frame.width
137 | } else {
138 | maxScale = deviceScreenWidth / imageView.frame.width
139 | }
140 | } else if imageView.frame.width > deviceScreenWidth {
141 | maxScale = 1.0
142 | } else {
143 | // here if imageView.frame.width == deviceScreenWidth
144 | maxScale = 2.5
145 | }
146 |
147 | maximumZoomScale = maxScale
148 | minimumZoomScale = minScale
149 | zoomScale = minScale
150 |
151 | // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
152 | // maximum zoom scale to 0.5
153 | // After changing this value, we still never use more
154 | maxScale /= scale
155 | if maxScale < minScale {
156 | maxScale = minScale * 2
157 | }
158 |
159 | // reset position
160 | imageView.frame.origin = CGPoint.zero
161 | setNeedsLayout()
162 | }
163 |
164 | open func prepareForReuse() {
165 | photo = nil
166 | if captionView != nil {
167 | captionView.removeFromSuperview()
168 | captionView = nil
169 | }
170 | }
171 |
172 | open func displayImage(_ image: UIImage) {
173 | // image
174 | imageView.image = image
175 | imageView.contentMode = photo.contentMode
176 |
177 | var imageViewFrame: CGRect = .zero
178 | imageViewFrame.origin = .zero
179 | // long photo
180 | if SKPhotoBrowserOptions.longPhotoWidthMatchScreen && image.size.height >= image.size.width {
181 | let imageHeight = SKMesurement.screenWidth / image.size.width * image.size.height
182 | imageViewFrame.size = CGSize(width: SKMesurement.screenWidth, height: imageHeight)
183 | } else {
184 | imageViewFrame.size = image.size
185 | }
186 | imageView.frame = imageViewFrame
187 |
188 | contentSize = imageViewFrame.size
189 | setMaxMinZoomScalesForCurrentBounds()
190 | }
191 |
192 | // MARK: - image
193 | open func displayImage(complete flag: Bool) {
194 | // reset scale
195 | maximumZoomScale = 1
196 | minimumZoomScale = 1
197 | zoomScale = 1
198 |
199 | if !flag {
200 | if photo.underlyingImage == nil {
201 | indicatorView.startAnimating()
202 | }
203 | photo.loadUnderlyingImageAndNotify()
204 | } else {
205 | indicatorView.stopAnimating()
206 | }
207 |
208 | if let image = photo.underlyingImage, photo != nil {
209 | displayImage(image)
210 | } else {
211 | // change contentSize will reset contentOffset, so only set the contentsize zero when the image is nil
212 | contentSize = CGSize.zero
213 | }
214 | setNeedsLayout()
215 | }
216 |
217 | open func displayImageFailure() {
218 | indicatorView.stopAnimating()
219 | }
220 |
221 | // MARK: - handle tap
222 | open func handleDoubleTap(_ touchPoint: CGPoint) {
223 | if let browser = browser {
224 | NSObject.cancelPreviousPerformRequests(withTarget: browser)
225 | }
226 |
227 | if zoomScale > minimumZoomScale {
228 | // zoom out
229 | setZoomScale(minimumZoomScale, animated: true)
230 | } else {
231 | // zoom in
232 | // I think that the result should be the same after double touch or pinch
233 | /* var newZoom: CGFloat = zoomScale * 3.13
234 | if newZoom >= maximumZoomScale {
235 | newZoom = maximumZoomScale
236 | }
237 | */
238 | let zoomRect = zoomRectForScrollViewWith(maximumZoomScale, touchPoint: touchPoint)
239 | zoom(to: zoomRect, animated: true)
240 | }
241 |
242 | // delay control
243 | browser?.hideControlsAfterDelay()
244 | }
245 | }
246 |
247 | // MARK: - UIScrollViewDelegate
248 |
249 | extension SKZoomingScrollView: UIScrollViewDelegate {
250 | public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
251 | return imageView
252 | }
253 |
254 | public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
255 | browser?.cancelControlHiding()
256 | }
257 |
258 | public func scrollViewDidZoom(_ scrollView: UIScrollView) {
259 | setNeedsLayout()
260 | layoutIfNeeded()
261 | }
262 | }
263 |
264 | // MARK: - SKDetectingImageViewDelegate
265 |
266 | extension SKZoomingScrollView: SKDetectingViewDelegate {
267 | func handleSingleTap(_ view: UIView, touch: UITouch) {
268 | guard let browser = browser else {
269 | return
270 | }
271 | guard SKPhotoBrowserOptions.enableZoomBlackArea == true else {
272 | return
273 | }
274 |
275 | if browser.areControlsHidden() == false && SKPhotoBrowserOptions.enableSingleTapDismiss == true {
276 | browser.determineAndClose()
277 | } else {
278 | browser.toggleControls()
279 | }
280 | }
281 |
282 | func handleDoubleTap(_ view: UIView, touch: UITouch) {
283 | if SKPhotoBrowserOptions.enableZoomBlackArea == true {
284 | let needPoint = getViewFramePercent(view, touch: touch)
285 | handleDoubleTap(needPoint)
286 | }
287 | }
288 | }
289 |
290 | // MARK: - SKDetectingImageViewDelegate
291 |
292 | extension SKZoomingScrollView: SKDetectingImageViewDelegate {
293 | func handleImageViewSingleTap(_ touchPoint: CGPoint) {
294 | guard let browser = browser else {
295 | return
296 | }
297 | if SKPhotoBrowserOptions.enableSingleTapDismiss {
298 | browser.determineAndClose()
299 | } else {
300 | browser.toggleControls()
301 | }
302 | }
303 |
304 | func handleImageViewDoubleTap(_ touchPoint: CGPoint) {
305 | handleDoubleTap(touchPoint)
306 | }
307 | }
308 |
309 | private extension SKZoomingScrollView {
310 | func getViewFramePercent(_ view: UIView, touch: UITouch) -> CGPoint {
311 | let oneWidthViewPercent = view.bounds.width / 100
312 | let viewTouchPoint = touch.location(in: view)
313 | let viewWidthTouch = viewTouchPoint.x
314 | let viewPercentTouch = viewWidthTouch / oneWidthViewPercent
315 | let photoWidth = imageView.bounds.width
316 | let onePhotoPercent = photoWidth / 100
317 | let needPoint = viewPercentTouch * onePhotoPercent
318 |
319 | var Y: CGFloat!
320 |
321 | if viewTouchPoint.y < view.bounds.height / 2 {
322 | Y = 0
323 | } else {
324 | Y = imageView.bounds.height
325 | }
326 | let allPoint = CGPoint(x: needPoint, y: Y)
327 | return allPoint
328 | }
329 |
330 | func zoomRectForScrollViewWith(_ scale: CGFloat, touchPoint: CGPoint) -> CGRect {
331 | let w = frame.size.width / scale
332 | let h = frame.size.height / scale
333 | let x = touchPoint.x - (h / max(SKMesurement.screenScale, 2.0))
334 | let y = touchPoint.y - (w / max(SKMesurement.screenScale, 2.0))
335 |
336 | return CGRect(x: x, y: y, width: w, height: h)
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SKPhotoBrowser
2 |
3 |
4 | Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ## features
25 | - Display one or more images by providing either `UIImage` objects, or string of URL array.
26 | - Photos can be zoomed and panned, and optional captions can be displayed
27 | - Minimalistic Facebook-like interface, swipe up/down to dismiss
28 | - Ability to custom control. (hide/ show toolbar for controls, / swipe control)
29 | - Handling and caching photos from web
30 | - Landscape handling
31 | - Delete photo support(by offbye). By set displayDelete=true show a delete icon in statusbar, deleted indexes can be obtain from delegate func didDeleted
32 |
33 | | Table/CollectionView sample | Button tap sample | gif sample |
34 | | ------------- | --------------- | --------------|
35 | |  |  | 
36 |
37 | ## Requirements
38 | - iOS 9.0+
39 | - Swift 2.0+
40 | - ARC
41 |
42 | ### Version vs Swift version.
43 |
44 | Below is a table that shows which version of SKPhotoBrowser you should use for your Swift version.
45 |
46 | | Swift version | SKPhotoBrowser version |
47 | | ------------- | ---------------|
48 | | 5.0 | >= 6.1.0 |
49 | | 4.2 | >= 6.0.0 |
50 | | 4.1 | >= 5.0.0 |
51 | | 3.2 | >= 4.0.0 |
52 | | 2.3 | 2.0.4 - 3.1.4 |
53 | | 2.2 | <= 2.0.3 |
54 |
55 | ## Installation
56 |
57 | #### CocoaPods
58 | available on CocoaPods. Just add the following to your project Podfile:
59 | ```
60 | pod 'SKPhotoBrowser'
61 | use_frameworks!
62 | ```
63 |
64 | #### Carthage
65 | To integrate into your Xcode project using Carthage, specify it in your Cartfile:
66 | ```
67 | github "suzuki-0000/SKPhotoBrowser"
68 | ```
69 |
70 | #### Info.plist
71 | If you want to use share image feature, it includes save image into galery, so you should specify a permission into your Info.plist (if you haven't done it yet).
72 | ```
73 | NSPhotoLibraryAddUsageDescription
74 | Used to save images into your galery
75 | ```
76 |
77 | #### Swift Package Manager
78 | Available in Swift Package Manager. Use the repository URL in Xcode
79 |
80 | ## Usage
81 | See the code snippet below for an example of how to implement, or see the example project.
82 |
83 | from UIImages:
84 | ```swift
85 | // 1. create SKPhoto Array from UIImage
86 | var images = [SKPhoto]()
87 | let photo = SKPhoto.photoWithImage(UIImage())// add some UIImage
88 | images.append(photo)
89 |
90 | // 2. create PhotoBrowser Instance, and present from your viewController.
91 | let browser = SKPhotoBrowser(photos: images)
92 | browser.initializePageIndex(0)
93 | present(browser, animated: true, completion: {})
94 | ```
95 |
96 | from URLs:
97 | ```swift
98 | // 1. create URL Array
99 | var images = [SKPhoto]()
100 | let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150x150.png")
101 | photo.shouldCachePhotoURLImage = false // you can use image cache by true(NSCache)
102 | images.append(photo)
103 |
104 | // 2. create PhotoBrowser Instance, and present.
105 | let browser = SKPhotoBrowser(photos: images)
106 | browser.initializePageIndex(0)
107 | present(browser, animated: true, completion: {})
108 | ```
109 |
110 | from local files:
111 | ```swift
112 | // 1. create images from local files
113 | var images = [SKLocalPhoto]()
114 | let photo = SKLocalPhoto.photoWithImageURL("..some_local_path/150x150.png")
115 | images.append(photo)
116 |
117 | // 2. create PhotoBrowser Instance, and present.
118 | let browser = SKPhotoBrowser(photos: images)
119 | browser.initializePageIndex(0)
120 | present(browser, animated: true, completion: {})
121 | ```
122 |
123 | If you want to use zooming effect from an existing view, use another initializer:
124 | ```swift
125 | // e.g.: some tableView or collectionView.
126 | func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
127 | let cell = collectionView.cellForItemAtIndexPath(indexPath)
128 | let originImage = cell.exampleImageView.image // some image for baseImage
129 |
130 | let browser = SKPhotoBrowser(originImage: originImage ?? UIImage(), photos: images, animatedFromView: cell)
131 | browser.initializePageIndex(indexPath.row)
132 | present(browser, animated: true, completion: {})
133 | }
134 | ```
135 |
136 | ### Custom
137 |
138 | #### Toolbar
139 | You can customize Toolbar via SKPhotoBrowserOptions.
140 |
141 | ```swift
142 | SKPhotoBrowserOptions.displayToolbar = false // all tool bar will be hidden
143 | SKPhotoBrowserOptions.displayCounterLabel = false // counter label will be hidden
144 | SKPhotoBrowserOptions.displayBackAndForwardButton = false // back / forward button will be hidden
145 | SKPhotoBrowserOptions.displayAction = false // action button will be hidden
146 | SKPhotoBrowserOptions.displayHorizontalScrollIndicator = false // horizontal scroll bar will be hidden
147 | SKPhotoBrowserOptions.displayVerticalScrollIndicator = false // vertical scroll bar will be hidden
148 | let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
149 | ```
150 |
151 | #### Colors
152 | You can customize text, icon and background colors via SKPhotoBrowserOptions or SKToolbarOptions
153 | ```swift
154 | SKPhotoBrowserOptions.backgroundColor = UIColor.whiteColor() // browser view will be white
155 | SKPhotoBrowserOptions.textAndIconColor = UIColor.blackColor() // text and icons will be black
156 | SKToolbarOptions.textShadowColor = UIColor.clearColor() // shadow of toolbar text will be removed
157 | SKToolbarOptions.font = UIFont(name: "Futura", size: 16.0) // font of toolbar will be 'Futura'
158 | ```
159 |
160 | #### Images
161 | You can customize the padding of displayed images via SKPhotoBrowserOptions
162 | ```swift
163 | SKPhotoBrowserOptions.imagePaddingX = 50 // image padding left and right will be 25
164 | SKPhotoBrowserOptions.imagePaddingY = 50 // image padding top and bottom will be 25
165 | ```
166 |
167 | #### Statusbar
168 | You can customize the visibility of the Statusbar in browser view via SKPhotoBrowserOptions
169 | ```swift
170 | SKPhotoBrowserOptions.displayStatusbar = false // status bar will be hidden
171 | ```
172 |
173 | #### Close And Delete Buttons
174 | That how you can customize close and delete buttons
175 | ```swift
176 | SKPhotoBrowserOptions.displayDeleteButton = true // delete button will be shown
177 | SKPhotoBrowserOptions.swapCloseAndDeleteButtons = true // now close button located on right side of screen and delete button is on left side
178 | SKPhotoBrowserOptions.closeAndDeleteButtonPadding = 20 // set offset from top and from nearest screen edge of close button and delete button
179 | ```
180 |
181 | #### Screenshot Protection
182 | You can protect your image from taking screenshot via SKPhotoBrowserOptions
183 | Only working on the device, not on simulator
184 | ```swift
185 | SKPhotoBrowserOptions.protectScreenshot = true // image will be hidden after taking screenshot
186 | ```
187 |
188 | #### Custom Cache From Web URL
189 | You can use SKCacheable protocol if others are adaptable. (SKImageCacheable or SKRequestResponseCacheable)
190 |
191 | ```swift
192 | e.g. SDWebImage
193 |
194 | // 1. create custom cache, implement in accordance with the protocol
195 | class CustomImageCache: SKImageCacheable { var cache: SDImageCache }
196 |
197 | // 2. replace SKCache instance with custom cache
198 | SKCache.sharedCache.imageCache = CustomImageCache()
199 | ```
200 |
201 | #### CustomButton Image
202 | Close, Delete buttons are able to change image and frame.
203 | ```swift
204 | browser.updateCloseButton(UIImage())
205 | browser.updateUpdateButton(UIImage())
206 | ```
207 |
208 | #### Delete Photo
209 | You can delete your photo for your own handling. detect button tap from `removePhoto` delegate function.
210 |
211 |
212 | #### Photo Captions
213 | Photo captions can be displayed simply bottom of PhotoBrowser. by setting the `caption` property on specific photos:
214 | ``` swift
215 | let photo = SKPhoto.photoWithImage(UIImage())
216 | photo.caption = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
217 | ```
218 |
219 | #### SwipeGesture
220 | vertical swipe can enable/disable:
221 | ``` swift
222 | SKPhotoBrowserOptions.disableVerticalSwipe = true
223 | ```
224 |
225 | #### Delegate
226 | There's some trigger point you can handle using delegate. those are optional.
227 | See [SKPhotoBrowserDelegate](https://github.com/suzuki-0000/SKPhotoBrowser/blob/master/SKPhotoBrowser/SKPhotoBrowserDelegate.swift) for more details.
228 | - didShowPhotoAtIndex(_ index:Int)
229 | - willDismissAtPageIndex(_ index:Int)
230 | - willShowActionSheet(_ photoIndex: Int)
231 | - didDismissAtPageIndex(_ index:Int)
232 | - didDismissActionSheetWithButtonIndex(_ buttonIndex: Int, photoIndex: Int)
233 | - didScrollToIndex(_ index: Int)
234 | - removePhoto(_ browser: SKPhotoBrowser, index: Int, reload: (() -> Void))
235 | - viewForPhoto(_ browser: SKPhotoBrowser, index: Int) -> UIView?
236 | - controlsVisibilityToggled(_ browser: SKPhotoBrowser, hidden: Bool)
237 |
238 | ```swift
239 | let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
240 | browser.delegate = self
241 |
242 | // MARK: - SKPhotoBrowserDelegate
243 | func didShowPhotoAtIndex(_ index: Int) {
244 | // when photo will be shown
245 | }
246 |
247 | func willDismissAtPageIndex(_ index: Int) {
248 | // when PhotoBrowser will be dismissed
249 | }
250 |
251 | func didDismissAtPageIndex(_ index: Int) {
252 | // when PhotoBrowser did dismissed
253 | }
254 |
255 | ```
256 |
257 | #### Options
258 | You can access via `SKPhotoBrowserOptions`, which can use for browser control.
259 | See [SKPhotoBrowserOptions](https://github.com/suzuki-0000/SKPhotoBrowser/blob/master/SKPhotoBrowser/SKPhotoBrowserOptions.swift) for more details.
260 | - single tap handling, dismiss/noaction
261 | - blackArea handling which is appearing outside of photo
262 | - bounce animation when appearing/dismissing
263 | - text color, font, or more
264 | ``` swift
265 | SKPhotoBrowserOptions.enableZoomBlackArea = true // default true
266 | SKPhotoBrowserOptions.enableSingleTapDismiss = true // default false
267 | ```
268 |
269 | ## Photos from
270 | - [Unsplash](https://unsplash.com)
271 |
272 | ## License
273 | available under the MIT license. See the LICENSE file for more info.
274 |
275 | ## Contributors
276 |
277 | Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)):
278 |
279 |
280 |
281 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | | [Alexander Khitev ](https://github.com/alexanderkhitev) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=alexanderkhitev "Code") | [K Rummler ](https://github.com/krummler) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=krummler "Code") | [Mads Bjerre ](http://wisekopf.com) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=madsb "Code") | [Meng Ye ](https://jk2K.com) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=jk2K "Code") | [_ant_one ](https://github.com/barrault01) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=barrault01 "Code") | [Tim Roesner ](http://timroesner.com) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=timroesner "Code") | [胥冥 ](http://www.zxming.com) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=zxming "Code") |
295 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
296 | | [Kevin Wolkober ](http://kevinwo.github.io) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=kevinwo "Code") | [PJ Gray ](http://www.saygoodnight.com/) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=pj4533 "Code") | [ghysrc ](https://github.com/ghysrc) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=ghysrc "Code") | [Josef Doležal ](http://josefdolezal.github.com) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=josefdolezal "Code") | [Mark Goody ](https://marramgrass.micro.blog/) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=marramgrass "Code") | [Philippe Riegert ](https://github.com/PhilippeRiegert) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=PhilippeRiegert "Code") | [Bryan Irace ](http://irace.me) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=irace "Code") |
297 | | [dirtmelon ](https://github.com/dirtmelon) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=dirtmelon "Code") | [Heberti Almeida ](https://dribbble.com/hebertialmeida) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=hebertialmeida "Code") | [Felix Weiss ](http://othellogame.net) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=appsunited "Code") | [.Some ](https://github.com/BigDanceMouse) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=BigDanceMouse "Code") | [Onur Var ](https://tr.linkedin.com/in/onur-var) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=OnurVar "Code") | [Andrew Barba ](https://abarba.me) [💻](https://github.com/suzuki-0000/SKPhotoBrowser/commits?author=AndrewBarba "Code") |
298 |
299 |
300 | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
301 |
--------------------------------------------------------------------------------
/SKPhotoBrowserExample/SKPhotoBrowserExample/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 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@babel/runtime@^7.14.6", "@babel/runtime@^7.7.6":
6 | version "7.14.6"
7 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
8 | integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
9 | dependencies:
10 | regenerator-runtime "^0.13.4"
11 |
12 | all-contributors-cli@^6.20.0:
13 | version "6.20.0"
14 | resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.20.0.tgz#9bc98dda38cb29cfe8afc8a78c004e14af25d2f6"
15 | integrity sha512-trEQlL1s1u8FSWSwY2w9uL4GCG7Fo9HIW5rm5LtlE0SQHSolfXQBzJib07Qes5j52/t72wjuE6sEKkuRrwiuuQ==
16 | dependencies:
17 | "@babel/runtime" "^7.7.6"
18 | async "^3.0.1"
19 | chalk "^4.0.0"
20 | didyoumean "^1.2.1"
21 | inquirer "^7.0.4"
22 | json-fixer "^1.5.1"
23 | lodash "^4.11.2"
24 | node-fetch "^2.6.0"
25 | pify "^5.0.0"
26 | yargs "^15.0.1"
27 |
28 | ansi-escapes@^4.2.1:
29 | version "4.3.2"
30 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
31 | integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
32 | dependencies:
33 | type-fest "^0.21.3"
34 |
35 | ansi-regex@^5.0.0:
36 | version "5.0.1"
37 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
38 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
39 |
40 | ansi-styles@^4.0.0, ansi-styles@^4.1.0:
41 | version "4.3.0"
42 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
43 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
44 | dependencies:
45 | color-convert "^2.0.1"
46 |
47 | async@^3.0.1:
48 | version "3.2.4"
49 | resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
50 | integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
51 |
52 | camelcase@^5.0.0:
53 | version "5.3.1"
54 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
55 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
56 |
57 | chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
58 | version "4.1.1"
59 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
60 | integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
61 | dependencies:
62 | ansi-styles "^4.1.0"
63 | supports-color "^7.1.0"
64 |
65 | chardet@^0.7.0:
66 | version "0.7.0"
67 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
68 | integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
69 |
70 | cli-cursor@^3.1.0:
71 | version "3.1.0"
72 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
73 | integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
74 | dependencies:
75 | restore-cursor "^3.1.0"
76 |
77 | cli-width@^3.0.0:
78 | version "3.0.0"
79 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
80 | integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
81 |
82 | cliui@^6.0.0:
83 | version "6.0.0"
84 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
85 | integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
86 | dependencies:
87 | string-width "^4.2.0"
88 | strip-ansi "^6.0.0"
89 | wrap-ansi "^6.2.0"
90 |
91 | color-convert@^2.0.1:
92 | version "2.0.1"
93 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
94 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
95 | dependencies:
96 | color-name "~1.1.4"
97 |
98 | color-name@~1.1.4:
99 | version "1.1.4"
100 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
101 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
102 |
103 | decamelize@^1.2.0:
104 | version "1.2.0"
105 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
106 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
107 |
108 | didyoumean@^1.2.1:
109 | version "1.2.2"
110 | resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
111 | integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
112 |
113 | emoji-regex@^8.0.0:
114 | version "8.0.0"
115 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
116 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
117 |
118 | escape-string-regexp@^1.0.5:
119 | version "1.0.5"
120 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
121 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
122 |
123 | external-editor@^3.0.3:
124 | version "3.1.0"
125 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
126 | integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
127 | dependencies:
128 | chardet "^0.7.0"
129 | iconv-lite "^0.4.24"
130 | tmp "^0.0.33"
131 |
132 | figures@^3.0.0:
133 | version "3.2.0"
134 | resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
135 | integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
136 | dependencies:
137 | escape-string-regexp "^1.0.5"
138 |
139 | find-up@^4.1.0:
140 | version "4.1.0"
141 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
142 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
143 | dependencies:
144 | locate-path "^5.0.0"
145 | path-exists "^4.0.0"
146 |
147 | get-caller-file@^2.0.1:
148 | version "2.0.5"
149 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
150 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
151 |
152 | has-flag@^4.0.0:
153 | version "4.0.0"
154 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
155 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
156 |
157 | iconv-lite@^0.4.24:
158 | version "0.4.24"
159 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
160 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
161 | dependencies:
162 | safer-buffer ">= 2.1.2 < 3"
163 |
164 | inquirer@^7.0.4:
165 | version "7.3.3"
166 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
167 | integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==
168 | dependencies:
169 | ansi-escapes "^4.2.1"
170 | chalk "^4.1.0"
171 | cli-cursor "^3.1.0"
172 | cli-width "^3.0.0"
173 | external-editor "^3.0.3"
174 | figures "^3.0.0"
175 | lodash "^4.17.19"
176 | mute-stream "0.0.8"
177 | run-async "^2.4.0"
178 | rxjs "^6.6.0"
179 | string-width "^4.1.0"
180 | strip-ansi "^6.0.0"
181 | through "^2.3.6"
182 |
183 | is-fullwidth-code-point@^3.0.0:
184 | version "3.0.0"
185 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
186 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
187 |
188 | json-fixer@^1.5.1:
189 | version "1.6.12"
190 | resolved "https://registry.yarnpkg.com/json-fixer/-/json-fixer-1.6.12.tgz#352026c905c6366e214c9f10f77d6d7f93c48322"
191 | integrity sha512-BGO9HExf0ZUVYvuWsps71Re513Ss0il1Wp7wYWkir2NthzincvNJEUu82KagEfAkGdjOMsypj3t2JB7drBKWnA==
192 | dependencies:
193 | "@babel/runtime" "^7.14.6"
194 | chalk "^4.1.1"
195 | pegjs "^0.10.0"
196 |
197 | locate-path@^5.0.0:
198 | version "5.0.0"
199 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
200 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
201 | dependencies:
202 | p-locate "^4.1.0"
203 |
204 | lodash@^4.11.2, lodash@^4.17.19:
205 | version "4.17.21"
206 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
207 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
208 |
209 | mimic-fn@^2.1.0:
210 | version "2.1.0"
211 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
212 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
213 |
214 | mute-stream@0.0.8:
215 | version "0.0.8"
216 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
217 | integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
218 |
219 | node-fetch@^2.6.0:
220 | version "2.6.7"
221 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
222 | integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
223 | dependencies:
224 | whatwg-url "^5.0.0"
225 |
226 | onetime@^5.1.0:
227 | version "5.1.2"
228 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
229 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
230 | dependencies:
231 | mimic-fn "^2.1.0"
232 |
233 | os-tmpdir@~1.0.2:
234 | version "1.0.2"
235 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
236 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
237 |
238 | p-limit@^2.2.0:
239 | version "2.3.0"
240 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
241 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
242 | dependencies:
243 | p-try "^2.0.0"
244 |
245 | p-locate@^4.1.0:
246 | version "4.1.0"
247 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
248 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
249 | dependencies:
250 | p-limit "^2.2.0"
251 |
252 | p-try@^2.0.0:
253 | version "2.2.0"
254 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
255 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
256 |
257 | path-exists@^4.0.0:
258 | version "4.0.0"
259 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
260 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
261 |
262 | pegjs@^0.10.0:
263 | version "0.10.0"
264 | resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd"
265 | integrity sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=
266 |
267 | pify@^5.0.0:
268 | version "5.0.0"
269 | resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f"
270 | integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==
271 |
272 | regenerator-runtime@^0.13.4:
273 | version "0.13.7"
274 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
275 | integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
276 |
277 | require-directory@^2.1.1:
278 | version "2.1.1"
279 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
280 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
281 |
282 | require-main-filename@^2.0.0:
283 | version "2.0.0"
284 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
285 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
286 |
287 | restore-cursor@^3.1.0:
288 | version "3.1.0"
289 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
290 | integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
291 | dependencies:
292 | onetime "^5.1.0"
293 | signal-exit "^3.0.2"
294 |
295 | run-async@^2.4.0:
296 | version "2.4.1"
297 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
298 | integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
299 |
300 | rxjs@^6.6.0:
301 | version "6.6.7"
302 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
303 | integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
304 | dependencies:
305 | tslib "^1.9.0"
306 |
307 | "safer-buffer@>= 2.1.2 < 3":
308 | version "2.1.2"
309 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
310 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
311 |
312 | set-blocking@^2.0.0:
313 | version "2.0.0"
314 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
315 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
316 |
317 | signal-exit@^3.0.2:
318 | version "3.0.3"
319 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
320 | integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
321 |
322 | string-width@^4.1.0, string-width@^4.2.0:
323 | version "4.2.2"
324 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
325 | integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
326 | dependencies:
327 | emoji-regex "^8.0.0"
328 | is-fullwidth-code-point "^3.0.0"
329 | strip-ansi "^6.0.0"
330 |
331 | strip-ansi@^6.0.0:
332 | version "6.0.0"
333 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
334 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
335 | dependencies:
336 | ansi-regex "^5.0.0"
337 |
338 | supports-color@^7.1.0:
339 | version "7.2.0"
340 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
341 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
342 | dependencies:
343 | has-flag "^4.0.0"
344 |
345 | through@^2.3.6:
346 | version "2.3.8"
347 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
348 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
349 |
350 | tmp@^0.0.33:
351 | version "0.0.33"
352 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
353 | integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
354 | dependencies:
355 | os-tmpdir "~1.0.2"
356 |
357 | tr46@~0.0.3:
358 | version "0.0.3"
359 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
360 | integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
361 |
362 | tslib@^1.9.0:
363 | version "1.14.1"
364 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
365 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
366 |
367 | type-fest@^0.21.3:
368 | version "0.21.3"
369 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
370 | integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
371 |
372 | webidl-conversions@^3.0.0:
373 | version "3.0.1"
374 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
375 | integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
376 |
377 | whatwg-url@^5.0.0:
378 | version "5.0.0"
379 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
380 | integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
381 | dependencies:
382 | tr46 "~0.0.3"
383 | webidl-conversions "^3.0.0"
384 |
385 | which-module@^2.0.0:
386 | version "2.0.0"
387 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
388 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
389 |
390 | wrap-ansi@^6.2.0:
391 | version "6.2.0"
392 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
393 | integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
394 | dependencies:
395 | ansi-styles "^4.0.0"
396 | string-width "^4.1.0"
397 | strip-ansi "^6.0.0"
398 |
399 | y18n@^4.0.0:
400 | version "4.0.3"
401 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
402 | integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
403 |
404 | yargs-parser@^18.1.2:
405 | version "18.1.3"
406 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
407 | integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
408 | dependencies:
409 | camelcase "^5.0.0"
410 | decamelize "^1.2.0"
411 |
412 | yargs@^15.0.1:
413 | version "15.4.1"
414 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
415 | integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
416 | dependencies:
417 | cliui "^6.0.0"
418 | decamelize "^1.2.0"
419 | find-up "^4.1.0"
420 | get-caller-file "^2.0.1"
421 | require-directory "^2.1.1"
422 | require-main-filename "^2.0.0"
423 | set-blocking "^2.0.0"
424 | string-width "^4.2.0"
425 | which-module "^2.0.0"
426 | y18n "^4.0.0"
427 | yargs-parser "^18.1.2"
428 |
--------------------------------------------------------------------------------