Compare commits
63 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 | |
|
|
9b77a81734 | |
|
|
50fe55684f | |
|
|
2362808151 | |
|
|
6d6a93a1ae | |
|
|
cb53afd3a2 | |
|
|
733b97cba5 | |
|
|
0dd21f9ef7 | |
|
|
90ec128c11 | |
|
|
a066d956ff | |
|
|
d2e350c1d3 | |
|
|
96fed7a499 | |
|
|
823fe89a1c | |
|
|
cdef28539e | |
|
|
8d2d24ff4f | |
|
|
4b897dcae0 |
|
|
@ -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.3"
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -61,13 +61,13 @@ private extension SKCaptionView {
|
|||
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
|
||||
photoLabel.opaque = false
|
||||
photoLabel.backgroundColor = .clearColor()
|
||||
photoLabel.textColor = .whiteColor()
|
||||
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 = UIFont.systemFontOfSize(17.0)
|
||||
photoLabel.font = SKPhotoBrowserOptions.captionFont
|
||||
photoLabel.text = photo?.caption
|
||||
addSubview(photoLabel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +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!)
|
||||
|
|
|
|||
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
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
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 */
|
||||
|
|
@ -70,6 +71,7 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
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 /* FromLocalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FromLocalViewController.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -91,6 +93,8 @@
|
|||
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 */
|
||||
|
|
@ -99,6 +103,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */,
|
||||
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -111,6 +116,8 @@
|
|||
8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */,
|
||||
8909B5591BC792150060A053 /* SKPhotoBrowserExample */,
|
||||
8909B5581BC792150060A053 /* Products */,
|
||||
D1D8ECD45B290372235B8168 /* Pods */,
|
||||
C646D2C27A5B9CCC003A9BD1 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -198,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 */
|
||||
|
|
@ -205,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 = (
|
||||
);
|
||||
|
|
@ -231,6 +258,7 @@
|
|||
TargetAttributes = {
|
||||
8909B5561BC792150060A053 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -300,6 +328,54 @@
|
|||
};
|
||||
/* 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;
|
||||
|
|
@ -427,6 +503,7 @@
|
|||
};
|
||||
8909B56A1BC792150060A053 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
|
|
@ -435,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";
|
||||
|
|
@ -448,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,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15F34" 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>
|
||||
|
|
@ -88,14 +88,26 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -107,10 +107,9 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
|
|||
|
||||
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 FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ extension FromLocalViewController {
|
|||
}
|
||||
|
||||
cell.exampleImageView.image = UIImage(named: "image\(indexPath.row % 10).jpg")
|
||||
// cell.exampleImageView.contentMode = .ScaleAspectFill
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
|
@ -61,11 +62,13 @@ extension FromLocalViewController {
|
|||
guard let originImage = cell.exampleImageView.image else {
|
||||
return
|
||||
}
|
||||
|
||||
// SKPhotoBrowserOptions.displayToolbar = false
|
||||
|
||||
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
|
||||
// let browser = SKPhotoBrowser(photos: images)
|
||||
browser.initializePageIndex(indexPath.row)
|
||||
browser.delegate = self
|
||||
browser.statusBarStyle = .LightContent
|
||||
// browser.updateCloseButton(UIImage(named: "image1.jpg")!)
|
||||
|
||||
presentViewController(browser, animated: true, completion: {})
|
||||
}
|
||||
|
|
@ -130,6 +133,7 @@ private extension FromLocalViewController {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,20 @@
|
|||
|
||||
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) {
|
||||
|
|
@ -35,6 +43,8 @@ extension FromWebViewController {
|
|||
}
|
||||
|
||||
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
|
||||
SKCache.sharedCache.removeImageForKey("somekey")
|
||||
reload()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +53,7 @@ extension FromWebViewController {
|
|||
private extension FromWebViewController {
|
||||
func createWebPhotos() -> [SKPhotoProtocol] {
|
||||
return (0..<10).map { (i: Int) -> SKPhotoProtocol in
|
||||
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/15\(i)x15\(i).png")
|
||||
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150\(i)x150\(i).png")
|
||||
photo.caption = caption[i%10]
|
||||
photo.shouldCachePhotoURLImage = true
|
||||
return photo
|
||||
|
|
@ -51,3 +61,24 @@ private extension FromWebViewController {
|
|||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue