Compare commits
72 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
e87ec9eb72 | |
|
|
2db02eecc0 | |
|
|
d40c5d2c2e | |
|
|
5714a95908 | |
|
|
8d2c1f3b38 | |
|
|
464114e40d | |
|
|
c3f8fa7e8a | |
|
|
3b275293b5 | |
|
|
6a591bcd5d | |
|
|
26378bae91 | |
|
|
709f024135 | |
|
|
49131da2e0 | |
|
|
1704f37363 | |
|
|
554dac6756 | |
|
|
de1f040c91 | |
|
|
c9e3749b0b | |
|
|
e55631cdec | |
|
|
d1b266c5b3 | |
|
|
2e5706c601 | |
|
|
06cd9c082d | |
|
|
a3b47e543e | |
|
|
4c068d83be | |
|
|
6ae7ad024e | |
|
|
5017c33a78 | |
|
|
08aeb6a147 | |
|
|
a53fad69a4 | |
|
|
95755537c3 | |
|
|
2dce338349 | |
|
|
850d584b57 | |
|
|
8fcdfcf535 | |
|
|
d5b42c8b47 | |
|
|
3d46160ef1 | |
|
|
9befa4cc10 | |
|
|
bde450fcff | |
|
|
b93e85dad0 | |
|
|
a091e2e94e | |
|
|
a9561d61d7 | |
|
|
f633789c65 | |
|
|
dca1f9dd4b | |
|
|
679a5683f7 | |
|
|
c113f68b63 | |
|
|
d25f78954d | |
|
|
fa6b95eebe | |
|
|
b4d911bee3 | |
|
|
bf45bca847 | |
|
|
d6ecf61405 | |
|
|
934831b4bf | |
|
|
4a76fa9b30 | |
|
|
0d0aa326da | |
|
|
a0a8e9119e | |
|
|
31e3378c9a | |
|
|
9b77a81734 | |
|
|
50fe55684f | |
|
|
2362808151 | |
|
|
6d6a93a1ae | |
|
|
cb53afd3a2 | |
|
|
733b97cba5 | |
|
|
0dd21f9ef7 | |
|
|
90ec128c11 | |
|
|
a066d956ff | |
|
|
d2e350c1d3 | |
|
|
96fed7a499 | |
|
|
823fe89a1c | |
|
|
cdef28539e | |
|
|
8d2d24ff4f | |
|
|
4b897dcae0 | |
|
|
6be29d1413 | |
|
|
99ed57e73e | |
|
|
711b492f0b | |
|
|
f0b3224892 | |
|
|
d931f4e4ec | |
|
|
770e711e4c |
|
|
@ -0,0 +1,186 @@
|
|||
# Change Log
|
||||
|
||||
## 3.1.2
|
||||
|
||||
Released on 16-9-2016
|
||||
|
||||
#### Fixed
|
||||
- Scrolling performance slowed #145
|
||||
|
||||
## 3.1.1
|
||||
|
||||
Released on 15-9-2016
|
||||
|
||||
#### Fixed
|
||||
- Example crash in xcode8 fixed
|
||||
- Provides various UI configuration options via SKPhotoBrowserOptions. #144
|
||||
|
||||
## 3.1.0
|
||||
|
||||
Released on 9-2016
|
||||
|
||||
#### Fixed
|
||||
- Issue with multiple actionButtonTitles #137
|
||||
- fix swiftlint warnings #140
|
||||
- Update for Xcode 8 GM (swift 2.3). #141
|
||||
|
||||
## 3.0.2
|
||||
|
||||
Released on 9-2016
|
||||
|
||||
#### Fixed
|
||||
- Issue with multiple actionButtonTitles #137
|
||||
- Impossible to zoom when resolution is 1024x768 #134
|
||||
- Crash bug at zooming scrool view #133
|
||||
|
||||
## 3.0.1
|
||||
|
||||
Released on 9-2016
|
||||
|
||||
#### Fixed
|
||||
- Skip loading image if already loaded #135
|
||||
|
||||
Released on 8-2016
|
||||
|
||||
#### Some Interface is removed, changed this version.
|
||||
- status bar handling is removed.
|
||||
- custom button handling interface is chagned.
|
||||
- custom option goes internal/private. use option via SKPhotoBrowserOptions.
|
||||
|
||||
#### Add
|
||||
- Add changelog
|
||||
|
||||
#### Fixed
|
||||
- prepare for swift3.0.
|
||||
- refactoring code for new implement.
|
||||
- Parent View disappears when dismissed. #120
|
||||
- Glitch when origin imageview is not correct size #108
|
||||
- Problems with the "long" photo #116
|
||||
|
||||
#### Remove
|
||||
- Statusbar handling.
|
||||
- Some public property to internal for improving
|
||||
|
||||
## 2.0.x
|
||||
Released on 8-2016
|
||||
|
||||
#### Added
|
||||
- Migrate UIImage cache category to new SKCache
|
||||
|
||||
#### Fixed
|
||||
- Make cached response data return optional
|
||||
- Fixed issue when animatedFromView not has a superview but has superlayer
|
||||
- Fixed when image downloaded then not show activityindicator
|
||||
- Update for Swift2.3
|
||||
|
||||
---
|
||||
|
||||
## 1.9.x
|
||||
Released on 6-2016
|
||||
|
||||
#### Added
|
||||
- Delegate to notify when the user scroll to an index
|
||||
- Single tap to dismiss
|
||||
|
||||
#### Fixed
|
||||
- Fixed a bug where the activity indicator was only visible
|
||||
- Fixed unit test and problems running when being bridged
|
||||
|
||||
---
|
||||
|
||||
## 1.8.x
|
||||
Released on 4-2016
|
||||
|
||||
#### Added
|
||||
- Using SKPhotoProtocol to enable usage from SKLocalPhoto
|
||||
- SKLocalPhoto to support local photo from file
|
||||
|
||||
#### Fixed
|
||||
- Bug when animation when tap.
|
||||
- The indicator may not disappear when loading local image
|
||||
- Event crash when closing before image has been loaded
|
||||
- Fix crash on initialisation
|
||||
|
||||
---
|
||||
|
||||
## 1.7.x
|
||||
Released on 3-2016
|
||||
|
||||
#### Added
|
||||
- Enable ability to override statusBar style
|
||||
|
||||
#### Fixed
|
||||
- Update for swift2.0
|
||||
- Bug when zooming small image
|
||||
- Prevent crash when closing before image has been loaded
|
||||
|
||||
---
|
||||
|
||||
## 1.6.x
|
||||
Released on 2016-3
|
||||
|
||||
#### Fixed
|
||||
- Change maxScale to 1.0 it works perfectly.
|
||||
- Fixed the bug which was after the device rotation
|
||||
|
||||
---
|
||||
|
||||
## 1.5.x
|
||||
Released on 2016-3
|
||||
|
||||
#### Added
|
||||
- Delete Button
|
||||
|
||||
#### Fixed
|
||||
- Change maxScale to 1.0 it works perfectly.
|
||||
- Rew algorithm for maxScale.
|
||||
- Changed UIActionSheet to UIAlertController with ActionSheet style
|
||||
|
||||
---
|
||||
|
||||
## 1.4.x
|
||||
Released on 2-2016
|
||||
|
||||
#### Added
|
||||
- Delegate add for actionbutton.
|
||||
- DidShowPhotoAtIndex delegate goes to optional.
|
||||
|
||||
#### Fixed
|
||||
- Zooming bug fixed.
|
||||
|
||||
---
|
||||
|
||||
## 1.3.x
|
||||
Released on 1-2016
|
||||
|
||||
#### Added
|
||||
- Added action functionality similar to IDMPhotoBrowser.
|
||||
- Add extra caption for share
|
||||
|
||||
#### Fixed
|
||||
- Bug fixed for mail crash
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 1.2.x
|
||||
Released on 10-2015
|
||||
|
||||
#### Added
|
||||
- SKPhotoProtocol is implemented.
|
||||
|
||||
#### Fixed
|
||||
- Double tap bug fixed
|
||||
|
||||
---
|
||||
|
||||
## 1.1.x
|
||||
Released on 10-2015
|
||||
|
||||
#### Fixed
|
||||
- some property make private.
|
||||
- layout bug fixed when zoom.
|
||||
|
||||
## 1.0.0
|
||||
Released on 10-2015
|
||||
|
||||
123
README.md
123
README.md
|
|
@ -1,18 +1,22 @@
|
|||
SKPhotoBrowser
|
||||
========================
|
||||
|
||||

|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/SKPhotoBrowser)
|
||||
|
||||
Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift, based on [IDMPhotoBrowser](https://github.com/ideaismobile/IDMPhotoBrowser), [MWPhotoBrowser](https://github.com/mwaterfall/MWPhotoBrowser).
|
||||
|
||||
## Note
|
||||
- released v3.0.x and this version goes some breaking changes. please check [CHANGELOG](https://github.com/suzuki-0000/SKPhotoBrowser/blob/master/CHANGELOG.md).
|
||||
|
||||
## features
|
||||
- Can display one or more images by providing either `UIImage` objects, or string of URL array.
|
||||
- Display one or more images by providing either `UIImage` objects, or string of URL array.
|
||||
- Photos can be zoomed and panned, and optional captions can be displayed
|
||||
- Minimalistic Facebook-like interface, swipe up/down to dismiss
|
||||
- has simple ability to custom photobrowser. (hide/show statusbar, some toolbar for controls, swipe control)
|
||||
- Handling and caching photos from web
|
||||
- Landscape handling.
|
||||
- Ability to custom control. (hide/ show toolbar for controls, / swipe control)
|
||||
- Handling and caching photos from web
|
||||
- Landscape handling
|
||||
- Delete photo support(by offbye). By set displayDelete=true show a delete icon in statusbar, deleted indexes can be obtain from delegate func didDeleted
|
||||
|
||||

|
||||
|
|
@ -42,43 +46,45 @@ github "suzuki-0000/SKPhotoBrowser"
|
|||
Add the code directly into your project.
|
||||
|
||||
##Usage
|
||||
See the code snippet below for an example of how to implement, or example project would be easy to understand.
|
||||
See the code snippet below for an example of how to implement, or see the example project.
|
||||
|
||||
from UIImages:
|
||||
```swift
|
||||
// add SKPhoto Array from UIImage
|
||||
// 1. create SKPhoto Array from UIImage
|
||||
var images = [SKPhoto]()
|
||||
let photo = SKPhoto.photoWithImage(UIImage())// add some UIImage
|
||||
images.append(photo)
|
||||
|
||||
// create PhotoBrowser Instance, and present.
|
||||
// 2. create PhotoBrowser Instance, and present from your viewController.
|
||||
let browser = SKPhotoBrowser(photos: images)
|
||||
browser.initializePageIndex(0)
|
||||
browser.delegate = self
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
```
|
||||
|
||||
from web URLs:
|
||||
from URLs:
|
||||
```swift
|
||||
// URL pattern snippet
|
||||
// 1. create URL Array
|
||||
var images = [SKPhoto]()
|
||||
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150x150.png")
|
||||
photo.shouldCachePhotoURLImage = false // you can use image cache by true(NSCache)
|
||||
images.append(photo)
|
||||
|
||||
// create PhotoBrowser Instance, and present.
|
||||
// 2. create PhotoBrowser Instance, and present.
|
||||
let browser = SKPhotoBrowser(photos: images)
|
||||
browser.initializePageIndex(0)
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
```
|
||||
|
||||
from local files:
|
||||
```swift
|
||||
// images from local files
|
||||
// 1. create images from local files
|
||||
var images = [SKLocalPhoto]()
|
||||
let photo = SKLocalPhoto.photoWithImageURL("..some_local_path/150x150.png")
|
||||
images.append(photo)
|
||||
|
||||
// create PhotoBrowser Instance, and present.
|
||||
// 2. create PhotoBrowser Instance, and present.
|
||||
let browser = SKPhotoBrowser(photos: images)
|
||||
browser.initializePageIndex(0)
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
```
|
||||
|
||||
|
|
@ -88,6 +94,7 @@ If you want to use zooming effect from an existing view, use another initializer
|
|||
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
|
||||
let cell = collectionView.cellForItemAtIndexPath(indexPath)
|
||||
let originImage = cell.exampleImageView.image // some image for baseImage
|
||||
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
browser.initializePageIndex(indexPath.row)
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
|
|
@ -97,40 +104,65 @@ func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath i
|
|||
### Custom
|
||||
|
||||
#### Toolbar
|
||||
You can customize the toolbar(back/forward, counter, some action) button.
|
||||
- displayCounterLabel (default is true)
|
||||
- displayBackAndForwardButton (default is true)
|
||||
- displayAction (default is true)
|
||||
|
||||
If you dont want the toolbar at all, you can set displayToolbar = false (default is true)
|
||||
You can customize Toolbar via SKPhotoBrowserOptions.
|
||||
|
||||
```swift
|
||||
SKPhotoBrowserOptions.displayToolbar = false // all tool bar will be hidden
|
||||
SKPhotoBrowserOptions.displayCounterLabel = false // counter label will be hidden
|
||||
SKPhotoBrowserOptions.displayBackAndForwardButton = false // back / forward button will be hidden
|
||||
SKPhotoBrowserOptions.displayAction = false // action button will be hidden
|
||||
SKPhotoBrowserOptions.displayDeleteButton = true // delete button will be shown
|
||||
SKPhotoBrowserOptions.displayHorizontalScrollIndicator = false // horizontal scroll bar will be hidden
|
||||
SKPhotoBrowserOptions.displayVerticalScrollIndicator = false // vertical scroll bar will be hidden
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
browser.displayToolbar = false // all tool bar will be hidden
|
||||
browser.displayCounterLabel = false // counter label will be hidden
|
||||
browser.displayBackAndForwardButton = false // back / forward button will be hidden
|
||||
browser.displayAction = false // action button will be hidden
|
||||
browser.displayDeleteButton = true // delete button will be shown
|
||||
```
|
||||
|
||||
#### Colors
|
||||
You can customize text, icon and background colors via SKPhotoBrowserOptions
|
||||
```swift
|
||||
SKPhotoBrowserOptions.backgroundColor = UIColor.whiteColor() // browser view will be white
|
||||
SKPhotoBrowserOptions.textAndIconColor = UIColor.blackColor() // text and icons will be black
|
||||
SKPhotoBrowserOptions.toolbarTextShadowColor = UIColor.clearColor() // shadow of toolbar text will be removed
|
||||
SKPhotoBrowserOptions.toolbarFont = UIFont(name: "Futura", size: 16.0) // font of toolbar will be 'Futura'
|
||||
SKPhotoBrowserOptions.captionFont = UIFont(name: "Helvetica", size: 18.0) // font of toolbar will be 'Helvetica'
|
||||
```
|
||||
|
||||
#### Images
|
||||
You can customize the padding of displayed images via SKPhotoBrowserOptions
|
||||
```swift
|
||||
SKPhotoBrowserOptions.imagePaddingX = 50 // image padding left and right will be 25
|
||||
SKPhotoBrowserOptions.imagePaddingY = 50 // image padding top and bottom will be 25
|
||||
```
|
||||
|
||||
#### Statusbar
|
||||
You can customize the visibility of the Statusbar in browser view via SKPhotoBrowserOptions
|
||||
```swift
|
||||
SKPhotoBrowserOptions.displayStatusbar = false // status bar will be hidden
|
||||
```
|
||||
|
||||
#### Custom Cache From Web URL
|
||||
You can use SKCacheable protocol if others are adaptable. (SKImageCacheable or SKRequestResponseCacheable)
|
||||
|
||||
```swift
|
||||
e.g. SDWebImage
|
||||
|
||||
// 1. create custon cache. implement function for protocol
|
||||
class CustomImageCache: SKImageCacheable { var cache: SDImageCache }
|
||||
|
||||
// 2. replace SKCache instance with custom cache
|
||||
SKCache.sharedCache.imageCache = CustomImageCache()
|
||||
```
|
||||
|
||||
#### CustomButton Image
|
||||
Close button is able to change image and frame.
|
||||
Close, Delete buttons are able to change image and frame.
|
||||
``` swift
|
||||
browser.displayCustomCloseButton = true // custom close button will be enable
|
||||
browser.customCloseButtonImage = UIImage(named: "some.png")
|
||||
browser.customCloseButtonShowFrame = CGRect()
|
||||
browser.customCloseButtonHideFrame = CGRect()
|
||||
```
|
||||
Delete button is able to change image and frame.
|
||||
``` swift
|
||||
browser.displayCustomDeleteButton = true // custom delete button will be enable
|
||||
browser.customDeleteButtonImage = UIImage(named: "some.png")
|
||||
browser.customDeleteButtonShowFrame = CGRect()
|
||||
browser.customDeleteButtonHideFrame = CGRect()
|
||||
browser.updateCloseButton(UIImage())
|
||||
browser.updateUpdateButton(UIImage())
|
||||
```
|
||||
|
||||
#### Delete
|
||||
You can delete your photo for your own hanlding.
|
||||
#### Delete Photo
|
||||
You can delete your photo for your own handling. detect button tap from `removePhoto` delegate function.
|
||||
|
||||
|
||||
#### Photo Captions
|
||||
Photo captions can be displayed simply bottom of PhotoBrowser. by setting the `caption` property on specific photos:
|
||||
|
|
@ -143,8 +175,7 @@ images.append(photo)
|
|||
#### SwipeGesture
|
||||
vertical swipe can enable/disable:
|
||||
``` swift
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
browser.disableVerticalSwipe = true
|
||||
SKPhotoBrowserOptions.disableVerticalSwipe = true
|
||||
```
|
||||
|
||||
#### Delegate
|
||||
|
|
@ -177,14 +208,16 @@ func didDismissAtPageIndex(index: Int) {
|
|||
|
||||
```
|
||||
|
||||
#### Minor Option
|
||||
- blackArea handling which is appearing outside of photo
|
||||
#### Options
|
||||
You can access via `SKPhotoBrowserOptions`, which can use for browser control.
|
||||
- single tap handling, dismiss/noaction
|
||||
- blackArea handling which is appearing outside of photo
|
||||
- bounce animation when appearing/dismissing
|
||||
- text color, font, or more
|
||||
``` swift
|
||||
enableZoomBlackArea = true // default true
|
||||
enableSingleTapDismiss = true // default false
|
||||
bounceAnimation = true // default false
|
||||
SKPhotoBrowserOptions.enableZoomBlackArea = true // default true
|
||||
SKPhotoBrowserOptions.enableSingleTapDismiss = true // default false
|
||||
SKPhotoBrowserOptions.bounceAnimation = true // default false
|
||||
```
|
||||
|
||||
## Photos from
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "SKPhotoBrowser"
|
||||
s.version = "2.0.2"
|
||||
s.version = "3.1.2"
|
||||
s.summary = "Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift2.0."
|
||||
s.homepage = "https://github.com/suzuki-0000/SKPhotoBrowser"
|
||||
s.license = { :type => "MIT", :file => "LICENSE" }
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0AE527521DABB87500619FAD /* SKNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE527511DABB87500619FAD /* SKNavigationBar.swift */; };
|
||||
210E53ED1C986D3A008DD5E3 /* UIView+Radius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210E53EC1C986D3A008DD5E3 /* UIView+Radius.swift */; };
|
||||
210E53EF1C986D57008DD5E3 /* UIImage+Rotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210E53EE1C986D57008DD5E3 /* UIImage+Rotation.swift */; };
|
||||
26C97AD51D0EB6870039F6CB /* SKCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C97AD41D0EB6870039F6CB /* SKCache.swift */; };
|
||||
|
|
@ -21,6 +22,13 @@
|
|||
8909B5491BC791510060A053 /* SKPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B5411BC791510060A053 /* SKPhotoBrowser.swift */; };
|
||||
8909B54A1BC791510060A053 /* SKZoomingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B5421BC791510060A053 /* SKZoomingScrollView.swift */; };
|
||||
8909B54D1BC7916E0060A053 /* SKPhotoBrowser.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */; };
|
||||
890A6F201D5D9E53003B01F0 /* SKToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */; };
|
||||
8917B1B01D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */; };
|
||||
8917B1B41D5A14B0000CE1C4 /* SKButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */; };
|
||||
89C24A821D657AD1005F09A9 /* SKPhotoBrowserOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */; };
|
||||
89C24A841D657AFE005F09A9 /* SKPagingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */; };
|
||||
89D0BA471D5994A8002A811B /* SKAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0BA461D5994A8002A811B /* SKAnimator.swift */; };
|
||||
89D0BA491D59966B002A811B /* SKMesurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0BA481D59966B002A811B /* SKMesurement.swift */; };
|
||||
8CA6C6521CBE76E80054D3C2 /* SKLocalPhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */; };
|
||||
A64B89361CB04222000071B9 /* SKPhotoBrowserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64B89351CB04222000071B9 /* SKPhotoBrowserTests.swift */; };
|
||||
A64B89381CB04222000071B9 /* SKPhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5301BC791280060A053 /* SKPhotoBrowser.framework */; };
|
||||
|
|
@ -37,6 +45,7 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0AE527511DABB87500619FAD /* SKNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKNavigationBar.swift; sourceTree = "<group>"; };
|
||||
210E53EC1C986D3A008DD5E3 /* UIView+Radius.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Radius.swift"; path = "extensions/UIView+Radius.swift"; sourceTree = "<group>"; };
|
||||
210E53EE1C986D57008DD5E3 /* UIImage+Rotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+Rotation.swift"; path = "extensions/UIImage+Rotation.swift"; sourceTree = "<group>"; };
|
||||
26C97AD41D0EB6870039F6CB /* SKCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKCache.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -53,6 +62,13 @@
|
|||
8909B5411BC791510060A053 /* SKPhotoBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowser.swift; sourceTree = "<group>"; };
|
||||
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKZoomingScrollView.swift; sourceTree = "<group>"; };
|
||||
8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SKPhotoBrowser.bundle; sourceTree = "<group>"; };
|
||||
890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKToolbar.swift; sourceTree = "<group>"; };
|
||||
8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserDelegate.swift; sourceTree = "<group>"; };
|
||||
8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKButtons.swift; sourceTree = "<group>"; };
|
||||
89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserOptions.swift; sourceTree = "<group>"; };
|
||||
89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPagingScrollView.swift; sourceTree = "<group>"; };
|
||||
89D0BA461D5994A8002A811B /* SKAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKAnimator.swift; sourceTree = "<group>"; };
|
||||
89D0BA481D59966B002A811B /* SKMesurement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKMesurement.swift; sourceTree = "<group>"; };
|
||||
8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKLocalPhoto.swift; sourceTree = "<group>"; };
|
||||
A64B89331CB04222000071B9 /* SKPhotoBrowserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SKPhotoBrowserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A64B89351CB04222000071B9 /* SKPhotoBrowserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserTests.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -108,17 +124,25 @@
|
|||
8909B5321BC791280060A053 /* SKPhotoBrowser */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
89D0BA461D5994A8002A811B /* SKAnimator.swift */,
|
||||
8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */,
|
||||
26C97AD41D0EB6870039F6CB /* SKCache.swift */,
|
||||
26C97AD81D0EB8BB0039F6CB /* SKCacheable.swift */,
|
||||
8909B53B1BC791510060A053 /* SKCaptionView.swift */,
|
||||
8909B53C1BC791510060A053 /* SKDetectingImageView.swift */,
|
||||
8909B53D1BC791510060A053 /* SKDetectingView.swift */,
|
||||
8909B53E1BC791510060A053 /* SKIndicatorView.swift */,
|
||||
8909B53F1BC791510060A053 /* SKPhoto.swift */,
|
||||
8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */,
|
||||
89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */,
|
||||
8909B53F1BC791510060A053 /* SKPhoto.swift */,
|
||||
8909B5411BC791510060A053 /* SKPhotoBrowser.swift */,
|
||||
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */,
|
||||
89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */,
|
||||
890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */,
|
||||
0AE527511DABB87500619FAD /* SKNavigationBar.swift */,
|
||||
8909B5331BC791280060A053 /* SKPhotoBrowser.h */,
|
||||
8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */,
|
||||
89D0BA481D59966B002A811B /* SKMesurement.swift */,
|
||||
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */,
|
||||
8909B5351BC791280060A053 /* Info.plist */,
|
||||
210E53EB1C986D1C008DD5E3 /* extensions */,
|
||||
8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */,
|
||||
|
|
@ -199,9 +223,11 @@
|
|||
TargetAttributes = {
|
||||
8909B52F1BC791280060A053 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
A64B89321CB04222000071B9 = {
|
||||
CreatedOnToolsVersion = 7.3;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -263,13 +289,21 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
26C97AD91D0EB8BB0039F6CB /* SKCacheable.swift in Sources */,
|
||||
89C24A841D657AFE005F09A9 /* SKPagingScrollView.swift in Sources */,
|
||||
890A6F201D5D9E53003B01F0 /* SKToolbar.swift in Sources */,
|
||||
8909B5441BC791510060A053 /* SKDetectingImageView.swift in Sources */,
|
||||
8909B54A1BC791510060A053 /* SKZoomingScrollView.swift in Sources */,
|
||||
8CA6C6521CBE76E80054D3C2 /* SKLocalPhoto.swift in Sources */,
|
||||
89D0BA471D5994A8002A811B /* SKAnimator.swift in Sources */,
|
||||
210E53ED1C986D3A008DD5E3 /* UIView+Radius.swift in Sources */,
|
||||
8917B1B01D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift in Sources */,
|
||||
89D0BA491D59966B002A811B /* SKMesurement.swift in Sources */,
|
||||
8909B5471BC791510060A053 /* SKPhoto.swift in Sources */,
|
||||
8909B5461BC791510060A053 /* SKIndicatorView.swift in Sources */,
|
||||
8917B1B41D5A14B0000CE1C4 /* SKButtons.swift in Sources */,
|
||||
89C24A821D657AD1005F09A9 /* SKPhotoBrowserOptions.swift in Sources */,
|
||||
26C97AD51D0EB6870039F6CB /* SKCache.swift in Sources */,
|
||||
0AE527521DABB87500619FAD /* SKNavigationBar.swift in Sources */,
|
||||
210E53EF1C986D57008DD5E3 /* UIImage+Rotation.swift in Sources */,
|
||||
8909B5431BC791510060A053 /* SKCaptionView.swift in Sources */,
|
||||
8909B5491BC791510060A053 /* SKPhotoBrowser.swift in Sources */,
|
||||
|
|
@ -402,6 +436,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -420,6 +455,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.SKPhotoBrowser;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -432,6 +468,7 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "Alexsander-Khitev.SKPhotoBrowserTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -444,6 +481,7 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "Alexsander-Khitev.SKPhotoBrowserTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// SKAnimator.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by keishi suzuki on 2016/08/09.
|
||||
// Copyright © 2016 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
@objc public protocol SKPhotoBrowserAnimatorDelegate {
|
||||
func willPresent(browser: SKPhotoBrowser)
|
||||
func willDismiss(browser: SKPhotoBrowser)
|
||||
}
|
||||
|
||||
class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
|
||||
var resizableImageView: UIImageView?
|
||||
|
||||
var senderOriginImage: UIImage!
|
||||
var senderViewOriginalFrame: CGRect = .zero
|
||||
var senderViewForAnimation: UIView?
|
||||
|
||||
var finalImageViewFrame: CGRect = .zero
|
||||
|
||||
var bounceAnimation: Bool = false
|
||||
var animationDuration: NSTimeInterval {
|
||||
if SKPhotoBrowserOptions.bounceAnimation {
|
||||
return 0.5
|
||||
}
|
||||
return 0.35
|
||||
}
|
||||
var animationDamping: CGFloat {
|
||||
if SKPhotoBrowserOptions.bounceAnimation {
|
||||
return 0.8
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func willPresent(browser: SKPhotoBrowser) {
|
||||
guard let appWindow = UIApplication.sharedApplication().delegate?.window else {
|
||||
return
|
||||
}
|
||||
guard let window = appWindow else {
|
||||
return
|
||||
}
|
||||
guard let sender = browser.delegate?.viewForPhoto?(browser, index: browser.initialPageIndex) ?? senderViewForAnimation else {
|
||||
presentAnimation(browser)
|
||||
return
|
||||
}
|
||||
|
||||
let photo = browser.photoAtIndex(browser.currentPageIndex)
|
||||
let imageFromView = (senderOriginImage ?? browser.getImageFromView(sender)).rotateImageByOrientation()
|
||||
let imageRatio = imageFromView.size.width / imageFromView.size.height
|
||||
|
||||
senderViewOriginalFrame = calcOriginFrame(sender)
|
||||
finalImageViewFrame = calcFinalFrame(imageRatio)
|
||||
|
||||
resizableImageView = UIImageView(image: imageFromView)
|
||||
resizableImageView!.frame = senderViewOriginalFrame
|
||||
resizableImageView!.clipsToBounds = true
|
||||
resizableImageView!.contentMode = photo.contentMode
|
||||
if sender.layer.cornerRadius != 0 {
|
||||
let duration = (animationDuration * Double(animationDamping))
|
||||
resizableImageView!.layer.masksToBounds = true
|
||||
resizableImageView!.addCornerRadiusAnimation(sender.layer.cornerRadius, to: 0, duration: duration)
|
||||
}
|
||||
window.addSubview(resizableImageView!)
|
||||
|
||||
presentAnimation(browser)
|
||||
}
|
||||
|
||||
func willDismiss(browser: SKPhotoBrowser) {
|
||||
guard let sender = browser.delegate?.viewForPhoto?(browser, index: browser.currentPageIndex),
|
||||
image = browser.photoAtIndex(browser.currentPageIndex).underlyingImage,
|
||||
scrollView = browser.pageDisplayedAtIndex(browser.currentPageIndex) else {
|
||||
|
||||
senderViewForAnimation?.hidden = false
|
||||
browser.dismissPhotoBrowser(animated: false)
|
||||
return
|
||||
}
|
||||
|
||||
senderViewForAnimation = sender
|
||||
browser.view.hidden = true
|
||||
browser.backgroundView.hidden = false
|
||||
browser.backgroundView.alpha = 1
|
||||
|
||||
senderViewOriginalFrame = calcOriginFrame(sender)
|
||||
|
||||
let photo = browser.photoAtIndex(browser.currentPageIndex)
|
||||
let contentOffset = scrollView.contentOffset
|
||||
let scrollFrame = scrollView.photoImageView.frame
|
||||
let offsetY = scrollView.center.y - (scrollView.bounds.height/2)
|
||||
let frame = CGRect(
|
||||
x: scrollFrame.origin.x - contentOffset.x,
|
||||
y: scrollFrame.origin.y + contentOffset.y + offsetY,
|
||||
width: scrollFrame.width,
|
||||
height: scrollFrame.height)
|
||||
|
||||
// resizableImageView.image = scrollView.photo?.underlyingImage?.rotateImageByOrientation()
|
||||
resizableImageView!.image = image.rotateImageByOrientation()
|
||||
resizableImageView!.frame = frame
|
||||
resizableImageView!.alpha = 1.0
|
||||
resizableImageView!.clipsToBounds = true
|
||||
resizableImageView!.contentMode = photo.contentMode
|
||||
if let view = senderViewForAnimation where view.layer.cornerRadius != 0 {
|
||||
let duration = (animationDuration * Double(animationDamping))
|
||||
resizableImageView!.layer.masksToBounds = true
|
||||
resizableImageView!.addCornerRadiusAnimation(0, to: view.layer.cornerRadius, duration: duration)
|
||||
}
|
||||
|
||||
dismissAnimation(browser)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKAnimator {
|
||||
func calcOriginFrame(sender: UIView) -> CGRect {
|
||||
if let senderViewOriginalFrameTemp = sender.superview?.convertRect(sender.frame, toView:nil) {
|
||||
return senderViewOriginalFrameTemp
|
||||
} else if let senderViewOriginalFrameTemp = sender.layer.superlayer?.convertRect(sender.frame, toLayer: nil) {
|
||||
return senderViewOriginalFrameTemp
|
||||
} else {
|
||||
return .zero
|
||||
}
|
||||
}
|
||||
|
||||
func calcFinalFrame(imageRatio: CGFloat) -> CGRect {
|
||||
if SKMesurement.screenRatio < imageRatio {
|
||||
let width = SKMesurement.screenWidth
|
||||
let height = width / imageRatio
|
||||
let yOffset = (SKMesurement.screenHeight - height) / 2
|
||||
return CGRect(x: 0, y: yOffset, width: width, height: height)
|
||||
} else {
|
||||
let height = SKMesurement.screenHeight
|
||||
let width = height * imageRatio
|
||||
let xOffset = (SKMesurement.screenWidth - width) / 2
|
||||
return CGRect(x: xOffset, y: 0, width: width, height: height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKAnimator {
|
||||
func presentAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
|
||||
browser.view.hidden = true
|
||||
browser.view.alpha = 0.0
|
||||
|
||||
UIView.animateWithDuration(
|
||||
animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping:animationDamping,
|
||||
initialSpringVelocity:0,
|
||||
options:.CurveEaseInOut,
|
||||
animations: {
|
||||
browser.showButtons()
|
||||
browser.backgroundView.alpha = 1.0
|
||||
|
||||
self.resizableImageView?.frame = self.finalImageViewFrame
|
||||
},
|
||||
completion: { (Bool) -> Void in
|
||||
UIApplication.sharedApplication().setStatusBarHidden(!SKPhotoBrowserOptions.displayStatusbar, withAnimation: .Fade)
|
||||
|
||||
browser.view.hidden = false
|
||||
browser.view.alpha = 1.0
|
||||
browser.backgroundView.hidden = true
|
||||
|
||||
self.resizableImageView?.alpha = 0.0
|
||||
})
|
||||
}
|
||||
|
||||
func dismissAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
|
||||
UIView.animateWithDuration(
|
||||
animationDuration,
|
||||
delay:0,
|
||||
usingSpringWithDamping:animationDamping,
|
||||
initialSpringVelocity:0,
|
||||
options:.CurveEaseInOut,
|
||||
animations: {
|
||||
browser.backgroundView.alpha = 0.0
|
||||
|
||||
self.resizableImageView?.layer.frame = self.senderViewOriginalFrame
|
||||
},
|
||||
completion: { (Bool) -> () in
|
||||
browser.dismissPhotoBrowser(animated: true) {
|
||||
self.resizableImageView?.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// SKButtons.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/09.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// helpers which often used
|
||||
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
|
||||
|
||||
class SKButton: UIButton {
|
||||
var showFrame: CGRect!
|
||||
var hideFrame: CGRect!
|
||||
var insets: UIEdgeInsets {
|
||||
|
||||
|
||||
return UI_USER_INTERFACE_IDIOM() == .Phone
|
||||
? UIEdgeInsets(top: 15.25, left: 15.25, bottom: 15.25, right: 15.25) : UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
|
||||
}
|
||||
var size: CGSize = CGSize(width: 44, height: 44)
|
||||
var margin: CGFloat = 5
|
||||
|
||||
var buttonTopOffset: CGFloat { return 5 }
|
||||
|
||||
func setup(imageName: String) {
|
||||
backgroundColor = .clearColor()
|
||||
tintColor = SKPhotoBrowserOptions.textAndIconColor
|
||||
imageEdgeInsets = insets
|
||||
// clipsToBounds = true
|
||||
translatesAutoresizingMaskIntoConstraints = true
|
||||
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
|
||||
|
||||
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
|
||||
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
|
||||
setImage(image, forState: .Normal)
|
||||
}
|
||||
|
||||
func updateFrame() { }
|
||||
|
||||
func setFrameSize(size: CGSize) {
|
||||
let newRect = CGRect(x: margin, y: buttonTopOffset, width: size.width, height: size.height)
|
||||
self.frame = newRect
|
||||
showFrame = newRect
|
||||
hideFrame = CGRect(x: margin, y: -20, width: size.width, height: size.height)
|
||||
}
|
||||
}
|
||||
|
||||
class SKCloseButton: SKButton {
|
||||
let imageName = "btn_common_close_wh"
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup(imageName)
|
||||
showFrame = CGRect(x: margin, y: buttonTopOffset, width: size.width, height: size.height)
|
||||
hideFrame = CGRect(x: margin, y: -20, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
override func updateFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
class SKDeleteButton: SKButton {
|
||||
let imageName = "btn_common_delete_wh"
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup(imageName)
|
||||
showFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: buttonTopOffset, width: size.width, height: size.height)
|
||||
hideFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: -20, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
override func updateFrame() {
|
||||
}
|
||||
|
||||
override func setFrameSize(size: CGSize) {
|
||||
let newRect = CGRect(x: SKMesurement.screenWidth - size.width, y: buttonTopOffset, width: size.width, height: size.height)
|
||||
self.frame = newRect
|
||||
showFrame = newRect
|
||||
hideFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: -20, width: size.width, height: size.height)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@
|
|||
import UIKit
|
||||
|
||||
public class SKCache {
|
||||
|
||||
public static let sharedCache = SKCache()
|
||||
public var imageCache: SKCacheable
|
||||
|
||||
|
|
@ -18,30 +17,46 @@ public class SKCache {
|
|||
}
|
||||
|
||||
public func imageForKey(key: String) -> UIImage? {
|
||||
return (self.imageCache as? SKImageCacheable)!.imageForKey(key)
|
||||
guard let cache = imageCache as? SKImageCacheable else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cache.imageForKey(key)
|
||||
}
|
||||
|
||||
public func setImage(image: UIImage, forKey key: String) {
|
||||
(self.imageCache as? SKImageCacheable)!.setImage(image, forKey: key)
|
||||
guard let cache = imageCache as? SKImageCacheable else {
|
||||
return
|
||||
}
|
||||
|
||||
cache.setImage(image, forKey: key)
|
||||
}
|
||||
|
||||
public func removeImageForKey(key: String) {
|
||||
(self.imageCache as? SKImageCacheable)!.removeImageForKey(key)
|
||||
guard let cache = imageCache as? SKImageCacheable else {
|
||||
return
|
||||
}
|
||||
|
||||
cache.removeImageForKey(key)
|
||||
}
|
||||
|
||||
public func imageForRequest(request: NSURLRequest) -> UIImage? {
|
||||
if let response = (self.imageCache as? SKRequestResponseCacheable)!.cachedResponseForRequest(request) {
|
||||
let data = response.data
|
||||
|
||||
return UIImage(data: data)
|
||||
guard let cache = imageCache as? SKRequestResponseCacheable else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let response = cache.cachedResponseForRequest(request) {
|
||||
return UIImage(data: response.data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func setImageData(data: NSData, response: NSURLResponse, request: NSURLRequest) {
|
||||
guard let cache = imageCache as? SKRequestResponseCacheable else {
|
||||
return
|
||||
}
|
||||
let cachedResponse = NSCachedURLResponse(response: response, data: data)
|
||||
(self.imageCache as? SKRequestResponseCacheable)!.storeCachedResponse(cachedResponse, forRequest: request)
|
||||
cache.storeCachedResponse(cachedResponse, forRequest: request)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,18 +64,18 @@ class SKDefaultImageCache: SKImageCacheable {
|
|||
var cache: NSCache
|
||||
|
||||
init() {
|
||||
self.cache = NSCache()
|
||||
cache = NSCache()
|
||||
}
|
||||
|
||||
func imageForKey(key: String) -> UIImage? {
|
||||
return self.cache.objectForKey(key) as? UIImage
|
||||
return cache.objectForKey(key) as? UIImage
|
||||
}
|
||||
|
||||
func setImage(image: UIImage, forKey key: String) {
|
||||
self.cache.setObject(image, forKey: key)
|
||||
cache.setObject(image, forKey: key)
|
||||
}
|
||||
|
||||
func removeImageForKey(key: String) {
|
||||
self.cache.removeObjectForKey(key)
|
||||
cache.removeObjectForKey(key)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import UIKit.UIImage
|
||||
|
||||
public protocol SKCacheable {}
|
||||
public protocol SKImageCacheable: SKCacheable {
|
||||
func imageForKey(key: String) -> UIImage?
|
||||
func setImage(image: UIImage, forKey key: String)
|
||||
|
|
@ -18,6 +19,3 @@ public protocol SKRequestResponseCacheable: SKCacheable {
|
|||
func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse?
|
||||
func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest)
|
||||
}
|
||||
|
||||
public protocol SKCacheable {
|
||||
}
|
||||
|
|
@ -9,14 +9,9 @@
|
|||
import UIKit
|
||||
|
||||
public class SKCaptionView: UIView {
|
||||
final let screenBound = UIScreen.mainScreen().bounds
|
||||
private var screenWidth: CGFloat { return screenBound.size.width }
|
||||
private var screenHeight: CGFloat { return screenBound.size.height }
|
||||
private var photo: SKPhotoProtocol!
|
||||
private var photo: SKPhotoProtocol?
|
||||
private var photoLabel: UILabel!
|
||||
private var photoLabelPadding: CGFloat = 10
|
||||
private var fadeView: UIView = UIView()
|
||||
private var gradientLayer = CAGradientLayer()
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
|
|
@ -33,36 +28,6 @@ public class SKCaptionView: UIView {
|
|||
setup()
|
||||
}
|
||||
|
||||
func setup() {
|
||||
opaque = false
|
||||
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
|
||||
|
||||
// setup background first
|
||||
fadeView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
|
||||
addSubview(fadeView)
|
||||
|
||||
// add layer at fadeView
|
||||
gradientLayer.colors = [UIColor(white: 0.0, alpha: 0.0).CGColor, UIColor(white: 0.0, alpha: 0.8).CGColor]
|
||||
fadeView.layer.insertSublayer(gradientLayer, atIndex: 0)
|
||||
|
||||
photoLabel = UILabel(frame: CGRect(x: photoLabelPadding, y: 0,
|
||||
width: bounds.size.width - (photoLabelPadding * 2), height: bounds.size.height))
|
||||
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
|
||||
photoLabel.opaque = false
|
||||
photoLabel.backgroundColor = .clearColor()
|
||||
photoLabel.textColor = .whiteColor()
|
||||
photoLabel.textAlignment = .Center
|
||||
photoLabel.lineBreakMode = .ByTruncatingTail
|
||||
photoLabel.numberOfLines = 3
|
||||
photoLabel.shadowColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
photoLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
photoLabel.font = UIFont.systemFontOfSize(17.0)
|
||||
if let cap = photo.caption {
|
||||
photoLabel.text = cap
|
||||
}
|
||||
addSubview(photoLabel)
|
||||
}
|
||||
|
||||
public override func sizeThatFits(size: CGSize) -> CGSize {
|
||||
guard let text = photoLabel.text else {
|
||||
return CGSize.zero
|
||||
|
|
@ -72,19 +37,39 @@ public class SKCaptionView: UIView {
|
|||
}
|
||||
|
||||
let font: UIFont = photoLabel.font
|
||||
let width: CGFloat = size.width - (photoLabelPadding * 2)
|
||||
let width: CGFloat = size.width - photoLabelPadding * 2
|
||||
let height: CGFloat = photoLabel.font.lineHeight * CGFloat(photoLabel.numberOfLines)
|
||||
|
||||
let attributedText = NSAttributedString(string: text, attributes: [NSFontAttributeName: font])
|
||||
let textSize = attributedText.boundingRectWithSize(CGSize(width: width, height: height),
|
||||
options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil).size
|
||||
let textSize = attributedText.boundingRectWithSize(CGSize(width: width, height: height), options: .UsesLineFragmentOrigin, context: nil).size
|
||||
|
||||
return CGSize(width: textSize.width, height: textSize.height + photoLabelPadding * 2)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKCaptionView {
|
||||
func setup() {
|
||||
opaque = false
|
||||
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
|
||||
|
||||
// setup photoLabel
|
||||
setupPhotoLabel()
|
||||
}
|
||||
|
||||
public override func layoutSubviews() {
|
||||
fadeView.frame = frame
|
||||
gradientLayer.frame = frame
|
||||
func setupPhotoLabel() {
|
||||
photoLabel = UILabel(frame: CGRect(x: photoLabelPadding, y: 0, width: bounds.size.width - (photoLabelPadding * 2), height: bounds.size.height))
|
||||
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
|
||||
photoLabel.opaque = false
|
||||
photoLabel.backgroundColor = .clearColor()
|
||||
photoLabel.textColor = SKPhotoBrowserOptions.textAndIconColor
|
||||
photoLabel.textAlignment = .Center
|
||||
photoLabel.lineBreakMode = .ByTruncatingTail
|
||||
photoLabel.numberOfLines = 3
|
||||
photoLabel.shadowColor = UIColor(white: 0.0, alpha: 0.5)
|
||||
photoLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
photoLabel.font = SKPhotoBrowserOptions.captionFont
|
||||
photoLabel.text = photo?.caption
|
||||
addSubview(photoLabel)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,27 +18,33 @@ class SKDetectingImageView: UIImageView {
|
|||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
setup()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
userInteractionEnabled = true
|
||||
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
|
||||
doubleTap.numberOfTapsRequired = 2
|
||||
doubleTap.numberOfTouchesRequired = 1
|
||||
self.addGestureRecognizer(doubleTap)
|
||||
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
|
||||
singleTap.numberOfTapsRequired = 1
|
||||
singleTap.numberOfTouchesRequired = 1
|
||||
singleTap.requireGestureRecognizerToFail(doubleTap)
|
||||
self.addGestureRecognizer(singleTap)
|
||||
setup()
|
||||
}
|
||||
|
||||
func handleDoubleTap(recognizer:UITapGestureRecognizer) {
|
||||
func handleDoubleTap(recognizer: UITapGestureRecognizer) {
|
||||
delegate?.handleImageViewDoubleTap(recognizer.locationInView(self))
|
||||
}
|
||||
|
||||
func handleSingleTap(recognizer:UITapGestureRecognizer) {
|
||||
func handleSingleTap(recognizer: UITapGestureRecognizer) {
|
||||
delegate?.handleImageViewSingleTap(recognizer.locationInView(self))
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKDetectingImageView {
|
||||
func setup() {
|
||||
userInteractionEnabled = true
|
||||
|
||||
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
|
||||
doubleTap.numberOfTapsRequired = 2
|
||||
addGestureRecognizer(doubleTap)
|
||||
|
||||
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
|
||||
singleTap.requireGestureRecognizerToFail(doubleTap)
|
||||
addGestureRecognizer(singleTap)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,20 +13,23 @@ import UIKit
|
|||
func handleDoubleTap(view: UIView, touch: UITouch)
|
||||
}
|
||||
|
||||
|
||||
class SKDetectingView: UIView {
|
||||
weak var delegate: SKDetectingViewDelegate?
|
||||
|
||||
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
|
||||
super.touchesEnded(touches, withEvent: event)
|
||||
defer {
|
||||
nextResponder()
|
||||
}
|
||||
|
||||
let touch = touches.first!
|
||||
guard let touch = touches.first else {
|
||||
return
|
||||
}
|
||||
switch touch.tapCount {
|
||||
case 1 : handleSingleTap(touch)
|
||||
case 2 : handleDoubleTap(touch)
|
||||
default: break
|
||||
}
|
||||
nextResponder()
|
||||
}
|
||||
|
||||
func handleSingleTap(touch: UITouch) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
import UIKit
|
||||
|
||||
class SKIndicatorView: UIActivityIndicatorView {
|
||||
|
||||
required init(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
|
@ -17,7 +16,6 @@ class SKIndicatorView: UIActivityIndicatorView {
|
|||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
center = CGPoint(x: frame.width / 2, y: frame.height / 2)
|
||||
activityIndicatorViewStyle = .WhiteLarge
|
||||
activityIndicatorViewStyle = SKPhotoBrowserOptions.backgroundColor.isEqual(UIColor.whiteColor()) ? .Gray : .WhiteLarge
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ public class SKLocalPhoto: NSObject, SKPhotoProtocol {
|
|||
|
||||
public var underlyingImage: UIImage!
|
||||
public var photoURL: String!
|
||||
public var contentMode: UIViewContentMode = .ScaleToFill
|
||||
public var shouldCachePhotoURLImage: Bool = false
|
||||
public var caption: String!
|
||||
public var index: Int = 0
|
||||
|
|
@ -49,9 +50,7 @@ public class SKLocalPhoto: NSObject, SKPhotoProtocol {
|
|||
self.underlyingImage = image
|
||||
self.loadUnderlyingImageComplete()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// SKMesurement.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/09.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
struct SKMesurement {
|
||||
static let isPhone: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Phone
|
||||
static let isPad: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Pad
|
||||
static var statusBarH: CGFloat {
|
||||
return UIApplication.sharedApplication().statusBarFrame.height
|
||||
}
|
||||
static var screenHeight: CGFloat {
|
||||
return UIScreen.mainScreen().bounds.height
|
||||
}
|
||||
static var screenWidth: CGFloat {
|
||||
return UIScreen.mainScreen().bounds.width
|
||||
}
|
||||
static var screenScale: CGFloat {
|
||||
return UIScreen.mainScreen().scale
|
||||
}
|
||||
static var screenRatio: CGFloat {
|
||||
return screenWidth / screenHeight
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// SKNavigationBar.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by Григорий Уланов on 10.10.16.
|
||||
// Copyright © 2016 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SKNavigationBar: UINavigationBar {
|
||||
var showFrame: CGRect!
|
||||
var hideFrame: CGRect!
|
||||
|
||||
private static let toolBarHeight: CGFloat = 64.0
|
||||
|
||||
private weak var browser: SKPhotoBrowser?
|
||||
|
||||
convenience init(browser: SKPhotoBrowser) {
|
||||
self.init(frame: CGRect.zero)
|
||||
|
||||
self.browser = browser
|
||||
|
||||
translucent = false
|
||||
tintColor = UIColor.whiteColor()
|
||||
barTintColor = UIColor.blackColor()
|
||||
titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
|
||||
|
||||
let navigationItem = UINavigationItem()
|
||||
pushNavigationItem(navigationItem, animated: false)
|
||||
}
|
||||
|
||||
func updateNavigationBar(currentPageIndex: Int) {
|
||||
guard let browser = browser else { return }
|
||||
|
||||
if browser.numberOfPhotos > 1 {
|
||||
self.topItem?.title = "\(currentPageIndex + 1) \(SKPhotoBrowserOptions.navigationBarCounterSepatator) \(browser.numberOfPhotos)"
|
||||
} else {
|
||||
self.topItem?.title = nil
|
||||
}
|
||||
}
|
||||
|
||||
func setNewFrame(rect: CGRect) {
|
||||
self.frame = rect
|
||||
showFrame = rect
|
||||
|
||||
hideFrame = CGRect(x: rect.origin.x, y: rect.origin.y - 20, width: rect.size.width, height: rect.size.height)
|
||||
}
|
||||
|
||||
func updateFrame(parentSize: CGSize) {
|
||||
let newRect = CGRect(x: 0, y: 0, width: parentSize.width, height: SKNavigationBar.toolBarHeight)
|
||||
setNewFrame(newRect)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
//
|
||||
// SKPagingScrollView.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/18.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class SKPagingScrollView: UIScrollView {
|
||||
let pageIndexTagOffset: Int = 1000
|
||||
let sideMargin: CGFloat = 10
|
||||
private var visiblePages = [SKZoomingScrollView]()
|
||||
private var recycledPages = [SKZoomingScrollView]()
|
||||
|
||||
private weak var browser: SKPhotoBrowser?
|
||||
var numberOfPhotos: Int {
|
||||
return browser?.photos.count ?? 0
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
pagingEnabled = true
|
||||
showsHorizontalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
|
||||
showsVerticalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
|
||||
}
|
||||
|
||||
convenience init(frame: CGRect, browser: SKPhotoBrowser) {
|
||||
self.init(frame: frame)
|
||||
self.browser = browser
|
||||
|
||||
updateFrame(bounds, currentPageIndex: browser.currentPageIndex)
|
||||
}
|
||||
|
||||
func reload() {
|
||||
visiblePages.forEach({$0.removeFromSuperview()})
|
||||
visiblePages.removeAll()
|
||||
recycledPages.removeAll()
|
||||
}
|
||||
|
||||
func loadAdjacentPhotosIfNecessary(photo: SKPhotoProtocol, currentPageIndex: Int) {
|
||||
guard let browser = browser, page = pageDisplayingAtPhoto(photo) else {
|
||||
return
|
||||
}
|
||||
let pageIndex = (page.tag - pageIndexTagOffset)
|
||||
if currentPageIndex == pageIndex {
|
||||
// Previous
|
||||
if pageIndex > 0 {
|
||||
let previousPhoto = browser.photos[pageIndex - 1]
|
||||
if previousPhoto.underlyingImage == nil {
|
||||
previousPhoto.loadUnderlyingImageAndNotify()
|
||||
}
|
||||
}
|
||||
// Next
|
||||
if pageIndex < numberOfPhotos - 1 {
|
||||
let nextPhoto = browser.photos[pageIndex + 1]
|
||||
if nextPhoto.underlyingImage == nil {
|
||||
nextPhoto.loadUnderlyingImageAndNotify()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteImage() {
|
||||
// index equals 0 because when we slide between photos delete button is hidden and user cannot to touch on delete button. And visible pages number equals 0
|
||||
if numberOfPhotos > 0 {
|
||||
visiblePages[0].captionView?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func animate(frame: CGRect) {
|
||||
setContentOffset(CGPoint(x: frame.origin.x - sideMargin, y: 0), animated: true)
|
||||
}
|
||||
|
||||
func updateFrame(bounds: CGRect, currentPageIndex: Int) {
|
||||
var frame = bounds
|
||||
frame.origin.x -= sideMargin
|
||||
frame.size.width += (2 * sideMargin)
|
||||
|
||||
self.frame = frame
|
||||
|
||||
if visiblePages.count > 0 {
|
||||
for page in visiblePages {
|
||||
let pageIndex = page.tag - pageIndexTagOffset
|
||||
page.frame = frameForPageAtIndex(pageIndex)
|
||||
page.setMaxMinZoomScalesForCurrentBounds()
|
||||
if page.captionView != nil {
|
||||
page.captionView.frame = frameForCaptionView(page.captionView, index: pageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateContentSize()
|
||||
updateContentOffset(currentPageIndex)
|
||||
}
|
||||
|
||||
func updateContentSize() {
|
||||
contentSize = CGSize(width: bounds.size.width * CGFloat(numberOfPhotos), height: bounds.size.height)
|
||||
}
|
||||
|
||||
func updateContentOffset(index: Int) {
|
||||
let pageWidth = bounds.size.width
|
||||
let newOffset = CGFloat(index) * pageWidth
|
||||
contentOffset = CGPoint(x: newOffset, y: 0)
|
||||
}
|
||||
|
||||
func tilePages() {
|
||||
guard let browser = browser else { return }
|
||||
|
||||
let firstIndex: Int = getFirstIndex()
|
||||
let lastIndex: Int = getLastIndex()
|
||||
|
||||
visiblePages
|
||||
.filter({ $0.tag - pageIndexTagOffset < firstIndex || $0.tag - pageIndexTagOffset < lastIndex })
|
||||
.forEach { page in
|
||||
recycledPages.append(page)
|
||||
page.prepareForReuse()
|
||||
page.removeFromSuperview()
|
||||
}
|
||||
|
||||
let visibleSet: Set<SKZoomingScrollView> = Set(visiblePages)
|
||||
let visibleSetWithoutRecycled: Set<SKZoomingScrollView> = visibleSet.subtract(recycledPages)
|
||||
visiblePages = Array(visibleSetWithoutRecycled)
|
||||
|
||||
while recycledPages.count > 2 {
|
||||
recycledPages.removeFirst()
|
||||
}
|
||||
|
||||
for index: Int in firstIndex...lastIndex {
|
||||
if visiblePages.filter({ $0.tag - pageIndexTagOffset == index }).count > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
let page: SKZoomingScrollView = SKZoomingScrollView(frame: frame, browser: browser)
|
||||
page.frame = frameForPageAtIndex(index)
|
||||
page.tag = index + pageIndexTagOffset
|
||||
page.photo = browser.photos[index]
|
||||
|
||||
visiblePages.append(page)
|
||||
addSubview(page)
|
||||
|
||||
// if exists caption, insert
|
||||
if let captionView: SKCaptionView = createCaptionView(index) {
|
||||
captionView.frame = frameForCaptionView(captionView, index: index)
|
||||
captionView.alpha = browser.areControlsHidden() ? 0 : 1
|
||||
addSubview(captionView)
|
||||
// ref val for control
|
||||
page.captionView = captionView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func frameForCaptionView(captionView: SKCaptionView, index: Int) -> CGRect {
|
||||
let pageFrame = frameForPageAtIndex(index)
|
||||
let captionSize = captionView.sizeThatFits(CGSize(width: pageFrame.size.width, height: 0))
|
||||
let navHeight = browser?.navigationController?.navigationBar.frame.size.height ?? 44
|
||||
return CGRect(x: pageFrame.origin.x, y: pageFrame.size.height - captionSize.height - navHeight,
|
||||
width: pageFrame.size.width, height: captionSize.height)
|
||||
}
|
||||
|
||||
func pageDisplayedAtIndex(index: Int) -> SKZoomingScrollView? {
|
||||
for page in visiblePages {
|
||||
if page.tag - pageIndexTagOffset == index {
|
||||
return page
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func pageDisplayingAtPhoto(photo: SKPhotoProtocol) -> SKZoomingScrollView? {
|
||||
for page in visiblePages {
|
||||
if page.photo === photo {
|
||||
return page
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCaptionViews() -> Set<SKCaptionView> {
|
||||
var captionViews = Set<SKCaptionView>()
|
||||
visiblePages
|
||||
.filter({ $0.captionView != nil })
|
||||
.forEach {
|
||||
captionViews.insert($0.captionView)
|
||||
}
|
||||
return captionViews
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKPagingScrollView {
|
||||
func frameForPageAtIndex(index: Int) -> CGRect {
|
||||
var pageFrame = bounds
|
||||
pageFrame.size.width -= (2 * 10)
|
||||
pageFrame.origin.x = (bounds.size.width * CGFloat(index)) + sideMargin
|
||||
return pageFrame
|
||||
}
|
||||
|
||||
func createCaptionView(index: Int) -> SKCaptionView? {
|
||||
guard let photo = browser?.photoAtIndex(index) where photo.caption != nil else {
|
||||
return nil
|
||||
}
|
||||
return SKCaptionView(photo: photo)
|
||||
}
|
||||
|
||||
func getFirstIndex() -> Int {
|
||||
let firstIndex = Int(floor((bounds.minX + sideMargin * 2) / bounds.width))
|
||||
if firstIndex < 0 {
|
||||
return 0
|
||||
}
|
||||
if firstIndex > numberOfPhotos - 1 {
|
||||
return numberOfPhotos - 1
|
||||
}
|
||||
return firstIndex
|
||||
}
|
||||
|
||||
func getLastIndex() -> Int {
|
||||
let lastIndex = Int(floor((bounds.maxX - sideMargin * 2 - 1) / bounds.width))
|
||||
if lastIndex < 0 {
|
||||
return 0
|
||||
}
|
||||
if lastIndex > numberOfPhotos - 1 {
|
||||
return numberOfPhotos - 1
|
||||
}
|
||||
return lastIndex
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ import UIKit
|
|||
var underlyingImage: UIImage! { get }
|
||||
var caption: String! { get }
|
||||
var index: Int { get set}
|
||||
var contentMode: UIViewContentMode { get set }
|
||||
func loadUnderlyingImageAndNotify()
|
||||
func checkCache()
|
||||
}
|
||||
|
|
@ -21,6 +22,7 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
|
|||
|
||||
public var underlyingImage: UIImage!
|
||||
public var photoURL: String!
|
||||
public var contentMode: UIViewContentMode = .ScaleAspectFill
|
||||
public var shouldCachePhotoURLImage: Bool = false
|
||||
public var caption: String!
|
||||
public var index: Int = 0
|
||||
|
|
@ -46,24 +48,30 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
|
|||
}
|
||||
|
||||
public func checkCache() {
|
||||
if photoURL != nil && shouldCachePhotoURLImage {
|
||||
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
|
||||
let request = NSURLRequest(URL: NSURL(string: photoURL)!)
|
||||
if let img = SKCache.sharedCache.imageForRequest(request) {
|
||||
underlyingImage = img
|
||||
}
|
||||
} else {
|
||||
if let img = SKCache.sharedCache.imageForKey(photoURL) {
|
||||
underlyingImage = img
|
||||
}
|
||||
guard let photoURL = photoURL else {
|
||||
return
|
||||
}
|
||||
guard shouldCachePhotoURLImage else {
|
||||
return
|
||||
}
|
||||
|
||||
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
|
||||
let request = NSURLRequest(URL: NSURL(string: photoURL)!)
|
||||
if let img = SKCache.sharedCache.imageForRequest(request) {
|
||||
underlyingImage = img
|
||||
}
|
||||
} else {
|
||||
if let img = SKCache.sharedCache.imageForKey(photoURL) {
|
||||
underlyingImage = img
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func loadUnderlyingImageAndNotify() {
|
||||
|
||||
if underlyingImage != nil && photoURL == nil {
|
||||
if underlyingImage != nil {
|
||||
loadUnderlyingImageComplete()
|
||||
return
|
||||
}
|
||||
|
||||
if photoURL != nil {
|
||||
|
|
@ -80,7 +88,7 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
if let res = response, let image = UIImage(data: res) {
|
||||
if let res = response, image = UIImage(data: res) {
|
||||
if _self.shouldCachePhotoURLImage {
|
||||
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
|
||||
SKCache.sharedCache.setImageData(response!, response: data!, request: task.originalRequest!)
|
||||
|
|
@ -105,16 +113,20 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
|
|||
NSNotificationCenter.defaultCenter().postNotificationName(SKPHOTO_LOADING_DID_END_NOTIFICATION, object: self)
|
||||
}
|
||||
|
||||
// MARK: - class func
|
||||
public class func photoWithImage(image: UIImage) -> SKPhoto {
|
||||
}
|
||||
|
||||
// MARK: - Static Function
|
||||
|
||||
extension SKPhoto {
|
||||
public static func photoWithImage(image: UIImage) -> SKPhoto {
|
||||
return SKPhoto(image: image)
|
||||
}
|
||||
|
||||
public class func photoWithImageURL(url: String) -> SKPhoto {
|
||||
public static func photoWithImageURL(url: String) -> SKPhoto {
|
||||
return SKPhoto(url: url)
|
||||
}
|
||||
|
||||
public class func photoWithImageURL(url: String, holder: UIImage?) -> SKPhoto {
|
||||
public static func photoWithImageURL(url: String, holder: UIImage?) -> SKPhoto {
|
||||
return SKPhoto(url: url, holder: holder)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// SKPhotoBrowserDelegate.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/09.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public protocol SKPhotoBrowserDelegate {
|
||||
|
||||
/**
|
||||
Tells the delegate that the browser started displaying a new photo
|
||||
|
||||
- Parameter index: the index of the new photo
|
||||
*/
|
||||
optional func didShowPhotoAtIndex(index: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate the browser will start to dismiss
|
||||
|
||||
- Parameter index: the index of the current photo
|
||||
*/
|
||||
optional func willDismissAtPageIndex(index: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate that the browser will start showing the `UIActionSheet`
|
||||
|
||||
- Parameter photoIndex: the index of the current photo
|
||||
*/
|
||||
optional func willShowActionSheet(photoIndex: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate that the browser has been dismissed
|
||||
|
||||
- Parameter index: the index of the current photo
|
||||
*/
|
||||
optional func didDismissAtPageIndex(index: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate that the browser did dismiss the UIActionSheet
|
||||
|
||||
- Parameter buttonIndex: the index of the pressed button
|
||||
- Parameter photoIndex: the index of the current photo
|
||||
*/
|
||||
optional func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate that the browser did scroll to index
|
||||
|
||||
- Parameter index: the index of the photo where the user had scroll
|
||||
*/
|
||||
optional func didScrollToIndex(index: Int)
|
||||
|
||||
/**
|
||||
Tells the delegate the user removed a photo, when implementing this call, be sure to call reload to finish the deletion process
|
||||
|
||||
- Parameter browser: reference to the calling SKPhotoBrowser
|
||||
- Parameter index: the index of the removed photo
|
||||
- Parameter reload: function that needs to be called after finishing syncing up
|
||||
*/
|
||||
optional func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void))
|
||||
|
||||
/**
|
||||
Asks the delegate for the view for a certain photo. Needed to detemine the animation when presenting/closing the browser.
|
||||
|
||||
- Parameter browser: reference to the calling SKPhotoBrowser
|
||||
- Parameter index: the index of the removed photo
|
||||
|
||||
- Returns: the view to animate to
|
||||
*/
|
||||
optional func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView?
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// SKPhotoBrowserOptions.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/18.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public struct SKPhotoBrowserOptions {
|
||||
public static var displayStatusbar: Bool = false
|
||||
|
||||
public static var displayAction: Bool = true
|
||||
public static var shareExtraCaption: String? = nil
|
||||
public static var actionButtonTitles: [String]?
|
||||
|
||||
public static var displayToolbar: Bool = true
|
||||
public static var displayCounterLabel: Bool = true
|
||||
public static var displayBackAndForwardButton: Bool = true
|
||||
public static var disableVerticalSwipe: Bool = false
|
||||
|
||||
public static var displayCloseButton: Bool = true
|
||||
public static var displayNavigationBar: Bool = true
|
||||
public static var displayDeleteButton: Bool = false
|
||||
|
||||
public static var displayHorizontalScrollIndicator: Bool = true
|
||||
public static var displayVerticalScrollIndicator: Bool = true
|
||||
|
||||
public static var bounceAnimation: Bool = false
|
||||
public static var enableZoomBlackArea: Bool = true
|
||||
public static var enableSingleTapDismiss: Bool = false
|
||||
|
||||
public static var backgroundColor = UIColor.blackColor()
|
||||
public static var textAndIconColor = UIColor.whiteColor()
|
||||
public static var toolbarTextShadowColor = UIColor.darkTextColor()
|
||||
|
||||
public static var navigationBarCounterSepatator: String = "из"
|
||||
|
||||
public static var toolbarFont = UIFont(name: "Helvetica", size: 16.0)
|
||||
public static var captionFont = UIFont.systemFontOfSize(17.0)
|
||||
|
||||
// FIXED: Scrolling performance slowed #145
|
||||
// public static var imagePaddingX: CGFloat = 0
|
||||
// public static var imagePaddingY: CGFloat = 0
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// SKToolbar.swift
|
||||
// SKPhotoBrowser
|
||||
//
|
||||
// Created by 鈴木 啓司 on 2016/08/12.
|
||||
// Copyright © 2016年 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// helpers which often used
|
||||
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
|
||||
|
||||
class SKToolbar: UIToolbar {
|
||||
var toolCounterLabel: UILabel!
|
||||
var toolCounterButton: UIBarButtonItem!
|
||||
var toolPreviousButton: UIBarButtonItem!
|
||||
var toolNextButton: UIBarButtonItem!
|
||||
var toolActionButton: UIBarButtonItem!
|
||||
|
||||
private weak var browser: SKPhotoBrowser?
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
convenience init(frame: CGRect, browser: SKPhotoBrowser) {
|
||||
self.init(frame: frame)
|
||||
self.browser = browser
|
||||
|
||||
setupApperance()
|
||||
setupPreviousButton()
|
||||
setupNextButton()
|
||||
setupCounterLabel()
|
||||
setupActionButton()
|
||||
setupToolbar()
|
||||
}
|
||||
|
||||
func updateToolbar(currentPageIndex: Int) {
|
||||
guard let browser = browser else { return }
|
||||
|
||||
if browser.numberOfPhotos > 1 {
|
||||
toolCounterLabel.text = "\(currentPageIndex + 1) / \(browser.numberOfPhotos)"
|
||||
} else {
|
||||
toolCounterLabel.text = nil
|
||||
}
|
||||
|
||||
toolPreviousButton.enabled = (currentPageIndex > 0)
|
||||
toolNextButton.enabled = (currentPageIndex < browser.numberOfPhotos - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKToolbar {
|
||||
func setupApperance() {
|
||||
backgroundColor = .clearColor()
|
||||
clipsToBounds = true
|
||||
translucent = true
|
||||
setBackgroundImage(UIImage(), forToolbarPosition: .Any, barMetrics: .Default)
|
||||
|
||||
// toolbar
|
||||
if !SKPhotoBrowserOptions.displayToolbar {
|
||||
hidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func setupToolbar() {
|
||||
guard let browser = browser else { return }
|
||||
|
||||
let flexSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: self, action: nil)
|
||||
var items = [UIBarButtonItem]()
|
||||
items.append(flexSpace)
|
||||
if browser.numberOfPhotos > 1 && SKPhotoBrowserOptions.displayBackAndForwardButton {
|
||||
items.append(toolPreviousButton)
|
||||
}
|
||||
if SKPhotoBrowserOptions.displayCounterLabel {
|
||||
items.append(flexSpace)
|
||||
items.append(toolCounterButton)
|
||||
items.append(flexSpace)
|
||||
} else {
|
||||
items.append(flexSpace)
|
||||
}
|
||||
if browser.numberOfPhotos > 1 && SKPhotoBrowserOptions.displayBackAndForwardButton {
|
||||
items.append(toolNextButton)
|
||||
}
|
||||
items.append(flexSpace)
|
||||
if SKPhotoBrowserOptions.displayAction {
|
||||
items.append(toolActionButton)
|
||||
}
|
||||
setItems(items, animated: false)
|
||||
}
|
||||
|
||||
func setupPreviousButton() {
|
||||
let previousBtn = SKPreviousButton(frame: frame)
|
||||
previousBtn.addTarget(browser, action: #selector(SKPhotoBrowser.gotoPreviousPage), forControlEvents: .TouchUpInside)
|
||||
toolPreviousButton = UIBarButtonItem(customView: previousBtn)
|
||||
}
|
||||
|
||||
func setupNextButton() {
|
||||
let nextBtn = SKNextButton(frame: frame)
|
||||
nextBtn.addTarget(browser, action: #selector(SKPhotoBrowser.gotoNextPage), forControlEvents: .TouchUpInside)
|
||||
toolNextButton = UIBarButtonItem(customView: nextBtn)
|
||||
}
|
||||
|
||||
func setupCounterLabel() {
|
||||
toolCounterLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 95, height: 40))
|
||||
toolCounterLabel.textAlignment = .Center
|
||||
toolCounterLabel.backgroundColor = .clearColor()
|
||||
toolCounterLabel.font = SKPhotoBrowserOptions.toolbarFont
|
||||
toolCounterLabel.textColor = SKPhotoBrowserOptions.textAndIconColor
|
||||
toolCounterLabel.shadowColor = SKPhotoBrowserOptions.toolbarTextShadowColor
|
||||
toolCounterLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
|
||||
toolCounterButton = UIBarButtonItem(customView: toolCounterLabel)
|
||||
}
|
||||
|
||||
func setupActionButton() {
|
||||
toolActionButton = UIBarButtonItem(barButtonSystemItem: .Action, target: browser, action: #selector(SKPhotoBrowser.actionButtonPressed))
|
||||
toolActionButton.tintColor = SKPhotoBrowserOptions.textAndIconColor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SKToolbarButton: UIButton {
|
||||
let insets: UIEdgeInsets = UIEdgeInsets(top: 13.25, left: 17.25, bottom: 13.25, right: 17.25)
|
||||
|
||||
func setup(imageName: String) {
|
||||
backgroundColor = .clearColor()
|
||||
tintColor = SKPhotoBrowserOptions.textAndIconColor
|
||||
imageEdgeInsets = insets
|
||||
translatesAutoresizingMaskIntoConstraints = true
|
||||
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
|
||||
contentMode = .Center
|
||||
|
||||
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
|
||||
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
|
||||
setImage(image, forState: .Normal)
|
||||
}
|
||||
}
|
||||
|
||||
class SKPreviousButton: SKToolbarButton {
|
||||
let imageName = "btn_common_back_wh"
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
|
||||
setup(imageName)
|
||||
}
|
||||
}
|
||||
|
||||
class SKNextButton: SKToolbarButton {
|
||||
let imageName = "btn_common_forward_wh"
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
|
||||
setup(imageName)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectingViewDelegate, SKDetectingImageViewDelegate {
|
||||
|
||||
public class SKZoomingScrollView: UIScrollView {
|
||||
var captionView: SKCaptionView!
|
||||
var photo: SKPhotoProtocol! {
|
||||
didSet {
|
||||
|
|
@ -49,14 +48,14 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
// tap
|
||||
tapView = SKDetectingView(frame: bounds)
|
||||
tapView.delegate = self
|
||||
tapView.backgroundColor = UIColor.clearColor()
|
||||
tapView.backgroundColor = .clearColor()
|
||||
tapView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
|
||||
addSubview(tapView)
|
||||
|
||||
// image
|
||||
photoImageView = SKDetectingImageView(frame: frame)
|
||||
photoImageView.delegate = self
|
||||
photoImageView.contentMode = .ScaleAspectFill
|
||||
photoImageView.contentMode = .Bottom
|
||||
photoImageView.backgroundColor = .clearColor()
|
||||
addSubview(photoImageView)
|
||||
|
||||
|
|
@ -104,7 +103,6 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
}
|
||||
|
||||
public func setMaxMinZoomScalesForCurrentBounds() {
|
||||
|
||||
maximumZoomScale = 1
|
||||
minimumZoomScale = 1
|
||||
zoomScale = 1
|
||||
|
|
@ -119,28 +117,12 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
let xScale = boundsSize.width / imageSize.width
|
||||
let yScale = boundsSize.height / imageSize.height
|
||||
let minScale: CGFloat = min(xScale, yScale)
|
||||
var maxScale: CGFloat!
|
||||
var maxScale: CGFloat = 1.0
|
||||
|
||||
|
||||
let scale = UIScreen.mainScreen().scale
|
||||
let scale = max(UIScreen.mainScreen().scale, 2.0)
|
||||
let deviceScreenWidth = UIScreen.mainScreen().bounds.width * scale // width in pixels. scale needs to remove if to use the old algorithm
|
||||
let deviceScreenHeight = UIScreen.mainScreen().bounds.height * scale // height in pixels. scale needs to remove if to use the old algorithm
|
||||
|
||||
// it is the old algorithm
|
||||
/* if photoImageView.frame.width < deviceScreenWidth {
|
||||
// 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.
|
||||
if UIApplication.sharedApplication().statusBarOrientation.isPortrait {
|
||||
maxScale = deviceScreenHeight / photoImageView.frame.width
|
||||
} else {
|
||||
maxScale = deviceScreenWidth / photoImageView.frame.width
|
||||
}
|
||||
} else if photoImageView.frame.width > deviceScreenWidth {
|
||||
maxScale = 1.0
|
||||
} else {
|
||||
// here if photoImageView.frame.width == deviceScreenWidth
|
||||
maxScale = 2.5
|
||||
} */
|
||||
|
||||
if photoImageView.frame.width < deviceScreenWidth {
|
||||
// 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.
|
||||
if UIApplication.sharedApplication().statusBarOrientation.isPortrait {
|
||||
|
|
@ -154,7 +136,7 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
// here if photoImageView.frame.width == deviceScreenWidth
|
||||
maxScale = 2.5
|
||||
}
|
||||
|
||||
|
||||
maximumZoomScale = maxScale
|
||||
minimumZoomScale = minScale
|
||||
zoomScale = minScale
|
||||
|
|
@ -200,9 +182,25 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
}
|
||||
|
||||
if let image = photo.underlyingImage {
|
||||
|
||||
// FIXED: Scrolling performance slowed #145
|
||||
|
||||
// create padding
|
||||
// let width: CGFloat = image.size.width + SKPhotoBrowserOptions.imagePaddingX
|
||||
// let height: CGFloat = image.size.height + SKPhotoBrowserOptions.imagePaddingY;
|
||||
// UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), false, 0.0);
|
||||
// let context: CGContextRef = UIGraphicsGetCurrentContext()!;
|
||||
// UIGraphicsPushContext(context);
|
||||
// let origin: CGPoint = CGPointMake((width - image.size.width) / 2, (height - image.size.height) / 2);
|
||||
// image.drawAtPoint(origin)
|
||||
// UIGraphicsPopContext();
|
||||
// let imageWithPadding = UIGraphicsGetImageFromCurrentImageContext();
|
||||
// UIGraphicsEndImageContext();
|
||||
|
||||
// image
|
||||
photoImageView.image = image
|
||||
photoImageView.contentMode = photo.contentMode
|
||||
photoImageView.backgroundColor = SKPhotoBrowserOptions.backgroundColor
|
||||
|
||||
var photoImageViewFrame = CGRect.zero
|
||||
photoImageViewFrame.origin = CGPoint.zero
|
||||
|
|
@ -238,23 +236,18 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
newZoom = maximumZoomScale
|
||||
}
|
||||
*/
|
||||
zoomToRect(zoomRectForScrollViewWith(maximumZoomScale, touchPoint: touchPoint), animated: true)
|
||||
let zoomRect = zoomRectForScrollViewWith(maximumZoomScale, touchPoint: touchPoint)
|
||||
zoomToRect(zoomRect, animated: true)
|
||||
}
|
||||
|
||||
// delay control
|
||||
photoBrowser?.hideControlsAfterDelay()
|
||||
}
|
||||
|
||||
public func zoomRectForScrollViewWith(scale: CGFloat, touchPoint: CGPoint) -> CGRect {
|
||||
let w = frame.size.width / scale
|
||||
let h = frame.size.height / scale
|
||||
let x = touchPoint.x - (w / 2.0)
|
||||
let y = touchPoint.y - (h / 2.0)
|
||||
|
||||
return CGRect(x: x, y: y, width: w, height: h)
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension SKZoomingScrollView: UIScrollViewDelegate {
|
||||
public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
|
||||
return photoImageView
|
||||
}
|
||||
|
|
@ -267,26 +260,56 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
setNeedsLayout()
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SKDetectingViewDelegate
|
||||
}
|
||||
|
||||
// MARK: - SKDetectingImageViewDelegate
|
||||
|
||||
extension SKZoomingScrollView: SKDetectingViewDelegate {
|
||||
func handleSingleTap(view: UIView, touch: UITouch) {
|
||||
if photoBrowser?.enableZoomBlackArea == true {
|
||||
if photoBrowser?.areControlsHidden() == false && photoBrowser?.enableSingleTapDismiss == true {
|
||||
photoBrowser?.determineAndClose()
|
||||
}
|
||||
photoBrowser?.toggleControls()
|
||||
guard let browser = photoBrowser else {
|
||||
return
|
||||
}
|
||||
guard SKPhotoBrowserOptions.enableZoomBlackArea == true else {
|
||||
return
|
||||
}
|
||||
|
||||
if browser.areControlsHidden() == false && SKPhotoBrowserOptions.enableSingleTapDismiss == true {
|
||||
browser.determineAndClose()
|
||||
} else {
|
||||
browser.toggleControls()
|
||||
}
|
||||
}
|
||||
|
||||
func handleDoubleTap(view: UIView, touch: UITouch) {
|
||||
if photoBrowser?.enableZoomBlackArea == true {
|
||||
if SKPhotoBrowserOptions.enableZoomBlackArea == true {
|
||||
let needPoint = getViewFramePercent(view, touch: touch)
|
||||
handleDoubleTap(needPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SKDetectingImageViewDelegate
|
||||
|
||||
extension SKZoomingScrollView: SKDetectingImageViewDelegate {
|
||||
func handleImageViewSingleTap(touchPoint: CGPoint) {
|
||||
guard let browser = photoBrowser else {
|
||||
return
|
||||
}
|
||||
if SKPhotoBrowserOptions.enableSingleTapDismiss {
|
||||
browser.determineAndClose()
|
||||
} else {
|
||||
browser.toggleControls()
|
||||
}
|
||||
}
|
||||
|
||||
private func getViewFramePercent(view: UIView, touch: UITouch) -> CGPoint {
|
||||
func handleImageViewDoubleTap(touchPoint: CGPoint) {
|
||||
handleDoubleTap(touchPoint)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SKZoomingScrollView {
|
||||
func getViewFramePercent(view: UIView, touch: UITouch) -> CGPoint {
|
||||
let oneWidthViewPercent = view.bounds.width / 100
|
||||
let viewTouchPoint = touch.locationInView(view)
|
||||
let viewWidthTouch = viewTouchPoint.x
|
||||
|
|
@ -307,16 +330,12 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
|
|||
return allPoint
|
||||
}
|
||||
|
||||
// MARK: - SKDetectingImageViewDelegate
|
||||
func handleImageViewSingleTap(touchPoint: CGPoint) {
|
||||
if photoBrowser!.enableSingleTapDismiss {
|
||||
photoBrowser?.determineAndClose()
|
||||
} else {
|
||||
photoBrowser?.toggleControls()
|
||||
}
|
||||
func zoomRectForScrollViewWith(scale: CGFloat, touchPoint: CGPoint) -> CGRect {
|
||||
let w = frame.size.width / scale
|
||||
let h = frame.size.height / scale
|
||||
let x = touchPoint.x - (h / max(UIScreen.mainScreen().scale, 2.0))
|
||||
let y = touchPoint.y - (w / max(UIScreen.mainScreen().scale, 2.0))
|
||||
|
||||
return CGRect(x: x, y: y, width: w, height: h)
|
||||
}
|
||||
|
||||
func handleImageViewDoubleTap(touchPoint: CGPoint) {
|
||||
handleDoubleTap(touchPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,63 +14,69 @@ extension UIImage {
|
|||
guard self.imageOrientation != .Up else {
|
||||
return self
|
||||
}
|
||||
|
||||
// We need to calculate the proper transformation to make the image upright.
|
||||
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
|
||||
var transform = CGAffineTransformIdentity
|
||||
|
||||
switch self.imageOrientation {
|
||||
case .Down, .DownMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
|
||||
|
||||
case .Left, .LeftMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
|
||||
|
||||
case .Right, .RightMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, 0, self.size.height)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
switch self.imageOrientation {
|
||||
case .UpMirrored, .DownMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
|
||||
transform = CGAffineTransformScale(transform, -1, 1)
|
||||
|
||||
case .LeftMirrored, .RightMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.height, 0)
|
||||
transform = CGAffineTransformScale(transform, -1, 1)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
let transform = calculateAffineTransform()
|
||||
|
||||
// Now we draw the underlying CGImage into a new context, applying the transform
|
||||
// calculated above.
|
||||
let ctx = CGBitmapContextCreate(nil, Int(self.size.width), Int(self.size.height),
|
||||
CGImageGetBitsPerComponent(self.CGImage), 0,
|
||||
CGImageGetColorSpace(self.CGImage),
|
||||
CGImageGetBitmapInfo(self.CGImage).rawValue)
|
||||
CGContextConcatCTM(ctx, transform)
|
||||
CGImageGetBitsPerComponent(self.CGImage!), 0,
|
||||
CGImageGetColorSpace(self.CGImage!)!,
|
||||
CGImageGetBitmapInfo(self.CGImage!).rawValue)
|
||||
CGContextConcatCTM(ctx!, transform)
|
||||
|
||||
switch self.imageOrientation {
|
||||
case .Left, .LeftMirrored, .Right, .RightMirrored:
|
||||
CGContextDrawImage(ctx, CGRect(x: 0, y: 0, width: size.height, height: size.width), self.CGImage)
|
||||
CGContextDrawImage(ctx!, CGRect(x: 0, y: 0, width: size.height, height: size.width), self.CGImage!)
|
||||
|
||||
default:
|
||||
CGContextDrawImage(ctx, CGRect(x: 0, y: 0, width: size.width, height: size.height), self.CGImage)
|
||||
CGContextDrawImage(ctx!, CGRect(x: 0, y: 0, width: size.width, height: size.height), self.CGImage!)
|
||||
}
|
||||
|
||||
// And now we just create a new UIImage from the drawing context
|
||||
if let cgImage = CGBitmapContextCreateImage(ctx) {
|
||||
if let cgImage = CGBitmapContextCreateImage(ctx!) {
|
||||
return UIImage(CGImage: cgImage)
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateAffineTransform() -> CGAffineTransform {
|
||||
// We need to calculate the proper transformation to make the image upright.
|
||||
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
|
||||
var transform = CGAffineTransformIdentity
|
||||
|
||||
switch self.imageOrientation {
|
||||
case .Down, .DownMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
|
||||
|
||||
case .Left, .LeftMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
|
||||
|
||||
case .Right, .RightMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, 0, self.size.height)
|
||||
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
switch self.imageOrientation {
|
||||
case .UpMirrored, .DownMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
|
||||
transform = CGAffineTransformScale(transform, -1, 1)
|
||||
|
||||
case .LeftMirrored, .RightMirrored:
|
||||
transform = CGAffineTransformTranslate(transform, self.size.height, 0)
|
||||
transform = CGAffineTransformScale(transform, -1, 1)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return transform
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
platform :ios, '8.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'SDWebImage', '~>3.8'
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
PODS:
|
||||
- SDWebImage (3.8.1):
|
||||
- SDWebImage/Core (= 3.8.1)
|
||||
- SDWebImage/Core (3.8.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
- SDWebImage (~> 3.8)
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
SDWebImage: 35f9627a3e44b4f292a8a8ce6a531fa488239b91
|
||||
|
||||
COCOAPODS: 0.39.0
|
||||
|
|
@ -7,9 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
212705891C92C69C00466223 /* CameraRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212705881C92C69C00466223 /* CameraRollViewController.swift */; };
|
||||
212705891C92C69C00466223 /* FromCameraRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212705881C92C69C00466223 /* FromCameraRollViewController.swift */; };
|
||||
8909B55B1BC792150060A053 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55A1BC792150060A053 /* AppDelegate.swift */; };
|
||||
8909B55D1BC792150060A053 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55C1BC792150060A053 /* ViewController.swift */; };
|
||||
8909B55D1BC792150060A053 /* FromLocalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55C1BC792150060A053 /* FromLocalViewController.swift */; };
|
||||
8909B5601BC792150060A053 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8909B55E1BC792150060A053 /* Main.storyboard */; };
|
||||
8909B5621BC792150060A053 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5611BC792150060A053 /* Assets.xcassets */; };
|
||||
8909B5651BC792150060A053 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5631BC792150060A053 /* LaunchScreen.storyboard */; };
|
||||
|
|
@ -25,8 +25,10 @@
|
|||
8909B58A1BC792DC0060A053 /* image7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57E1BC792DC0060A053 /* image7.jpg */; };
|
||||
8909B58B1BC792DC0060A053 /* image8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57F1BC792DC0060A053 /* image8.jpg */; };
|
||||
8909B58C1BC792DC0060A053 /* image9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5801BC792DC0060A053 /* image9.jpg */; };
|
||||
89FC5DBB1D54A04900F1BE52 /* FromWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */; };
|
||||
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5711BC792570060A053 /* SKPhotoBrowser.framework */; };
|
||||
A6A7B7811C9578E30025AC07 /* SKPhotoBrowser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5711BC792570060A053 /* SKPhotoBrowser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C2188E912BB106233AA4AB2 /* Pods.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -37,6 +39,13 @@
|
|||
remoteGlobalIDString = 8909B5301BC791280060A053;
|
||||
remoteInfo = SKPhotoBrowser;
|
||||
};
|
||||
89FC5DBD1D54A04A00F1BE52 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = A64B89331CB04222000071B9;
|
||||
remoteInfo = SKPhotoBrowserTests;
|
||||
};
|
||||
A6A7B7821C9578E30025AC07 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */;
|
||||
|
|
@ -61,10 +70,11 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
212705881C92C69C00466223 /* CameraRollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraRollViewController.swift; sourceTree = "<group>"; };
|
||||
212705881C92C69C00466223 /* FromCameraRollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromCameraRollViewController.swift; sourceTree = "<group>"; };
|
||||
7C2188E912BB106233AA4AB2 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8909B5571BC792150060A053 /* SKPhotoBrowserExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SKPhotoBrowserExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8909B55A1BC792150060A053 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
8909B55C1BC792150060A053 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
8909B55C1BC792150060A053 /* FromLocalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FromLocalViewController.swift; sourceTree = "<group>"; };
|
||||
8909B55F1BC792150060A053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
8909B5611BC792150060A053 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
8909B5641BC792150060A053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
|
|
@ -82,6 +92,9 @@
|
|||
8909B57E1BC792DC0060A053 /* image7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image7.jpg; sourceTree = "<group>"; };
|
||||
8909B57F1BC792DC0060A053 /* image8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image8.jpg; sourceTree = "<group>"; };
|
||||
8909B5801BC792DC0060A053 /* image9.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image9.jpg; sourceTree = "<group>"; };
|
||||
89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromWebViewController.swift; sourceTree = "<group>"; };
|
||||
B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -90,6 +103,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */,
|
||||
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -102,6 +116,8 @@
|
|||
8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */,
|
||||
8909B5591BC792150060A053 /* SKPhotoBrowserExample */,
|
||||
8909B5581BC792150060A053 /* Products */,
|
||||
D1D8ECD45B290372235B8168 /* Pods */,
|
||||
C646D2C27A5B9CCC003A9BD1 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -129,6 +145,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
8909B5711BC792570060A053 /* SKPhotoBrowser.framework */,
|
||||
89FC5DBE1D54A04A00F1BE52 /* SKPhotoBrowserTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -155,8 +172,9 @@
|
|||
A6EBAD761CAFB87A00F80595 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8909B55C1BC792150060A053 /* ViewController.swift */,
|
||||
212705881C92C69C00466223 /* CameraRollViewController.swift */,
|
||||
8909B55C1BC792150060A053 /* FromLocalViewController.swift */,
|
||||
212705881C92C69C00466223 /* FromCameraRollViewController.swift */,
|
||||
89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */,
|
||||
);
|
||||
name = Controllers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -187,6 +205,23 @@
|
|||
name = Info;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C646D2C27A5B9CCC003A9BD1 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7C2188E912BB106233AA4AB2 /* Pods.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D1D8ECD45B290372235B8168 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */,
|
||||
DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
|
@ -194,10 +229,13 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 8909B5691BC792150060A053 /* Build configuration list for PBXNativeTarget "SKPhotoBrowserExample" */;
|
||||
buildPhases = (
|
||||
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */,
|
||||
8909B5531BC792150060A053 /* Sources */,
|
||||
8909B5541BC792150060A053 /* Frameworks */,
|
||||
8909B5551BC792150060A053 /* Resources */,
|
||||
A6A7B7841C9578E30025AC07 /* Embed Frameworks */,
|
||||
575DBD5855AB711E5773767A /* Embed Pods Frameworks */,
|
||||
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -220,6 +258,7 @@
|
|||
TargetAttributes = {
|
||||
8909B5561BC792150060A053 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -255,6 +294,13 @@
|
|||
remoteRef = 8909B5701BC792570060A053 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
89FC5DBE1D54A04A00F1BE52 /* SKPhotoBrowserTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = SKPhotoBrowserTests.xctest;
|
||||
remoteRef = 89FC5DBD1D54A04A00F1BE52 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
|
|
@ -282,14 +328,63 @@
|
|||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
575DBD5855AB711E5773767A /* Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
8909B5531BC792150060A053 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
212705891C92C69C00466223 /* CameraRollViewController.swift in Sources */,
|
||||
8909B55D1BC792150060A053 /* ViewController.swift in Sources */,
|
||||
212705891C92C69C00466223 /* FromCameraRollViewController.swift in Sources */,
|
||||
8909B55D1BC792150060A053 /* FromLocalViewController.swift in Sources */,
|
||||
8909B55B1BC792150060A053 /* AppDelegate.swift in Sources */,
|
||||
89FC5DBB1D54A04900F1BE52 /* FromWebViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -408,6 +503,7 @@
|
|||
};
|
||||
8909B56A1BC792150060A053 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
|
|
@ -416,11 +512,13 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
8909B56B1BC792150060A053 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
|
|
@ -429,6 +527,7 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 2.3;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
|||
10
SKPhotoBrowserExample/SKPhotoBrowserExample.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
SKPhotoBrowserExample/SKPhotoBrowserExample.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:SKPhotoBrowserExample.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="qgG-zu-Htx">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="qgG-zu-Htx">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Local Images-->
|
||||
<!--FromLocal-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="BYZ-38-t0r" customClass="FromLocalViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="ZcN-ut-rvO" secondAttribute="bottom" id="jxM-4I-Axo"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Local Images" id="KFf-qk-pEk"/>
|
||||
<tabBarItem key="tabBarItem" title="FromLocal" id="KFf-qk-pEk"/>
|
||||
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="collectionView" destination="ZcN-ut-rvO" id="bJb-LN-o9w"/>
|
||||
|
|
@ -66,12 +66,57 @@
|
|||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1680" y="-305"/>
|
||||
<point key="canvasLocation" x="1678" y="-667"/>
|
||||
</scene>
|
||||
<!--Camera Roll-->
|
||||
<!--FromWeb-->
|
||||
<scene sceneID="mNO-ib-nK2">
|
||||
<objects>
|
||||
<viewController id="wTD-ba-cWw" customClass="FromWebViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="GH3-21-jcQ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="y5T-GC-T6k"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="pNX-nL-SLw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZK0-HE-9Bs">
|
||||
<rect key="frame" x="220" y="253" width="161" height="94"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="40"/>
|
||||
<state key="normal" title="Present"/>
|
||||
<connections>
|
||||
<action selector="pushButton:" destination="wTD-ba-cWw" eventType="touchUpInside" id="xIc-2O-Gmb"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yxe-Wd-8iE">
|
||||
<rect key="frame" x="180" y="78" width="240" height="128"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="128" id="a4K-nx-MJW"/>
|
||||
<constraint firstAttribute="width" constant="240" id="tzG-IV-N9z"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="top" secondItem="yxe-Wd-8iE" secondAttribute="bottom" constant="50" id="8Aq-op-KMh"/>
|
||||
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="centerY" secondItem="pNX-nL-SLw" secondAttribute="centerY" id="9jd-Un-cAK"/>
|
||||
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="centerX" secondItem="pNX-nL-SLw" secondAttribute="centerX" id="h6U-H1-RV4"/>
|
||||
<constraint firstItem="yxe-Wd-8iE" firstAttribute="centerX" secondItem="pNX-nL-SLw" secondAttribute="centerX" id="xA1-uS-dJw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="FromWeb" id="2lE-np-9H3"/>
|
||||
<connections>
|
||||
<outlet property="imageView" destination="yxe-Wd-8iE" id="aMH-Df-3r0"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="jdP-ty-Ygs" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1678" y="34"/>
|
||||
</scene>
|
||||
<!--CameraRoll-->
|
||||
<scene sceneID="V6Y-ws-thI">
|
||||
<objects>
|
||||
<viewController id="aTQ-Do-sRD" customClass="CameraRollViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="aTQ-Do-sRD" customClass="FromCameraRollViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="9hr-eY-Ckr"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="l1U-c1-FNM"/>
|
||||
|
|
@ -127,14 +172,14 @@
|
|||
<constraint firstItem="f1m-3Z-8mE" firstAttribute="top" secondItem="9hr-eY-Ckr" secondAttribute="bottom" id="qdD-8q-n5J"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Camera Roll" id="fDV-zv-lCk"/>
|
||||
<tabBarItem key="tabBarItem" title="CameraRoll" id="fDV-zv-lCk"/>
|
||||
<connections>
|
||||
<outlet property="collectionView" destination="f1m-3Z-8mE" id="W6f-GL-fNz"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="X3n-FY-lyb" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1680" y="1049"/>
|
||||
<point key="canvasLocation" x="1678" y="694"/>
|
||||
</scene>
|
||||
<!--Tab Bar Controller-->
|
||||
<scene sceneID="MxJ-Af-928">
|
||||
|
|
@ -148,6 +193,7 @@
|
|||
</tabBar>
|
||||
<connections>
|
||||
<segue destination="BYZ-38-t0r" kind="relationship" relationship="viewControllers" id="TmI-eA-2RC"/>
|
||||
<segue destination="wTD-ba-cWw" kind="relationship" relationship="viewControllers" id="prf-dw-yqt"/>
|
||||
<segue destination="aTQ-Do-sRD" kind="relationship" relationship="viewControllers" id="j9q-XO-aL9"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
import Photos
|
||||
import SKPhotoBrowser
|
||||
|
||||
class CameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
|
||||
class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
|
||||
|
||||
@IBOutlet weak var collectionView: UICollectionView!
|
||||
|
||||
|
|
@ -107,10 +107,9 @@ class CameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UIColl
|
|||
|
||||
browser.initializePageIndex(indexPath.row)
|
||||
browser.delegate = self
|
||||
browser.bounceAnimation = true
|
||||
browser.displayDeleteButton = true
|
||||
browser.displayAction = false
|
||||
browser.statusBarStyle = .LightContent
|
||||
// browser.bounceAnimation = true
|
||||
// browser.displayDeleteButton = true
|
||||
// browser.displayAction = false
|
||||
self.presentViewController(browser, animated: true, completion: {})
|
||||
}
|
||||
|
||||
|
|
@ -168,8 +167,7 @@ class CameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UIColl
|
|||
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
|
||||
completion(image: image, requestId: requestId?.intValue)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return imageManager.requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options) { image, dict in
|
||||
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
|
||||
completion(image: image, requestId: requestId?.intValue)
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
//
|
||||
// ViewController.swift
|
||||
// SKPhotoBrowserExample
|
||||
//
|
||||
// Created by suzuki_keishi on 2015/10/06.
|
||||
// Copyright © 2015 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SKPhotoBrowser
|
||||
|
||||
class FromLocalViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, SKPhotoBrowserDelegate {
|
||||
@IBOutlet weak var collectionView: UICollectionView!
|
||||
|
||||
var images = [SKPhotoProtocol]()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setupTestData()
|
||||
setupCollectionView()
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
}
|
||||
|
||||
override func prefersStatusBarHidden() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func preferredStatusBarStyle() -> UIStatusBarStyle {
|
||||
return .LightContent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
extension FromLocalViewController {
|
||||
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return images.count
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath) as? ExampleCollectionViewCell else {
|
||||
return UICollectionViewCell()
|
||||
}
|
||||
|
||||
cell.exampleImageView.image = UIImage(named: "image\(indexPath.row % 10).jpg")
|
||||
// cell.exampleImageView.contentMode = .ScaleAspectFill
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
|
||||
extension FromLocalViewController {
|
||||
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
|
||||
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
|
||||
return
|
||||
}
|
||||
guard let originImage = cell.exampleImageView.image else {
|
||||
return
|
||||
}
|
||||
|
||||
// SKPhotoBrowserOptions.displayToolbar = false
|
||||
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
browser.initializePageIndex(indexPath.row)
|
||||
browser.delegate = self
|
||||
// browser.updateCloseButton(UIImage(named: "image1.jpg")!)
|
||||
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
|
||||
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
|
||||
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 300)
|
||||
} else {
|
||||
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SKPhotoBrowserDelegate
|
||||
|
||||
extension FromLocalViewController {
|
||||
func didShowPhotoAtIndex(index: Int) {
|
||||
collectionView.visibleCells().forEach({$0.hidden = false})
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
|
||||
}
|
||||
|
||||
func willDismissAtPageIndex(index: Int) {
|
||||
collectionView.visibleCells().forEach({$0.hidden = false})
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
|
||||
}
|
||||
|
||||
func willShowActionSheet(photoIndex: Int) {
|
||||
// do some handle if you need
|
||||
}
|
||||
|
||||
func didDismissAtPageIndex(index: Int) {
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = false
|
||||
}
|
||||
|
||||
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
|
||||
// handle dismissing custom actions
|
||||
}
|
||||
|
||||
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
|
||||
reload()
|
||||
}
|
||||
|
||||
func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView? {
|
||||
return collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - private
|
||||
|
||||
private extension FromLocalViewController {
|
||||
private func setupTestData() {
|
||||
images = createLocalPhotos()
|
||||
}
|
||||
|
||||
private func setupCollectionView() {
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
func createLocalPhotos() -> [SKPhotoProtocol] {
|
||||
return (0..<10).map { (i: Int) -> SKPhotoProtocol in
|
||||
let photo = SKPhoto.photoWithImage(UIImage(named: "image\(i%10).jpg")!)
|
||||
photo.caption = caption[i%10]
|
||||
// photo.contentMode = .ScaleAspectFill
|
||||
return photo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleCollectionViewCell: UICollectionViewCell {
|
||||
@IBOutlet weak var exampleImageView: UIImageView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
exampleImageView.image = nil
|
||||
layer.cornerRadius = 25.0
|
||||
layer.masksToBounds = true
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
exampleImageView.image = nil
|
||||
}
|
||||
}
|
||||
|
||||
var caption = ["Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
]
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// FromWebViewController.swift
|
||||
// SKPhotoBrowserExample
|
||||
//
|
||||
// Created by suzuki_keishi on 2015/10/06.
|
||||
// Copyright © 2015 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SKPhotoBrowser
|
||||
import SDWebImage
|
||||
|
||||
class FromWebViewController: UIViewController, SKPhotoBrowserDelegate {
|
||||
@IBOutlet weak var imageView: UIImageView!
|
||||
var images = [SKPhotoProtocol]()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
SKCache.sharedCache.imageCache = CustomImageCache()
|
||||
imageView.sd_setImageWithURL(NSURL(string: "https://placehold.jp/1500x1500.png")) {
|
||||
guard let url = $0.3.absoluteString else { return }
|
||||
SKCache.sharedCache.setImage($0.0, forKey: url)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func pushButton(sender: AnyObject) {
|
||||
let browser = SKPhotoBrowser(photos: createWebPhotos())
|
||||
browser.initializePageIndex(0)
|
||||
browser.delegate = self
|
||||
|
||||
presentViewController(browser, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SKPhotoBrowserDelegate
|
||||
|
||||
extension FromWebViewController {
|
||||
func didDismissAtPageIndex(index: Int) {
|
||||
}
|
||||
|
||||
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
|
||||
}
|
||||
|
||||
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
|
||||
SKCache.sharedCache.removeImageForKey("somekey")
|
||||
reload()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - private
|
||||
|
||||
private extension FromWebViewController {
|
||||
func createWebPhotos() -> [SKPhotoProtocol] {
|
||||
return (0..<10).map { (i: Int) -> SKPhotoProtocol in
|
||||
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150\(i)x150\(i).png")
|
||||
photo.caption = caption[i%10]
|
||||
photo.shouldCachePhotoURLImage = true
|
||||
return photo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CustomImageCache: SKImageCacheable {
|
||||
var cache: SDImageCache
|
||||
|
||||
init() {
|
||||
let cache = SDImageCache(namespace: "com.suzuki.custom.cache")
|
||||
self.cache = cache
|
||||
}
|
||||
|
||||
func imageForKey(key: String) -> UIImage? {
|
||||
guard let image = cache.imageFromDiskCacheForKey(key) else { return nil }
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
func setImage(image: UIImage, forKey key: String) {
|
||||
cache.storeImage(image, forKey: key)
|
||||
}
|
||||
|
||||
func removeImageForKey(key: String) {
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>for example, this app accesses the user’s photo library</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
|
@ -46,10 +48,10 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,167 +0,0 @@
|
|||
//
|
||||
// ViewController.swift
|
||||
// SKPhotoBrowserExample
|
||||
//
|
||||
// Created by suzuki_keishi on 2015/10/06.
|
||||
// Copyright © 2015 suzuki_keishi. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SKPhotoBrowser
|
||||
|
||||
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, SKPhotoBrowserDelegate {
|
||||
private final let screenBound = UIScreen.mainScreen().bounds
|
||||
private var screenWidth: CGFloat { return screenBound.size.width }
|
||||
private var screenHeight: CGFloat { return screenBound.size.height }
|
||||
|
||||
@IBOutlet weak var collectionView: UICollectionView!
|
||||
var images = [SKPhotoProtocol]()
|
||||
var caption = ["Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
|
||||
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
|
||||
"It has survived not only five centuries, but also the leap into electronic typesetting",
|
||||
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
|
||||
]
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
for i in 0..<10 {
|
||||
let photo = SKPhoto.photoWithImage(UIImage(named: "image\(i%10).jpg")!)
|
||||
if i == 0 {
|
||||
// MARK: [BUG] this image can't be cached!!!
|
||||
photo.photoURL = "https://images.unsplash.com/photo-1451906278231-17b8ff0a8880?crop=entropy&dpr=2&fit=crop&fm=jpg&h=800&ixjsv=2.1.0&ixlib=rb-0.3.5&q=50&w=1275"
|
||||
}
|
||||
if i == 1 {
|
||||
photo.photoURL = "https://images.unsplash.com/photo-1458640904116-093b74971de9?crop=entropy&dpr=2&fit=crop&fm=jpg&h=800&ixjsv=2.1.0&ixlib=rb-0.3.5&q=50&w=1275"
|
||||
}
|
||||
photo.caption = caption[i%10]
|
||||
images.append(photo)
|
||||
}
|
||||
|
||||
setupTableView()
|
||||
}
|
||||
|
||||
private func setupTableView() {
|
||||
collectionView.delegate = self
|
||||
collectionView.dataSource = self
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
super.didReceiveMemoryWarning()
|
||||
}
|
||||
|
||||
override func prefersStatusBarHidden() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDataSource
|
||||
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return images.count
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath) as? ExampleCollectionViewCell else {
|
||||
return UICollectionViewCell()
|
||||
}
|
||||
cell.exampleImageView.image = images[indexPath.row].underlyingImage
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
|
||||
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
|
||||
return
|
||||
}
|
||||
guard let originImage = cell.exampleImageView.image else {
|
||||
return
|
||||
}
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
browser.initializePageIndex(indexPath.row)
|
||||
browser.delegate = self
|
||||
browser.displayDeleteButton = true
|
||||
browser.statusBarStyle = .LightContent
|
||||
browser.bounceAnimation = true
|
||||
// browser.enableSingleTapDismiss = true
|
||||
|
||||
// Can hide the action button by setting to false
|
||||
browser.displayAction = true
|
||||
|
||||
// delete action(you must write `removePhoto` delegate, what resource you want to delete)
|
||||
// browser.displayDeleteButton = true
|
||||
|
||||
// Optional action button titles (if left off, it uses activity controller
|
||||
// browser.actionButtonTitles = ["Do One Action", "Do Another Action"]
|
||||
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
}
|
||||
|
||||
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
|
||||
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
|
||||
return CGSize(width: screenWidth/2 - 5, height: 300)
|
||||
} else {
|
||||
return CGSize(width: screenWidth/2 - 5, height: 200)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SKPhotoBrowserDelegate
|
||||
func didShowPhotoAtIndex(index: Int) {
|
||||
collectionView.visibleCells().forEach({$0.hidden = false})
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
|
||||
}
|
||||
|
||||
func willDismissAtPageIndex(index: Int) {
|
||||
collectionView.visibleCells().forEach({$0.hidden = false})
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
|
||||
}
|
||||
|
||||
func willShowActionSheet(photoIndex: Int) {
|
||||
// do some handle if you need
|
||||
}
|
||||
|
||||
func didDismissAtPageIndex(index: Int) {
|
||||
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = false
|
||||
}
|
||||
|
||||
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
|
||||
// handle dismissing custom actions
|
||||
}
|
||||
|
||||
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
|
||||
reload()
|
||||
}
|
||||
|
||||
func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView? {
|
||||
|
||||
return collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))
|
||||
}
|
||||
|
||||
override func preferredStatusBarStyle() -> UIStatusBarStyle {
|
||||
return .LightContent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ExampleCollectionViewCell: UICollectionViewCell {
|
||||
|
||||
@IBOutlet weak var exampleImageView: UIImageView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
exampleImageView.image = nil
|
||||
layer.cornerRadius = 25.0
|
||||
layer.masksToBounds = true
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
exampleImageView.image = nil
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue