Compare commits

..

14 Commits

Author SHA1 Message Date
Grigory Ulanov e87ec9eb72 black color 2016-10-11 11:53:12 +03:00
Grigory Ulanov 2db02eecc0 text color changed 2016-10-11 11:15:08 +03:00
Grigory Ulanov d40c5d2c2e navigationBar added 2016-10-10 16:47:04 +03:00
Grigory Ulanov 5714a95908 full view controller swipe fade 2016-09-21 16:29:37 +03:00
keishi suzuki 8d2c1f3b38 Merge pull request #146 from suzuki-0000/bugfix/#145
fixed scrolling performance, result of adding padding to the displaye…
2016-09-16 11:06:54 +09:00
suzuki_keishi 464114e40d fixed scrolling performance, result of adding padding to the displayed image. 2016-09-16 11:06:06 +09:00
suzuki_keishi c3f8fa7e8a update change log. 2016-09-15 16:12:03 +09:00
suzuki_keishi 3b275293b5 update pod spec and changelog. 2016-09-15 16:06:48 +09:00
suzuki_keishi 6a591bcd5d Merge branch 'master' of github.com:suzuki-0000/SKPhotoBrowser into feature/#144 2016-09-15 16:05:31 +09:00
suzuki_keishi 26378bae91 crash in exapmle in xcode8 fixed. 2016-09-15 16:05:20 +09:00
keishi suzuki 709f024135 Merge pull request #144 from appsunited/master
Provides various UI configuration options via SKPhotoBrowserOptions
2016-09-15 16:04:37 +09:00
keishi suzuki 49131da2e0 update readme 2016-09-14 11:43:06 +09:00
Felix Weiss 1704f37363 Provides various UI configuration options via SKPhotoBrowserOptions like padding for images and color customizations. 2016-09-14 02:15:15 +02:00
keishi suzuki 554dac6756 update readme 2016-09-12 13:35:30 +09:00
33 changed files with 646 additions and 715 deletions

View File

@ -1,5 +1,20 @@
# 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
@ -7,7 +22,7 @@ Released on 9-2016
#### Fixed
- Issue with multiple actionButtonTitles #137
- fix swiftlint warnings #140
- Update for Xcode GM. #141
- Update for Xcode 8 GM (swift 2.3). #141
## 3.0.2
@ -16,7 +31,7 @@ Released on 9-2016
#### Fixed
- Issue with multiple actionButtonTitles #137
- Impossible to zoom when resolution is 1024x768 #134
- unexpectedly found nil while unwrapping an Optional value #133
- Crash bug at zooming scrool view #133
## 3.0.1

View File

@ -1,6 +1,7 @@
SKPhotoBrowser
========================
![Language](https://img.shields.io/badge/language-Swift%202.3-orange.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Cocoapods Compatible](https://img.shields.io/cocoapods/v/SKPhotoBrowser.svg?style=flat)](http://cocoadocs.org/docsets/SKPhotoBrowser)
@ -106,14 +107,39 @@ func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath i
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.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)
```
#### 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)
@ -130,8 +156,8 @@ SKCache.sharedCache.imageCache = CustomImageCache()
#### CustomButton Image
Close, Delete buttons are able to change image and frame.
``` swift
browser.browser.updateCloseButton(UIImage())
browser.browser.updateUpdateButton(UIImage())
browser.updateCloseButton(UIImage())
browser.updateUpdateButton(UIImage())
```
#### Delete Photo
@ -149,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
@ -183,10 +208,12 @@ 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
SKPhotoBrowserOptions.enableZoomBlackArea = true // default true
SKPhotoBrowserOptions.enableSingleTapDismiss = true // default false

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SKPhotoBrowser"
s.version = "3.1.0"
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" }

View File

@ -7,7 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
0A6BA1F31DAE0E2700778221 /* SKNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BA1F21DAE0E2700778221 /* SKNavigationBar.swift */; };
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 */; };
@ -45,7 +45,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0A6BA1F21DAE0E2700778221 /* SKNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKNavigationBar.swift; sourceTree = "<group>"; };
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>"; };
@ -137,8 +137,8 @@
8909B53F1BC791510060A053 /* SKPhoto.swift */,
8909B5411BC791510060A053 /* SKPhotoBrowser.swift */,
89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */,
0A6BA1F21DAE0E2700778221 /* SKNavigationBar.swift */,
890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */,
0AE527511DABB87500619FAD /* SKNavigationBar.swift */,
8909B5331BC791280060A053 /* SKPhotoBrowser.h */,
8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */,
89D0BA481D59966B002A811B /* SKMesurement.swift */,
@ -218,7 +218,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = suzuki_keishi;
TargetAttributes = {
8909B52F1BC791280060A053 = {
@ -303,7 +303,7 @@
8917B1B41D5A14B0000CE1C4 /* SKButtons.swift in Sources */,
89C24A821D657AD1005F09A9 /* SKPhotoBrowserOptions.swift in Sources */,
26C97AD51D0EB6870039F6CB /* SKCache.swift in Sources */,
0A6BA1F31DAE0E2700778221 /* SKNavigationBar.swift in Sources */,
0AE527521DABB87500619FAD /* SKNavigationBar.swift in Sources */,
210E53EF1C986D57008DD5E3 /* UIImage+Rotation.swift in Sources */,
8909B5431BC791510060A053 /* SKCaptionView.swift in Sources */,
8909B5491BC791510060A053 /* SKPhotoBrowser.swift in Sources */,
@ -344,10 +344,8 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -394,10 +392,8 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -417,7 +413,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@ -429,7 +424,6 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -442,7 +436,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@ -450,7 +444,6 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -462,7 +455,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.SKPhotoBrowser;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Release;
};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -10,13 +10,12 @@ import UIKit
@objc public protocol SKPhotoBrowserAnimatorDelegate {
func willPresent(_ browser: SKPhotoBrowser)
func willDismiss(_ browser: SKPhotoBrowser, sender: UIView?)
func willPresent(browser: SKPhotoBrowser)
func willDismiss(browser: SKPhotoBrowser)
}
class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
var resizableImageView: UIImageView?
var parentViewController: UIViewController?
var senderOriginImage: UIImage!
var senderViewOriginalFrame: CGRect = .zero
@ -25,7 +24,7 @@ class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
var finalImageViewFrame: CGRect = .zero
var bounceAnimation: Bool = false
var animationDuration: TimeInterval {
var animationDuration: NSTimeInterval {
if SKPhotoBrowserOptions.bounceAnimation {
return 0.5
}
@ -38,8 +37,8 @@ class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
return 1
}
func willPresent(_ browser: SKPhotoBrowser) {
guard let appWindow = UIApplication.shared.delegate?.window else {
func willPresent(browser: SKPhotoBrowser) {
guard let appWindow = UIApplication.sharedApplication().delegate?.window else {
return
}
guard let window = appWindow else {
@ -71,20 +70,23 @@ class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
presentAnimation(browser)
}
func willDismiss(_ browser: SKPhotoBrowser, sender: UIView?) {
guard let sender = sender, let image = browser.photoAtIndex(browser.currentPageIndex).underlyingImage,
let scrollView = browser.pageDisplayedAtIndex(browser.currentPageIndex) else {
self.resizableImageView?.removeFromSuperview()
browser.dismissPhotoBrowser(animated: true)
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.isHidden = true
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
@ -95,35 +97,34 @@ class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
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, view.layer.cornerRadius != 0 {
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)
}
parentViewController?.view.insertSubview(resizableImageView!, at: 1)
dismissAnimation(browser)
}
}
private extension SKAnimator {
func calcOriginFrame(_ sender: UIView) -> CGRect {
if let senderViewOriginalFrameTemp = sender.superview?.convert(sender.frame, to:nil) {
func calcOriginFrame(sender: UIView) -> CGRect {
if let senderViewOriginalFrameTemp = sender.superview?.convertRect(sender.frame, toView:nil) {
return senderViewOriginalFrameTemp
} else if let senderViewOriginalFrameTemp = sender.layer.superlayer?.convert(sender.frame, to: nil) {
} else if let senderViewOriginalFrameTemp = sender.layer.superlayer?.convertRect(sender.frame, toLayer: nil) {
return senderViewOriginalFrameTemp
} else {
return .zero
}
}
func calcFinalFrame(_ imageRatio: CGFloat) -> CGRect {
func calcFinalFrame(imageRatio: CGFloat) -> CGRect {
if SKMesurement.screenRatio < imageRatio {
let width = SKMesurement.screenWidth
let height = width / imageRatio
@ -139,16 +140,16 @@ private extension SKAnimator {
}
private extension SKAnimator {
func presentAnimation(_ browser: SKPhotoBrowser, completion: ((Void) -> Void)? = nil) {
browser.view.isHidden = true
func presentAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
browser.view.hidden = true
browser.view.alpha = 0.0
UIView.animate(
withDuration: animationDuration,
UIView.animateWithDuration(
animationDuration,
delay: 0,
usingSpringWithDamping:animationDamping,
initialSpringVelocity:0,
options:UIViewAnimationOptions(),
options:.CurveEaseInOut,
animations: {
browser.showButtons()
browser.backgroundView.alpha = 1.0
@ -156,21 +157,23 @@ private extension SKAnimator {
self.resizableImageView?.frame = self.finalImageViewFrame
},
completion: { (Bool) -> Void in
browser.view.isHidden = false
UIApplication.sharedApplication().setStatusBarHidden(!SKPhotoBrowserOptions.displayStatusbar, withAnimation: .Fade)
browser.view.hidden = false
browser.view.alpha = 1.0
browser.backgroundView.isHidden = true
browser.backgroundView.hidden = true
self.resizableImageView?.alpha = 0.0
})
}
func dismissAnimation(_ browser: SKPhotoBrowser, completion: ((Void) -> Void)? = nil) {
UIView.animate(
withDuration: animationDuration,
func dismissAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
UIView.animateWithDuration(
animationDuration,
delay:0,
usingSpringWithDamping:animationDamping,
initialSpringVelocity:0,
options:UIViewAnimationOptions(),
options:.CurveEaseInOut,
animations: {
browser.backgroundView.alpha = 0.0

View File

@ -9,7 +9,7 @@
import Foundation
// helpers which often used
private let bundle = Bundle(for: SKPhotoBrowser.self)
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
class SKButton: UIButton {
var showFrame: CGRect!
@ -17,7 +17,7 @@ class SKButton: UIButton {
var insets: UIEdgeInsets {
return UI_USER_INTERFACE_IDIOM() == .phone
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)
@ -25,21 +25,22 @@ class SKButton: UIButton {
var buttonTopOffset: CGFloat { return 5 }
func setup(_ imageName: String) {
backgroundColor = UIColor.clear
func setup(imageName: String) {
backgroundColor = .clearColor()
tintColor = SKPhotoBrowserOptions.textAndIconColor
imageEdgeInsets = insets
// clipsToBounds = true
translatesAutoresizingMaskIntoConstraints = true
autoresizingMask = [.flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin]
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
in: bundle, compatibleWith: nil) ?? UIImage()
setImage(image, for: UIControlState())
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
setImage(image, forState: .Normal)
}
func updateFrame() { }
func setFrameSize(_ size: CGSize) {
func setFrameSize(size: CGSize) {
let newRect = CGRect(x: margin, y: buttonTopOffset, width: size.width, height: size.height)
self.frame = newRect
showFrame = newRect
@ -80,7 +81,7 @@ class SKDeleteButton: SKButton {
override func updateFrame() {
}
override func setFrameSize(_ size: CGSize) {
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

View File

@ -8,15 +8,15 @@
import UIKit
open class SKCache {
open static let sharedCache = SKCache()
open var imageCache: SKCacheable
public class SKCache {
public static let sharedCache = SKCache()
public var imageCache: SKCacheable
init() {
self.imageCache = SKDefaultImageCache()
}
open func imageForKey(_ key: String) -> UIImage? {
public func imageForKey(key: String) -> UIImage? {
guard let cache = imageCache as? SKImageCacheable else {
return nil
}
@ -24,7 +24,7 @@ open class SKCache {
return cache.imageForKey(key)
}
open func setImage(_ image: UIImage, forKey key: String) {
public func setImage(image: UIImage, forKey key: String) {
guard let cache = imageCache as? SKImageCacheable else {
return
}
@ -32,7 +32,7 @@ open class SKCache {
cache.setImage(image, forKey: key)
}
open func removeImageForKey(_ key: String) {
public func removeImageForKey(key: String) {
guard let cache = imageCache as? SKImageCacheable else {
return
}
@ -40,7 +40,7 @@ open class SKCache {
cache.removeImageForKey(key)
}
open func imageForRequest(_ request: URLRequest) -> UIImage? {
public func imageForRequest(request: NSURLRequest) -> UIImage? {
guard let cache = imageCache as? SKRequestResponseCacheable else {
return nil
}
@ -51,31 +51,31 @@ open class SKCache {
return nil
}
open func setImageData(_ data: Data, response: URLResponse, request: URLRequest) {
public func setImageData(data: NSData, response: NSURLResponse, request: NSURLRequest) {
guard let cache = imageCache as? SKRequestResponseCacheable else {
return
}
let cachedResponse = CachedURLResponse(response: response, data: data)
let cachedResponse = NSCachedURLResponse(response: response, data: data)
cache.storeCachedResponse(cachedResponse, forRequest: request)
}
}
class SKDefaultImageCache: SKImageCacheable {
var cache: NSCache<AnyObject, AnyObject>
var cache: NSCache
init() {
cache = NSCache()
}
func imageForKey(_ key: String) -> UIImage? {
return cache.object(forKey: key as AnyObject) as? UIImage
func imageForKey(key: String) -> UIImage? {
return cache.objectForKey(key) as? UIImage
}
func setImage(_ image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key as AnyObject)
func setImage(image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key)
}
func removeImageForKey(_ key: String) {
cache.removeObject(forKey: key as AnyObject)
func removeImageForKey(key: String) {
cache.removeObjectForKey(key)
}
}

View File

@ -10,12 +10,12 @@ import UIKit.UIImage
public protocol SKCacheable {}
public protocol SKImageCacheable: SKCacheable {
func imageForKey(_ key: String) -> UIImage?
func setImage(_ image: UIImage, forKey key: String)
func removeImageForKey(_ key: String)
func imageForKey(key: String) -> UIImage?
func setImage(image: UIImage, forKey key: String)
func removeImageForKey(key: String)
}
public protocol SKRequestResponseCacheable: SKCacheable {
func cachedResponseForRequest(_ request: URLRequest) -> CachedURLResponse?
func storeCachedResponse(_ cachedResponse: CachedURLResponse, forRequest request: URLRequest)
func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse?
func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest)
}

View File

@ -7,31 +7,11 @@
//
import UIKit
fileprivate func < <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
fileprivate func > <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
}
open class SKCaptionView: UIView {
fileprivate var photo: SKPhotoProtocol?
fileprivate var photoLabel: UILabel!
fileprivate var photoLabelPadding: CGFloat = 10
public class SKCaptionView: UIView {
private var photo: SKPhotoProtocol?
private var photoLabel: UILabel!
private var photoLabelPadding: CGFloat = 10
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
@ -42,13 +22,13 @@ open class SKCaptionView: UIView {
}
public convenience init(photo: SKPhotoProtocol) {
let screenBound = UIScreen.main.bounds
let screenBound = UIScreen.mainScreen().bounds
self.init(frame: CGRect(x: 0, y: 0, width: screenBound.size.width, height: screenBound.size.height))
self.photo = photo
setup()
}
open override func sizeThatFits(_ size: CGSize) -> CGSize {
public override func sizeThatFits(size: CGSize) -> CGSize {
guard let text = photoLabel.text else {
return CGSize.zero
}
@ -61,7 +41,7 @@ open class SKCaptionView: UIView {
let height: CGFloat = photoLabel.font.lineHeight * CGFloat(photoLabel.numberOfLines)
let attributedText = NSAttributedString(string: text, attributes: [NSFontAttributeName: font])
let textSize = attributedText.boundingRect(with: CGSize(width: width, height: height), options: .usesLineFragmentOrigin, context: nil).size
let textSize = attributedText.boundingRectWithSize(CGSize(width: width, height: height), options: .UsesLineFragmentOrigin, context: nil).size
return CGSize(width: textSize.width, height: textSize.height + photoLabelPadding * 2)
}
@ -69,8 +49,8 @@ open class SKCaptionView: UIView {
private extension SKCaptionView {
func setup() {
isOpaque = false
autoresizingMask = [.flexibleWidth, .flexibleTopMargin, .flexibleRightMargin, .flexibleLeftMargin]
opaque = false
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
// setup photoLabel
setupPhotoLabel()
@ -78,16 +58,16 @@ private extension SKCaptionView {
func setupPhotoLabel() {
photoLabel = UILabel(frame: CGRect(x: photoLabelPadding, y: 0, width: bounds.size.width - (photoLabelPadding * 2), height: bounds.size.height))
photoLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
photoLabel.isOpaque = false
photoLabel.backgroundColor = UIColor.clear
photoLabel.textColor = UIColor.white
photoLabel.textAlignment = .center
photoLabel.lineBreakMode = .byTruncatingTail
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
photoLabel.opaque = false
photoLabel.backgroundColor = .clearColor()
photoLabel.textColor = SKPhotoBrowserOptions.textAndIconColor
photoLabel.textAlignment = .Center
photoLabel.lineBreakMode = .ByTruncatingTail
photoLabel.numberOfLines = 3
photoLabel.shadowColor = UIColor(white: 0.0, alpha: 0.5)
photoLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
photoLabel.font = UIFont.systemFont(ofSize: 17.0)
photoLabel.font = SKPhotoBrowserOptions.captionFont
photoLabel.text = photo?.caption
addSubview(photoLabel)
}

View File

@ -9,8 +9,8 @@
import UIKit
@objc protocol SKDetectingImageViewDelegate {
func handleImageViewSingleTap(_ touchPoint: CGPoint)
func handleImageViewDoubleTap(_ touchPoint: CGPoint)
func handleImageViewSingleTap(touchPoint: CGPoint)
func handleImageViewDoubleTap(touchPoint: CGPoint)
}
class SKDetectingImageView: UIImageView {
@ -26,25 +26,25 @@ class SKDetectingImageView: UIImageView {
setup()
}
func handleDoubleTap(_ recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewDoubleTap(recognizer.location(in: self))
func handleDoubleTap(recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewDoubleTap(recognizer.locationInView(self))
}
func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewSingleTap(recognizer.location(in: self))
func handleSingleTap(recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewSingleTap(recognizer.locationInView(self))
}
}
private extension SKDetectingImageView {
func setup() {
isUserInteractionEnabled = true
userInteractionEnabled = true
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
doubleTap.numberOfTapsRequired = 2
addGestureRecognizer(doubleTap)
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
singleTap.require(toFail: doubleTap)
singleTap.requireGestureRecognizerToFail(doubleTap)
addGestureRecognizer(singleTap)
}
}
}

View File

@ -9,17 +9,17 @@
import UIKit
@objc protocol SKDetectingViewDelegate {
func handleSingleTap(_ view: UIView, touch: UITouch)
func handleDoubleTap(_ view: UIView, touch: UITouch)
func handleSingleTap(view: UIView, touch: UITouch)
func handleDoubleTap(view: UIView, touch: UITouch)
}
class SKDetectingView: UIView {
weak var delegate: SKDetectingViewDelegate?
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
defer {
_ = next
nextResponder()
}
guard let touch = touches.first else {
@ -32,11 +32,11 @@ class SKDetectingView: UIView {
}
}
func handleSingleTap(_ touch: UITouch) {
func handleSingleTap(touch: UITouch) {
delegate?.handleSingleTap(self, touch: touch)
}
func handleDoubleTap(_ touch: UITouch) {
func handleDoubleTap(touch: UITouch) {
delegate?.handleDoubleTap(self, touch: touch)
}
}

View File

@ -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
}
}

View File

@ -9,14 +9,14 @@
import UIKit
// MARK: - SKLocalPhoto
open class SKLocalPhoto: NSObject, SKPhotoProtocol {
public class SKLocalPhoto: NSObject, SKPhotoProtocol {
open var underlyingImage: UIImage!
open var photoURL: String!
open var contentMode: UIViewContentMode = .scaleToFill
open var shouldCachePhotoURLImage: Bool = false
open var caption: String!
open var index: Int = 0
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
override init() {
super.init()
@ -33,9 +33,9 @@ open class SKLocalPhoto: NSObject, SKPhotoProtocol {
underlyingImage = holder
}
open func checkCache() {}
public func checkCache() {}
open func loadUnderlyingImageAndNotify() {
public func loadUnderlyingImageAndNotify() {
if underlyingImage != nil && photoURL == nil {
loadUnderlyingImageComplete()
@ -43,8 +43,8 @@ open class SKLocalPhoto: NSObject, SKPhotoProtocol {
if photoURL != nil {
// Fetch Image
if FileManager.default.fileExists(atPath: photoURL) {
if let data = FileManager.default.contents(atPath: photoURL) {
if NSFileManager.defaultManager().fileExistsAtPath(photoURL) {
if let data = NSFileManager.defaultManager().contentsAtPath(photoURL) {
self.loadUnderlyingImageComplete()
if let image = UIImage(data: data) {
self.underlyingImage = image
@ -55,16 +55,16 @@ open class SKLocalPhoto: NSObject, SKPhotoProtocol {
}
}
open func loadUnderlyingImageComplete() {
NotificationCenter.default.post(name: Notification.Name(rawValue: SKPHOTO_LOADING_DID_END_NOTIFICATION), object: self)
public func loadUnderlyingImageComplete() {
NSNotificationCenter.defaultCenter().postNotificationName(SKPHOTO_LOADING_DID_END_NOTIFICATION, object: self)
}
// MARK: - class func
open class func photoWithImageURL(_ url: String) -> SKLocalPhoto {
public class func photoWithImageURL(url: String) -> SKLocalPhoto {
return SKLocalPhoto(url: url)
}
open class func photoWithImageURL(_ url: String, holder: UIImage?) -> SKLocalPhoto {
public class func photoWithImageURL(url: String, holder: UIImage?) -> SKLocalPhoto {
return SKLocalPhoto(url: url, holder: holder)
}
}

View File

@ -10,19 +10,19 @@ import Foundation
import UIKit
struct SKMesurement {
static let isPhone: Bool = UIDevice.current.userInterfaceIdiom == .phone
static let isPad: Bool = UIDevice.current.userInterfaceIdiom == .pad
static let isPhone: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Phone
static let isPad: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Pad
static var statusBarH: CGFloat {
return UIApplication.shared.statusBarFrame.height
return UIApplication.sharedApplication().statusBarFrame.height
}
static var screenHeight: CGFloat {
return UIScreen.main.bounds.height
return UIScreen.mainScreen().bounds.height
}
static var screenWidth: CGFloat {
return UIScreen.main.bounds.width
return UIScreen.mainScreen().bounds.width
}
static var screenScale: CGFloat {
return UIScreen.main.scale
return UIScreen.mainScreen().scale
}
static var screenRatio: CGFloat {
return screenWidth / screenHeight

View File

@ -2,119 +2,54 @@
// SKNavigationBar.swift
// SKPhotoBrowser
//
// Created by Григорий Уланов on 12.10.16.
// Created by Григорий Уланов on 10.10.16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
class SKNavigationBar: UIView {
class SKNavigationBar: UINavigationBar {
var showFrame: CGRect!
var hideFrame: CGRect!
private static let toolBarHeight: CGFloat = 44.0
private static let toolBarHeight: CGFloat = 64.0
fileprivate weak var browser: SKPhotoBrowser?
private var countLabel: UILabel?
private var doneButton: UIButton?
var onDoneTap: (() -> Void)?
var isStatusBarHidden: Bool = false {
didSet {
if let bounds = superview?.bounds {
updateFrame(bounds.size)
}
}
}
private var isBigStatusBar: Bool {
return statusBarHeight > 20 ? true : false
}
private var statusBarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.height
}
private weak var browser: SKPhotoBrowser?
convenience init(browser: SKPhotoBrowser) {
self.init(frame: CGRect.zero)
self.browser = browser
backgroundColor = UIColor.black.withAlphaComponent(0.65)
translucent = false
tintColor = UIColor.whiteColor()
barTintColor = UIColor.blackColor()
titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
countLabel = UILabel()
countLabel?.font = UIFont.boldSystemFont(ofSize: 18)
countLabel?.textColor = UIColor.white
countLabel?.textAlignment = .center
if let countLabel = countLabel {
addSubview(countLabel)
}
doneButton = UIButton()
doneButton?.setTitle(SKPhotoBrowserOptions.navigationBarDoneTitle, for: .normal)
doneButton?.setTitleColor(UIColor.white, for: .normal)
doneButton?.addTarget(self, action: #selector(doneButtonAction), for: .touchUpInside)
if let doneButton = doneButton {
addSubview(doneButton)
}
let navigationItem = UINavigationItem()
pushNavigationItem(navigationItem, animated: false)
}
@objc private func doneButtonAction() {
onDoneTap?()
}
func updateNavigationBar(_ currentPageIndex: Int) {
func updateNavigationBar(currentPageIndex: Int) {
guard let browser = browser else { return }
if browser.numberOfPhotos > 1 {
self.countLabel?.text = "\(currentPageIndex + 1) \(SKPhotoBrowserOptions.navigationBarCounterSepatator) \(browser.numberOfPhotos)"
self.topItem?.title = "\(currentPageIndex + 1) \(SKPhotoBrowserOptions.navigationBarCounterSepatator) \(browser.numberOfPhotos)"
} else {
self.countLabel?.text = nil
self.topItem?.title = nil
}
}
override func layoutSubviews() {
super.layoutSubviews()
if !isStatusBarHidden {
countLabel?.frame = CGRect(x: 0,
y: isBigStatusBar ? 0 : statusBarHeight,
width: bounds.width,
height: isBigStatusBar ? bounds.height : bounds.height - statusBarHeight)
doneButton?.frame = CGRect(x: 0,
y: isBigStatusBar ? 0 : statusBarHeight,
width: 85,
height: isBigStatusBar ? bounds.height : bounds.height - statusBarHeight)
} else {
countLabel?.frame = bounds
doneButton?.frame = CGRect(x: 0, y: 0, width: 85, height: bounds.height)
}
}
private func setNewFrame(rect: CGRect) {
frame = rect
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)
layoutSubviews()
}
func updateFrame(_ parentSize: CGSize) {
var newRect: CGRect?
if !isStatusBarHidden {
newRect = CGRect(x: 0,
y: isBigStatusBar ? statusBarHeight/2 : 0,
width: parentSize.width,
height: isBigStatusBar ? SKNavigationBar.toolBarHeight : SKNavigationBar.toolBarHeight + statusBarHeight)
} else {
newRect = CGRect(x: 0, y: 0, width: parentSize.width, height: SKNavigationBar.toolBarHeight)
}
if let newRect = newRect {
setNewFrame(rect: newRect)
}
func updateFrame(parentSize: CGSize) {
let newRect = CGRect(x: 0, y: 0, width: parentSize.width, height: SKNavigationBar.toolBarHeight)
setNewFrame(newRect)
}
}

View File

@ -11,10 +11,10 @@ import Foundation
class SKPagingScrollView: UIScrollView {
let pageIndexTagOffset: Int = 1000
let sideMargin: CGFloat = 10
fileprivate var visiblePages = [SKZoomingScrollView]()
fileprivate var recycledPages = [SKZoomingScrollView]()
private var visiblePages = [SKZoomingScrollView]()
private var recycledPages = [SKZoomingScrollView]()
fileprivate weak var browser: SKPhotoBrowser?
private weak var browser: SKPhotoBrowser?
var numberOfPhotos: Int {
return browser?.photos.count ?? 0
}
@ -27,9 +27,9 @@ class SKPagingScrollView: UIScrollView {
override init(frame: CGRect) {
super.init(frame: frame)
isPagingEnabled = true
showsHorizontalScrollIndicator = true
showsVerticalScrollIndicator = true
pagingEnabled = true
showsHorizontalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
showsVerticalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
}
convenience init(frame: CGRect, browser: SKPhotoBrowser) {
@ -45,8 +45,8 @@ class SKPagingScrollView: UIScrollView {
recycledPages.removeAll()
}
func loadAdjacentPhotosIfNecessary(_ photo: SKPhotoProtocol, currentPageIndex: Int) {
guard let browser = browser, let page = pageDisplayingAtPhoto(photo) else {
func loadAdjacentPhotosIfNecessary(photo: SKPhotoProtocol, currentPageIndex: Int) {
guard let browser = browser, page = pageDisplayingAtPhoto(photo) else {
return
}
let pageIndex = (page.tag - pageIndexTagOffset)
@ -75,11 +75,11 @@ class SKPagingScrollView: UIScrollView {
}
}
func animate(_ frame: CGRect) {
func animate(frame: CGRect) {
setContentOffset(CGPoint(x: frame.origin.x - sideMargin, y: 0), animated: true)
}
func updateFrame(_ bounds: CGRect, currentPageIndex: Int) {
func updateFrame(bounds: CGRect, currentPageIndex: Int) {
var frame = bounds
frame.origin.x -= sideMargin
frame.size.width += (2 * sideMargin)
@ -105,7 +105,7 @@ class SKPagingScrollView: UIScrollView {
contentSize = CGSize(width: bounds.size.width * CGFloat(numberOfPhotos), height: bounds.size.height)
}
func updateContentOffset(_ index: Int) {
func updateContentOffset(index: Int) {
let pageWidth = bounds.size.width
let newOffset = CGFloat(index) * pageWidth
contentOffset = CGPoint(x: newOffset, y: 0)
@ -126,7 +126,7 @@ class SKPagingScrollView: UIScrollView {
}
let visibleSet: Set<SKZoomingScrollView> = Set(visiblePages)
let visibleSetWithoutRecycled: Set<SKZoomingScrollView> = visibleSet.subtracting(recycledPages)
let visibleSetWithoutRecycled: Set<SKZoomingScrollView> = visibleSet.subtract(recycledPages)
visiblePages = Array(visibleSetWithoutRecycled)
while recycledPages.count > 2 {
@ -157,7 +157,7 @@ class SKPagingScrollView: UIScrollView {
}
}
func frameForCaptionView(_ captionView: SKCaptionView, index: Int) -> CGRect {
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
@ -165,7 +165,7 @@ class SKPagingScrollView: UIScrollView {
width: pageFrame.size.width, height: captionSize.height)
}
func pageDisplayedAtIndex(_ index: Int) -> SKZoomingScrollView? {
func pageDisplayedAtIndex(index: Int) -> SKZoomingScrollView? {
for page in visiblePages {
if page.tag - pageIndexTagOffset == index {
return page
@ -174,7 +174,7 @@ class SKPagingScrollView: UIScrollView {
return nil
}
func pageDisplayingAtPhoto(_ photo: SKPhotoProtocol) -> SKZoomingScrollView? {
func pageDisplayingAtPhoto(photo: SKPhotoProtocol) -> SKZoomingScrollView? {
for page in visiblePages {
if page.photo === photo {
return page
@ -195,15 +195,15 @@ class SKPagingScrollView: UIScrollView {
}
private extension SKPagingScrollView {
func frameForPageAtIndex(_ index: Int) -> CGRect {
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), photo.caption != nil else {
func createCaptionView(index: Int) -> SKCaptionView? {
guard let photo = browser?.photoAtIndex(index) where photo.caption != nil else {
return nil
}
return SKCaptionView(photo: photo)

View File

@ -18,14 +18,14 @@ import UIKit
}
// MARK: - SKPhoto
open class SKPhoto: NSObject, SKPhotoProtocol {
public class SKPhoto: NSObject, SKPhotoProtocol {
open var underlyingImage: UIImage!
open var photoURL: String!
open var contentMode: UIViewContentMode = .scaleAspectFill
open var shouldCachePhotoURLImage: Bool = false
open var caption: String!
open var index: Int = 0
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
override init() {
super.init()
@ -47,7 +47,7 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
underlyingImage = holder
}
open func checkCache() {
public func checkCache() {
guard let photoURL = photoURL else {
return
}
@ -56,7 +56,7 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
}
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
let request = URLRequest(url: URL(string: photoURL)!)
let request = NSURLRequest(URL: NSURL(string: photoURL)!)
if let img = SKCache.sharedCache.imageForRequest(request) {
underlyingImage = img
}
@ -67,7 +67,7 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
}
}
open func loadUnderlyingImageAndNotify() {
public func loadUnderlyingImageAndNotify() {
if underlyingImage != nil {
loadUnderlyingImageComplete()
@ -76,33 +76,27 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
if photoURL != nil {
// Fetch Image
let session = URLSession(configuration: URLSessionConfiguration.default)
if let nsURL = URL(string: photoURL) {
var task: URLSessionDataTask!
var loadingRequest = URLRequest(url: nsURL)
if let customHeaders = SKPhotoBrowserOptions.imageLoadingCustomHeaders {
loadingRequest.allHTTPHeaderFields = customHeaders
}
task = session.dataTask(with: loadingRequest, completionHandler: { [weak self] (data, response, error) in
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
if let nsURL = NSURL(string: photoURL) {
var task: NSURLSessionDataTask!
task = session.dataTaskWithURL(nsURL, completionHandler: { [weak self](response: NSData?, data: NSURLResponse?, error: NSError?) in
if let _self = self {
if error != nil {
DispatchQueue.main.async {
dispatch_async(dispatch_get_main_queue()) {
_self.loadUnderlyingImageComplete()
}
}
if let data = data, let response = response, let image = UIImage(data: data) {
if let res = response, image = UIImage(data: res) {
if _self.shouldCachePhotoURLImage {
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
SKCache.sharedCache.setImageData(data, response: response, request: task.originalRequest!)
SKCache.sharedCache.setImageData(response!, response: data!, request: task.originalRequest!)
} else {
SKCache.sharedCache.setImage(image, forKey: _self.photoURL)
}
}
DispatchQueue.main.async {
dispatch_async(dispatch_get_main_queue()) {
_self.underlyingImage = image
_self.loadUnderlyingImageComplete()
}
@ -115,8 +109,8 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
}
}
open func loadUnderlyingImageComplete() {
NotificationCenter.default.post(name: Notification.Name(rawValue: SKPHOTO_LOADING_DID_END_NOTIFICATION), object: self)
public func loadUnderlyingImageComplete() {
NSNotificationCenter.defaultCenter().postNotificationName(SKPHOTO_LOADING_DID_END_NOTIFICATION, object: self)
}
}
@ -124,15 +118,15 @@ open class SKPhoto: NSObject, SKPhotoProtocol {
// MARK: - Static Function
extension SKPhoto {
public static func photoWithImage(_ image: UIImage) -> SKPhoto {
public static func photoWithImage(image: UIImage) -> SKPhoto {
return SKPhoto(image: image)
}
public static func photoWithImageURL(_ url: String) -> SKPhoto {
public static func photoWithImageURL(url: String) -> SKPhoto {
return SKPhoto(url: url)
}
public static func photoWithImageURL(_ url: String, holder: UIImage?) -> SKPhoto {
public static func photoWithImageURL(url: String, holder: UIImage?) -> SKPhoto {
return SKPhoto(url: url, holder: holder)
}
}

View File

@ -11,112 +11,108 @@ import UIKit
public let SKPHOTO_LOADING_DID_END_NOTIFICATION = "photoLoadingDidEndNotification"
// MARK: - SKPhotoBrowser
open class SKPhotoBrowser: UIViewController {
public class SKPhotoBrowser: UIViewController {
let pageIndexTagOffset: Int = 1000
fileprivate var closeButton: SKCloseButton!
fileprivate var deleteButton: SKDeleteButton!
fileprivate var toolbar: SKToolbar!
fileprivate var navigationBar: SKNavigationBar!
private var closeButton: SKCloseButton!
private var deleteButton: SKDeleteButton!
private var toolbar: SKToolbar!
private var navigationBar: SKNavigationBar!
// actions
fileprivate var activityViewController: UIActivityViewController!
fileprivate var panGesture: UIPanGestureRecognizer!
private var activityViewController: UIActivityViewController!
private var panGesture: UIPanGestureRecognizer!
// tool for controls
fileprivate var applicationWindow: UIWindow!
fileprivate lazy var pagingScrollView: SKPagingScrollView = SKPagingScrollView(frame: self.view.frame, browser: self)
private var applicationWindow: UIWindow!
private lazy var pagingScrollView: SKPagingScrollView = SKPagingScrollView(frame: self.view.frame, browser: self)
var backgroundView: UIView!
var initialPageIndex: Int = 0
var currentPageIndex: Int = 0
// status bar
private var isStatusBarHidden: Bool = false {
didSet {
setNeedsStatusBarAppearanceUpdate()
navigationBar.isStatusBarHidden = isStatusBarHidden
}
}
// for status check property
fileprivate var isEndAnimationByToolBar: Bool = true
fileprivate var isViewActive: Bool = false
fileprivate var isPerformingLayout: Bool = false
private var isEndAnimationByToolBar: Bool = true
private var isViewActive: Bool = false
private var isPerformingLayout: Bool = false
// pangesture property
fileprivate var firstX: CGFloat = 0.0
fileprivate var firstY: CGFloat = 0.0
private var firstX: CGFloat = 0.0
private var firstY: CGFloat = 0.0
// timer
fileprivate var controlVisibilityTimer: Timer!
private var controlVisibilityTimer: NSTimer!
// blocks
public var willDismissPage: ((_ animated: Bool) -> Void)?
public var willDismissPage: (() -> Void)?
public var didPresentPage: (() -> Void)?
// delegate
fileprivate let animator = SKAnimator()
open weak var delegate: SKPhotoBrowserDelegate?
private let animator = SKAnimator()
public weak var delegate: SKPhotoBrowserDelegate?
// photos
var photos: [SKPhotoProtocol] = [SKPhotoProtocol]()
var numberOfPhotos: Int {
return photos.count
}
// statusbar initial state
private var statusbarHidden: Bool = UIApplication.sharedApplication().statusBarHidden
// MARK - Initializer
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
public override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: Bundle!) {
public override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nil, bundle: nil)
setup()
}
public convenience init(photos: [SKPhotoProtocol]) {
self.init(nibName: nil, bundle: nil)
let pictures = photos.flatMap { $0 }
for photo in pictures {
let picutres = photos.flatMap { $0 }
for photo in picutres {
photo.checkCache()
self.photos.append(photo)
}
}
public convenience init(originImage: UIImage, photos: [SKPhotoProtocol], animatedFromView: UIView, fromViewController: UIViewController) {
public convenience init(originImage: UIImage, photos: [SKPhotoProtocol], animatedFromView: UIView) {
self.init(nibName: nil, bundle: nil)
animator.senderOriginImage = originImage
animator.senderViewForAnimation = animatedFromView
animator.parentViewController = fromViewController
let pictures = photos.flatMap { $0 }
for photo in pictures {
let picutres = photos.flatMap { $0 }
for photo in picutres {
photo.checkCache()
self.photos.append(photo)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func setup() {
guard let window = UIApplication.shared.delegate?.window else {
guard let window = UIApplication.sharedApplication().delegate?.window else {
return
}
applicationWindow = window
modalPresentationCapturesStatusBarAppearance = true
modalPresentationStyle = .overFullScreen
modalTransitionStyle = .crossDissolve
NotificationCenter.default.addObserver(self, selector: #selector(self.handleSKPhotoLoadingDidEndNotification(_:)), name: NSNotification.Name(rawValue: SKPHOTO_LOADING_DID_END_NOTIFICATION), object: nil)
modalPresentationStyle = .Custom
modalTransitionStyle = .CrossDissolve
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.handleSKPhotoLoadingDidEndNotification(_:)), name: SKPHOTO_LOADING_DID_END_NOTIFICATION, object: nil)
}
// MARK: - override
override open func viewDidLoad() {
override public func viewDidLoad() {
super.viewDidLoad()
configureAppearance()
@ -126,13 +122,10 @@ open class SKPhotoBrowser: UIViewController {
configureToolbar()
animator.willPresent(self)
NotificationCenter.default.addObserver(self, selector: #selector(didChangeOrientation), name: .UIDeviceOrientationDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didChangeStatusBarFrame), name: .UIApplicationDidChangeStatusBarFrame, object: nil)
didPresentPage?()
}
override open func viewWillAppear(_ animated: Bool) {
override public func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
reloadData()
@ -142,61 +135,37 @@ open class SKPhotoBrowser: UIViewController {
i = i + 1
}
}
func didChangeOrientation() {
isStatusBarHidden = UIApplication.shared.statusBarOrientation != .portrait
}
func didChangeStatusBarFrame() {
viewWillLayoutSubviews()
view.layoutSubviews()
viewDidLayoutSubviews()
}
open override func viewDidLayoutSubviews() {
setNeedsStatusBarAppearanceUpdate()
override public func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
isPerformingLayout = true
closeButton.updateFrame()
deleteButton.updateFrame()
navigationBar.updateFrame(view.bounds.size)
pagingScrollView.updateFrame(view.bounds, currentPageIndex: currentPageIndex)
toolbar.frame = frameForToolbarAtOrientation()
// where did start
delegate?.didShowPhotoAtIndex?(currentPageIndex)
isPerformingLayout = false
super.viewDidLayoutSubviews()
}
override open func viewDidAppear(_ animated: Bool) {
override public func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
isViewActive = true
setNeedsStatusBarAppearanceUpdate()
didPresentPage?()
}
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
open override var prefersStatusBarHidden: Bool {
return isStatusBarHidden
}
// MARK: - Notification
open func handleSKPhotoLoadingDidEndNotification(_ notification: Notification) {
public func handleSKPhotoLoadingDidEndNotification(notification: NSNotification) {
guard let photo = notification.object as? SKPhotoProtocol else {
return
}
DispatchQueue.main.async(execute: {
guard let page = self.pagingScrollView.pageDisplayingAtPhoto(photo), let photo = page.photo else {
dispatch_async(dispatch_get_main_queue(), {
guard let page = self.pagingScrollView.pageDisplayingAtPhoto(photo), photo = page.photo else {
return
}
@ -209,18 +178,17 @@ open class SKPhotoBrowser: UIViewController {
})
}
open func loadAdjacentPhotosIfNecessary(_ photo: SKPhotoProtocol) {
public func loadAdjacentPhotosIfNecessary(photo: SKPhotoProtocol) {
pagingScrollView.loadAdjacentPhotosIfNecessary(photo, currentPageIndex: currentPageIndex)
}
// MARK: - initialize / setup
open func reloadData() {
backgroundView.backgroundColor = view.backgroundColor
public func reloadData() {
performLayout()
view.setNeedsLayout()
}
open func performLayout() {
public func performLayout() {
isPerformingLayout = true
toolbar.updateToolbar(currentPageIndex)
@ -238,51 +206,52 @@ open class SKPhotoBrowser: UIViewController {
isPerformingLayout = false
}
open func prepareForClosePhotoBrowser() {
public func prepareForClosePhotoBrowser() {
UIApplication.sharedApplication().setStatusBarHidden(statusbarHidden, withAnimation: .None)
cancelControlHiding()
applicationWindow.removeGestureRecognizer(panGesture)
NSObject.cancelPreviousPerformRequests(withTarget: self)
NSObject.cancelPreviousPerformRequestsWithTarget(self)
}
open func dismissPhotoBrowser(animated: Bool, completion: ((Void) -> Void)? = nil) {
public func dismissPhotoBrowser(animated animated: Bool, completion: (Void -> Void)? = nil) {
prepareForClosePhotoBrowser()
if !animated {
modalTransitionStyle = .crossDissolve
modalTransitionStyle = .CrossDissolve
}
dismiss(animated: !animated) {
dismissViewControllerAnimated(!animated) {
completion?()
self.delegate?.didDismissAtPageIndex?(self.currentPageIndex)
}
}
open func determineAndClose(sender: UIView?) {
public func determineAndClose() {
delegate?.willDismissAtPageIndex?(currentPageIndex)
animator.willDismiss(self, sender: sender)
willDismissPage?(sender != nil)
animator.willDismiss(self)
willDismissPage?()
}
}
// MARK: - Public Function For Customizing Buttons
public extension SKPhotoBrowser {
func updateCloseButton(_ image: UIImage, size: CGSize? = nil) {
func updateCloseButton(image: UIImage, size: CGSize? = nil) {
if closeButton == nil {
configureCloseButton()
}
closeButton.setImage(image, for: UIControlState())
closeButton.setImage(image, forState: .Normal)
if let size = size {
closeButton.setFrameSize(size)
}
}
func updateDeleteButton(_ image: UIImage, size: CGSize? = nil) {
func updateDeleteButton(image: UIImage, size: CGSize? = nil) {
if deleteButton == nil {
configureDeleteButton()
}
deleteButton.setImage(image, for: UIControlState())
deleteButton.setImage(image, forState: .Normal)
if let size = size {
deleteButton.setFrameSize(size)
@ -293,7 +262,7 @@ public extension SKPhotoBrowser {
// MARK: - Public Function For Browser Control
public extension SKPhotoBrowser {
func initializePageIndex(_ index: Int) {
func initializePageIndex(index: Int) {
var i = index
if index >= numberOfPhotos {
i = numberOfPhotos - 1
@ -302,7 +271,7 @@ public extension SKPhotoBrowser {
initialPageIndex = i
currentPageIndex = i
if isViewLoaded {
if isViewLoaded() {
jumpToPageAtIndex(index)
if !isViewActive {
pagingScrollView.tilePages()
@ -310,7 +279,7 @@ public extension SKPhotoBrowser {
}
}
func jumpToPageAtIndex(_ index: Int) {
func jumpToPageAtIndex(index: Int) {
if index < numberOfPhotos {
if !isEndAnimationByToolBar {
return
@ -325,7 +294,7 @@ public extension SKPhotoBrowser {
hideControlsAfterDelay()
}
func photoAtIndex(_ index: Int) -> SKPhotoProtocol {
func photoAtIndex(index: Int) -> SKPhotoProtocol {
return photos[index]
}
@ -348,14 +317,14 @@ public extension SKPhotoBrowser {
// reset
cancelControlHiding()
// start
controlVisibilityTimer = Timer.scheduledTimer(timeInterval: 4.0, target: self, selector: #selector(SKPhotoBrowser.hideControls(_:)), userInfo: nil, repeats: false)
controlVisibilityTimer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: #selector(SKPhotoBrowser.hideControls(_:)), userInfo: nil, repeats: false)
}
func hideControls() {
setControlsHidden(true, animated: true, permanent: false)
}
func hideControls(_ timer: Timer) {
func hideControls(timer: NSTimer) {
hideControls()
}
@ -367,7 +336,7 @@ public extension SKPhotoBrowser {
return toolbar.alpha == 0.0
}
func popupShare(includeCaption: Bool = true) {
func popupShare(includeCaption includeCaption: Bool = true) {
let photo = photos[currentPageIndex]
guard let underlyingImage = photo.underlyingImage else {
return
@ -376,10 +345,9 @@ public extension SKPhotoBrowser {
var activityItems: [AnyObject] = [underlyingImage]
if photo.caption != nil && includeCaption {
if let shareExtraCaption = SKPhotoBrowserOptions.shareExtraCaption {
let caption = photo.caption + shareExtraCaption
activityItems.append(caption as AnyObject)
activityItems.append(photo.caption + shareExtraCaption)
} else {
activityItems.append(photo.caption as AnyObject)
activityItems.append(photo.caption)
}
}
activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
@ -388,13 +356,13 @@ public extension SKPhotoBrowser {
self.hideControlsAfterDelay()
self.activityViewController = nil
}
if UI_USER_INTERFACE_IDIOM() == .phone {
present(activityViewController, animated: true, completion: nil)
if UI_USER_INTERFACE_IDIOM() == .Phone {
presentViewController(activityViewController, animated: true, completion: nil)
} else {
activityViewController.modalPresentationStyle = .popover
activityViewController.modalPresentationStyle = .Popover
let popover: UIPopoverPresentationController! = activityViewController.popoverPresentationController
popover.barButtonItem = toolbar.toolActionButton
present(activityViewController, animated: true, completion: nil)
presentViewController(activityViewController, animated: true, completion: nil)
}
}
}
@ -414,13 +382,13 @@ internal extension SKPhotoBrowser {
}
}
func pageDisplayedAtIndex(_ index: Int) -> SKZoomingScrollView? {
func pageDisplayedAtIndex(index: Int) -> SKZoomingScrollView? {
return pagingScrollView.pageDisplayedAtIndex(index)
}
func getImageFromView(_ sender: UIView) -> UIImage {
func getImageFromView(sender: UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(sender.frame.size, true, 0.0)
sender.layer.render(in: UIGraphicsGetCurrentContext()!)
sender.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
@ -431,16 +399,24 @@ internal extension SKPhotoBrowser {
internal extension SKPhotoBrowser {
func frameForToolbarAtOrientation() -> CGRect {
let height: CGFloat = 44
let currentOrientation = UIApplication.sharedApplication().statusBarOrientation
var height: CGFloat = navigationController?.navigationBar.frame.size.height ?? 44
if UIInterfaceOrientationIsLandscape(currentOrientation) {
height = 32
}
return CGRect(x: 0, y: view.bounds.size.height - height, width: view.bounds.size.width, height: height)
}
func frameForToolbarHideAtOrientation() -> CGRect {
let height: CGFloat = 44
let currentOrientation = UIApplication.sharedApplication().statusBarOrientation
var height: CGFloat = navigationController?.navigationBar.frame.size.height ?? 44
if UIInterfaceOrientationIsLandscape(currentOrientation) {
height = 32
}
return CGRect(x: 0, y: view.bounds.size.height + height, width: view.bounds.size.width, height: height)
}
func frameForPageAtIndex(_ index: Int) -> CGRect {
func frameForPageAtIndex(index: Int) -> CGRect {
let bounds = pagingScrollView.bounds
var pageFrame = bounds
pageFrame.size.width -= (2 * 10)
@ -452,19 +428,19 @@ internal extension SKPhotoBrowser {
// MARK: - Internal Function For Button Pressed, UIGesture Control
internal extension SKPhotoBrowser {
func panGestureRecognized(_ sender: UIPanGestureRecognizer) {
func panGestureRecognized(sender: UIPanGestureRecognizer) {
guard let zoomingScrollView: SKZoomingScrollView = pagingScrollView.pageDisplayedAtIndex(currentPageIndex) else {
return
}
backgroundView.isHidden = true
backgroundView.hidden = true
let viewHeight: CGFloat = zoomingScrollView.frame.size.height
let viewHalfHeight: CGFloat = viewHeight/2
var translatedPoint: CGPoint = sender.translation(in: self.view)
var translatedPoint: CGPoint = sender.translationInView(self.view)
// gesture began
if sender.state == .began {
if sender.state == .Began {
firstX = zoomingScrollView.center.x
firstY = zoomingScrollView.center.y
@ -480,29 +456,22 @@ internal extension SKPhotoBrowser {
? zoomingScrollView.center.y - viewHalfHeight
: -(zoomingScrollView.center.y - viewHalfHeight)) / viewHalfHeight
view.alpha = CGFloat.maximum(0.6, offset)
view.alpha = max(0.7, offset)
// gesture end
if sender.state == .ended || sender.state == .cancelled || sender.state == .failed {
if sender.state == .Ended {
if zoomingScrollView.center.y > viewHalfHeight + minOffset
|| zoomingScrollView.center.y < viewHalfHeight - minOffset {
if let sender = self.delegate?.viewForPhoto?(self, index: self.currentPageIndex) {
determineAndClose(sender: sender)
} else {
UIView.animate(withDuration: 0.3, animations: {
self.view.alpha = 0
}, completion: { _ in
self.determineAndClose(sender: nil)
})
}
backgroundView.backgroundColor = view.backgroundColor
determineAndClose()
} else {
// Continue Showing View
setNeedsStatusBarAppearanceUpdate()
let velocityY: CGFloat = CGFloat(0.35) * sender.velocity(in: self.view).y
let velocityY: CGFloat = CGFloat(0.35) * sender.velocityInView(self.view).y
let finalX: CGFloat = firstX
let finalY: CGFloat = viewHalfHeight
@ -510,8 +479,8 @@ internal extension SKPhotoBrowser {
UIView.beginAnimations(nil, context: nil)
UIView.setAnimationDuration(animationDuration)
UIView.setAnimationCurve(UIViewAnimationCurve.easeIn)
view.backgroundColor = UIColor.black
UIView.setAnimationCurve(UIViewAnimationCurve.EaseIn)
view.backgroundColor = SKPhotoBrowserOptions.backgroundColor
view.alpha = 1
zoomingScrollView.center = CGPoint(x: finalX, y: finalY)
UIView.commitAnimations()
@ -519,17 +488,17 @@ internal extension SKPhotoBrowser {
}
}
func deleteButtonPressed(_ sender: UIButton) {
func deleteButtonPressed(sender: UIButton) {
delegate?.removePhoto?(self, index: currentPageIndex) { [weak self] in
self?.deleteImage()
}
}
func closeButtonPressed(_ sender: UIButton) {
determineAndClose(sender: nil)
func closeButtonPressed(sender: UIButton) {
determineAndClose()
}
func actionButtonPressed(ignoreAndShare: Bool) {
func actionButtonPressed(ignoreAndShare ignoreAndShare: Bool) {
delegate?.willShowActionSheet?(currentPageIndex)
guard numberOfPhotos > 0 else {
@ -537,26 +506,26 @@ internal extension SKPhotoBrowser {
}
if let titles = SKPhotoBrowserOptions.actionButtonTitles {
let actionSheetController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
actionSheetController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
let actionSheetController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
actionSheetController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: { (action) -> Void in
}))
for idx in titles.indices {
actionSheetController.addAction(UIAlertAction(title: titles[idx], style: .default, handler: { (action) -> Void in
actionSheetController.addAction(UIAlertAction(title: titles[idx], style: .Default, handler: { (action) -> Void in
self.delegate?.didDismissActionSheetWithButtonIndex?(idx, photoIndex: self.currentPageIndex)
}))
}
if UI_USER_INTERFACE_IDIOM() == .phone {
present(actionSheetController, animated: true, completion: nil)
if UI_USER_INTERFACE_IDIOM() == .Phone {
presentViewController(actionSheetController, animated: true, completion: nil)
} else {
actionSheetController.modalPresentationStyle = .popover
actionSheetController.modalPresentationStyle = .Popover
if let popoverController = actionSheetController.popoverPresentationController {
popoverController.sourceView = self.view
popoverController.barButtonItem = toolbar.toolActionButton
}
present(actionSheetController, animated: true, completion: { () -> Void in
presentViewController(actionSheetController, animated: true, completion: { () -> Void in
})
}
@ -569,12 +538,12 @@ internal extension SKPhotoBrowser {
// MARK: - Private Function
private extension SKPhotoBrowser {
func configureAppearance() {
view.backgroundColor = UIColor.black
view.backgroundColor = SKPhotoBrowserOptions.backgroundColor
view.clipsToBounds = true
view.isOpaque = false
view.opaque = false
backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: SKMesurement.screenWidth, height: SKMesurement.screenHeight))
backgroundView.backgroundColor = UIColor.black
backgroundView.backgroundColor = SKPhotoBrowserOptions.backgroundColor
backgroundView.alpha = 0.0
applicationWindow.addSubview(backgroundView)
@ -583,6 +552,7 @@ private extension SKPhotoBrowser {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(SKPhotoBrowser.panGestureRecognized(_:)))
panGesture.minimumNumberOfTouches = 1
panGesture.maximumNumberOfTouches = 1
if !SKPhotoBrowserOptions.disableVerticalSwipe {
view.addGestureRecognizer(panGesture)
}
@ -590,26 +560,22 @@ private extension SKPhotoBrowser {
func configureCloseButton() {
closeButton = SKCloseButton(frame: .zero)
closeButton.addTarget(self, action: #selector(closeButtonPressed(_:)), for: .touchUpInside)
closeButton.isHidden = !SKPhotoBrowserOptions.displayCloseButton
closeButton.addTarget(self, action: #selector(closeButtonPressed(_:)), forControlEvents: .TouchUpInside)
closeButton.hidden = !SKPhotoBrowserOptions.displayCloseButton
view.addSubview(closeButton)
}
func configureDeleteButton() {
deleteButton = SKDeleteButton(frame: .zero)
deleteButton.addTarget(self, action: #selector(deleteButtonPressed(_:)), for: .touchUpInside)
deleteButton.isHidden = !SKPhotoBrowserOptions.displayDeleteButton
deleteButton.addTarget(self, action: #selector(deleteButtonPressed(_:)), forControlEvents: .TouchUpInside)
deleteButton.hidden = !SKPhotoBrowserOptions.displayDeleteButton
view.addSubview(deleteButton)
}
func configureNavigationBar() {
navigationBar = SKNavigationBar(browser: self)
navigationBar.updateFrame(view.bounds.size)
navigationBar.isHidden = !SKPhotoBrowserOptions.displayNavigationBar
navigationBar.onDoneTap = { [weak self] in
self?.willDismissPage?(false)
self?.dismissPhotoBrowser(animated: false)
}
navigationBar.hidden = !SKPhotoBrowserOptions.displayNavigationBar
view.addSubview(navigationBar)
}
@ -618,12 +584,12 @@ private extension SKPhotoBrowser {
view.addSubview(toolbar)
}
func setControlsHidden(_ hidden: Bool, animated: Bool, permanent: Bool) {
func setControlsHidden(hidden: Bool, animated: Bool, permanent: Bool) {
cancelControlHiding()
let captionViews = pagingScrollView.getCaptionViews()
UIView.animate(withDuration: 0.35,
UIView.animateWithDuration(0.35,
animations: { () -> Void in
let alpha: CGFloat = hidden ? 0.0 : 1.0
self.toolbar.alpha = alpha
@ -648,11 +614,10 @@ private extension SKPhotoBrowser {
if !permanent {
hideControlsAfterDelay()
}
setNeedsStatusBarAppearanceUpdate()
}
func deleteImage() {
private func deleteImage() {
defer {
reloadData()
}
@ -660,7 +625,7 @@ private extension SKPhotoBrowser {
if photos.count > 1 {
pagingScrollView.deleteImage()
photos.remove(at: currentPageIndex)
photos.removeAtIndex(currentPageIndex)
if currentPageIndex != 0 {
gotoPreviousPage()
}
@ -676,7 +641,7 @@ private extension SKPhotoBrowser {
// MARK: - UIScrollView Delegate
extension SKPhotoBrowser: UIScrollViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
public func scrollViewDidScroll(scrollView: UIScrollView) {
guard isViewActive else {
return
}
@ -699,14 +664,14 @@ extension SKPhotoBrowser: UIScrollViewDelegate {
}
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
hideControlsAfterDelay()
let currentIndex = pagingScrollView.contentOffset.x / pagingScrollView.frame.size.width
delegate?.didScrollToIndex?(Int(currentIndex))
}
public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
public func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
isEndAnimationByToolBar = true
}
}

View File

@ -15,28 +15,28 @@ import Foundation
- Parameter index: the index of the new photo
*/
@objc optional func didShowPhotoAtIndex(_ index: Int)
optional func didShowPhotoAtIndex(index: Int)
/**
Tells the delegate the browser will start to dismiss
- Parameter index: the index of the current photo
*/
@objc optional func willDismissAtPageIndex(_ index: Int)
optional func willDismissAtPageIndex(index: Int)
/**
Tells the delegate that the browser will start showing the `UIActionSheet`
- Parameter photoIndex: the index of the current photo
*/
@objc optional func willShowActionSheet(_ photoIndex: Int)
optional func willShowActionSheet(photoIndex: Int)
/**
Tells the delegate that the browser has been dismissed
- Parameter index: the index of the current photo
*/
@objc optional func didDismissAtPageIndex(_ index: Int)
optional func didDismissAtPageIndex(index: Int)
/**
Tells the delegate that the browser did dismiss the UIActionSheet
@ -44,14 +44,14 @@ import Foundation
- Parameter buttonIndex: the index of the pressed button
- Parameter photoIndex: the index of the current photo
*/
@objc optional func didDismissActionSheetWithButtonIndex(_ buttonIndex: Int, photoIndex: Int)
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
*/
@objc optional func didScrollToIndex(_ index: Int)
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
@ -60,7 +60,7 @@ import Foundation
- Parameter index: the index of the removed photo
- Parameter reload: function that needs to be called after finishing syncing up
*/
@objc optional func removePhoto(_ browser: SKPhotoBrowser, index: Int, reload: (() -> Void))
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.
@ -70,6 +70,7 @@ import Foundation
- Returns: the view to animate to
*/
@objc optional func viewForPhoto(_ browser: SKPhotoBrowser, index: Int) -> UIView?
optional func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView?
}

View File

@ -6,9 +6,11 @@
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
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]?
@ -18,16 +20,27 @@ public struct SKPhotoBrowserOptions {
public static var displayBackAndForwardButton: Bool = true
public static var disableVerticalSwipe: Bool = false
public static var displayCloseButton = false
public static var displayNavigationBar = true
public static var displayDeleteButton = false
public static var navigationBarCounterSepatator = "из"
public static var navigationBarDoneTitle = "Готово"
public static var displayCloseButton: Bool = true
public static var displayNavigationBar: Bool = true
public static var displayDeleteButton: Bool = false
public static var bounceAnimation = false
public static var enableZoomBlackArea = true
public static var enableSingleTapDismiss = 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 imageLoadingCustomHeaders: [String : String]?
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
}

View File

@ -9,7 +9,7 @@
import Foundation
// helpers which often used
private let bundle = Bundle(for: SKPhotoBrowser.self)
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
class SKToolbar: UIToolbar {
var toolCounterLabel: UILabel!
@ -18,7 +18,7 @@ class SKToolbar: UIToolbar {
var toolNextButton: UIBarButtonItem!
var toolActionButton: UIBarButtonItem!
fileprivate weak var browser: SKPhotoBrowser?
private weak var browser: SKPhotoBrowser?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
@ -40,7 +40,7 @@ class SKToolbar: UIToolbar {
setupToolbar()
}
func updateToolbar(_ currentPageIndex: Int) {
func updateToolbar(currentPageIndex: Int) {
guard let browser = browser else { return }
if browser.numberOfPhotos > 1 {
@ -49,28 +49,28 @@ class SKToolbar: UIToolbar {
toolCounterLabel.text = nil
}
toolPreviousButton.isEnabled = (currentPageIndex > 0)
toolNextButton.isEnabled = (currentPageIndex < browser.numberOfPhotos - 1)
toolPreviousButton.enabled = (currentPageIndex > 0)
toolNextButton.enabled = (currentPageIndex < browser.numberOfPhotos - 1)
}
}
private extension SKToolbar {
func setupApperance() {
backgroundColor = UIColor.black.withAlphaComponent(0.65)
backgroundColor = .clearColor()
clipsToBounds = true
isTranslucent = true
setBackgroundImage(UIImage(), forToolbarPosition: .any, barMetrics: .default)
translucent = true
setBackgroundImage(UIImage(), forToolbarPosition: .Any, barMetrics: .Default)
// toolbar
if !SKPhotoBrowserOptions.displayToolbar {
isHidden = true
hidden = true
}
}
func setupToolbar() {
guard let browser = browser else { return }
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
let flexSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: self, action: nil)
var items = [UIBarButtonItem]()
items.append(flexSpace)
if browser.numberOfPhotos > 1 && SKPhotoBrowserOptions.displayBackAndForwardButton {
@ -95,30 +95,30 @@ private extension SKToolbar {
func setupPreviousButton() {
let previousBtn = SKPreviousButton(frame: frame)
previousBtn.addTarget(browser, action: #selector(SKPhotoBrowser.gotoPreviousPage), for: .touchUpInside)
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), for: .touchUpInside)
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 = UIColor.clear
toolCounterLabel.font = UIFont(name: "Helvetica", size: 16.0)
toolCounterLabel.textColor = UIColor.white
toolCounterLabel.shadowColor = UIColor.black
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 = UIColor.white
toolActionButton = UIBarButtonItem(barButtonSystemItem: .Action, target: browser, action: #selector(SKPhotoBrowser.actionButtonPressed))
toolActionButton.tintColor = SKPhotoBrowserOptions.textAndIconColor
}
}
@ -126,16 +126,17 @@ private extension SKToolbar {
class SKToolbarButton: UIButton {
let insets: UIEdgeInsets = UIEdgeInsets(top: 13.25, left: 17.25, bottom: 13.25, right: 17.25)
func setup(_ imageName: String) {
backgroundColor = UIColor.clear
func setup(imageName: String) {
backgroundColor = .clearColor()
tintColor = SKPhotoBrowserOptions.textAndIconColor
imageEdgeInsets = insets
translatesAutoresizingMaskIntoConstraints = true
autoresizingMask = [.flexibleBottomMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin]
contentMode = .center
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
contentMode = .Center
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
in: bundle, compatibleWith: nil) ?? UIImage()
setImage(image, for: UIControlState())
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
setImage(image, forState: .Normal)
}
}
@ -161,4 +162,4 @@ class SKNextButton: SKToolbarButton {
super.init(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
setup(imageName)
}
}
}

View File

@ -8,7 +8,7 @@
import UIKit
open class SKZoomingScrollView: UIScrollView {
public class SKZoomingScrollView: UIScrollView {
var captionView: SKCaptionView!
var photo: SKPhotoProtocol! {
didSet {
@ -19,10 +19,10 @@ open class SKZoomingScrollView: UIScrollView {
}
}
fileprivate(set) var photoImageView: SKDetectingImageView!
fileprivate weak var photoBrowser: SKPhotoBrowser?
fileprivate var tapView: SKDetectingView!
fileprivate var indicatorView: SKIndicatorView!
private(set) var photoImageView: SKDetectingImageView!
private weak var photoBrowser: SKPhotoBrowser?
private var tapView: SKDetectingView!
private var indicatorView: SKIndicatorView!
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
@ -48,15 +48,15 @@ open class SKZoomingScrollView: UIScrollView {
// tap
tapView = SKDetectingView(frame: bounds)
tapView.delegate = self
tapView.backgroundColor = UIColor.clear
tapView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
tapView.backgroundColor = .clearColor()
tapView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
addSubview(tapView)
// image
photoImageView = SKDetectingImageView(frame: frame)
photoImageView.delegate = self
photoImageView.contentMode = .bottom
photoImageView.backgroundColor = UIColor.clear
photoImageView.contentMode = .Bottom
photoImageView.backgroundColor = .clearColor()
addSubview(photoImageView)
// indicator
@ -64,17 +64,17 @@ open class SKZoomingScrollView: UIScrollView {
addSubview(indicatorView)
// self
backgroundColor = UIColor.clear
backgroundColor = .clearColor()
delegate = self
showsHorizontalScrollIndicator = false
showsVerticalScrollIndicator = false
decelerationRate = UIScrollViewDecelerationRateFast
autoresizingMask = [.flexibleWidth, .flexibleTopMargin, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin]
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleBottomMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
}
// MARK: - override
open override func layoutSubviews() {
public override func layoutSubviews() {
tapView.frame = bounds
indicatorView.frame = bounds
@ -97,12 +97,12 @@ open class SKZoomingScrollView: UIScrollView {
}
// Center
if !photoImageView.frame.equalTo(frameToCenter) {
if !CGRectEqualToRect(photoImageView.frame, frameToCenter) {
photoImageView.frame = frameToCenter
}
}
open func setMaxMinZoomScalesForCurrentBounds() {
public func setMaxMinZoomScalesForCurrentBounds() {
maximumZoomScale = 1
minimumZoomScale = 1
zoomScale = 1
@ -114,18 +114,18 @@ open class SKZoomingScrollView: UIScrollView {
let boundsSize = bounds.size
let imageSize = photoImageView.frame.size
let xScale = CGFloat(Int(boundsSize.width / imageSize.width * 100)) / 100
let yScale = CGFloat(Int(boundsSize.height / imageSize.height * 100)) / 100
let xScale = boundsSize.width / imageSize.width
let yScale = boundsSize.height / imageSize.height
let minScale: CGFloat = min(xScale, yScale)
var maxScale: CGFloat = 1.0
let scale = max(UIScreen.main.scale, 2.0)
let deviceScreenWidth = UIScreen.main.bounds.width * scale // width in pixels. scale needs to remove if to use the old algorithm
let deviceScreenHeight = UIScreen.main.bounds.height * scale // height in pixels. scale needs to remove if to use the old algorithm
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
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.shared.statusBarOrientation.isPortrait {
if UIApplication.sharedApplication().statusBarOrientation.isPortrait {
maxScale = deviceScreenHeight / photoImageView.frame.width
} else {
maxScale = deviceScreenWidth / photoImageView.frame.width
@ -156,7 +156,7 @@ open class SKZoomingScrollView: UIScrollView {
setNeedsLayout()
}
open func prepareForReuse() {
public func prepareForReuse() {
photo = nil
if captionView != nil {
captionView.removeFromSuperview()
@ -165,7 +165,7 @@ open class SKZoomingScrollView: UIScrollView {
}
// MARK: - image
open func displayImage(complete flag: Bool) {
public func displayImage(complete flag: Bool) {
// reset scale
maximumZoomScale = 1
minimumZoomScale = 1
@ -182,10 +182,25 @@ open class SKZoomingScrollView: UIScrollView {
}
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
@ -200,14 +215,14 @@ open class SKZoomingScrollView: UIScrollView {
setNeedsLayout()
}
open func displayImageFailure() {
public func displayImageFailure() {
indicatorView.stopAnimating()
}
// MARK: - handle tap
open func handleDoubleTap(_ touchPoint: CGPoint) {
public func handleDoubleTap(touchPoint: CGPoint) {
if let photoBrowser = photoBrowser {
NSObject.cancelPreviousPerformRequests(withTarget: photoBrowser)
NSObject.cancelPreviousPerformRequestsWithTarget(photoBrowser)
}
if zoomScale > minimumZoomScale {
@ -222,7 +237,7 @@ open class SKZoomingScrollView: UIScrollView {
}
*/
let zoomRect = zoomRectForScrollViewWith(maximumZoomScale, touchPoint: touchPoint)
zoom(to: zoomRect, animated: true)
zoomToRect(zoomRect, animated: true)
}
// delay control
@ -233,15 +248,15 @@ open class SKZoomingScrollView: UIScrollView {
// MARK: - UIScrollViewDelegate
extension SKZoomingScrollView: UIScrollViewDelegate {
public func viewForZooming(in scrollView: UIScrollView) -> UIView? {
public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return photoImageView
}
public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
public func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) {
photoBrowser?.cancelControlHiding()
}
public func scrollViewDidZoom(_ scrollView: UIScrollView) {
public func scrollViewDidZoom(scrollView: UIScrollView) {
setNeedsLayout()
layoutIfNeeded()
}
@ -250,7 +265,7 @@ extension SKZoomingScrollView: UIScrollViewDelegate {
// MARK: - SKDetectingImageViewDelegate
extension SKZoomingScrollView: SKDetectingViewDelegate {
func handleSingleTap(_ view: UIView, touch: UITouch) {
func handleSingleTap(view: UIView, touch: UITouch) {
guard let browser = photoBrowser else {
return
}
@ -259,13 +274,13 @@ extension SKZoomingScrollView: SKDetectingViewDelegate {
}
if browser.areControlsHidden() == false && SKPhotoBrowserOptions.enableSingleTapDismiss == true {
browser.determineAndClose(sender: nil)
browser.determineAndClose()
} else {
browser.toggleControls()
}
}
func handleDoubleTap(_ view: UIView, touch: UITouch) {
func handleDoubleTap(view: UIView, touch: UITouch) {
if SKPhotoBrowserOptions.enableZoomBlackArea == true {
let needPoint = getViewFramePercent(view, touch: touch)
handleDoubleTap(needPoint)
@ -277,26 +292,26 @@ extension SKZoomingScrollView: SKDetectingViewDelegate {
// MARK: - SKDetectingImageViewDelegate
extension SKZoomingScrollView: SKDetectingImageViewDelegate {
func handleImageViewSingleTap(_ touchPoint: CGPoint) {
func handleImageViewSingleTap(touchPoint: CGPoint) {
guard let browser = photoBrowser else {
return
}
if SKPhotoBrowserOptions.enableSingleTapDismiss {
browser.determineAndClose(sender: nil)
browser.determineAndClose()
} else {
browser.toggleControls()
}
}
func handleImageViewDoubleTap(_ touchPoint: CGPoint) {
func handleImageViewDoubleTap(touchPoint: CGPoint) {
handleDoubleTap(touchPoint)
}
}
private extension SKZoomingScrollView {
func getViewFramePercent(_ view: UIView, touch: UITouch) -> CGPoint {
func getViewFramePercent(view: UIView, touch: UITouch) -> CGPoint {
let oneWidthViewPercent = view.bounds.width / 100
let viewTouchPoint = touch.location(in: view)
let viewTouchPoint = touch.locationInView(view)
let viewWidthTouch = viewTouchPoint.x
let viewPercentTouch = viewWidthTouch / oneWidthViewPercent
@ -315,11 +330,11 @@ private extension SKZoomingScrollView {
return allPoint
}
func zoomRectForScrollViewWith(_ scale: CGFloat, touchPoint: CGPoint) -> CGRect {
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.main.scale, 2.0))
let y = touchPoint.y - (w / max(UIScreen.main.scale, 2.0))
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)
}

View File

@ -11,7 +11,7 @@ import UIKit
extension UIImage {
func rotateImageByOrientation() -> UIImage {
// No-op if the orientation is already correct
guard self.imageOrientation != .up else {
guard self.imageOrientation != .Up else {
return self
}
@ -19,58 +19,58 @@ extension UIImage {
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
let ctx = CGContext(data: nil, width: Int(self.size.width), height: Int(self.size.height),
bitsPerComponent: self.cgImage!.bitsPerComponent, bytesPerRow: 0,
space: self.cgImage!.colorSpace!,
bitmapInfo: self.cgImage!.bitmapInfo.rawValue)
ctx!.concatenate(transform)
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)
switch self.imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx!.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
case .Left, .LeftMirrored, .Right, .RightMirrored:
CGContextDrawImage(ctx!, CGRect(x: 0, y: 0, width: size.height, height: size.width), self.CGImage!)
default:
ctx!.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
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 = ctx!.makeImage() {
return UIImage(cgImage: cgImage)
if let cgImage = CGBitmapContextCreateImage(ctx!) {
return UIImage(CGImage: cgImage)
} else {
return self
}
}
fileprivate func calculateAffineTransform() -> CGAffineTransform {
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 = CGAffineTransform.identity
var transform = CGAffineTransformIdentity
switch self.imageOrientation {
case .down, .downMirrored:
transform = transform.translatedBy(x: self.size.width, y: self.size.height)
transform = transform.rotated(by: CGFloat(M_PI))
case .Down, .DownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
case .left, .leftMirrored:
transform = transform.translatedBy(x: self.size.width, y: 0)
transform = transform.rotated(by: CGFloat(M_PI_2))
case .Left, .LeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
case .right, .rightMirrored:
transform = transform.translatedBy(x: 0, y: self.size.height)
transform = transform.rotated(by: 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 = transform.translatedBy(x: self.size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .UpMirrored, .DownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
transform = CGAffineTransformScale(transform, -1, 1)
case .leftMirrored, .rightMirrored:
transform = transform.translatedBy(x: self.size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case .LeftMirrored, .RightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0)
transform = CGAffineTransformScale(transform, -1, 1)
default:
break

View File

@ -9,13 +9,13 @@
import UIKit
extension UIView {
func addCornerRadiusAnimation(_ from: CGFloat, to: CGFloat, duration: CFTimeInterval) {
func addCornerRadiusAnimation(from: CGFloat, to: CGFloat, duration: CFTimeInterval) {
let animation = CABasicAnimation(keyPath: "cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fromValue = from
animation.toValue = to
animation.duration = duration
self.layer.add(animation, forKey: "cornerRadius")
self.layer.addAnimation(animation, forKey: "cornerRadius")
self.layer.cornerRadius = to
}
}

View File

@ -1,6 +1,5 @@
platform :ios, '9.0'
platform :ios, '8.0'
use_frameworks!
target "SKPhotoBrowserExample" do
pod 'SDWebImage', '~>3.8'
end
pod 'SDWebImage', '~>3.8'

View File

@ -9,6 +9,4 @@ DEPENDENCIES:
SPEC CHECKSUMS:
SDWebImage: 35f9627a3e44b4f292a8a8ce6a531fa488239b91
PODFILE CHECKSUM: a32274f1ce260fbc242dfd48f2b1af8a84a5bc0e
COCOAPODS: 1.1.0.beta.2
COCOAPODS: 0.39.0

View File

@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
212705891C92C69C00466223 /* FromCameraRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212705881C92C69C00466223 /* FromCameraRollViewController.swift */; };
88ABF7F872EE84010BCE3BD6 /* Pods_SKPhotoBrowserExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB686E31A24C3FFC5B05ECF8 /* Pods_SKPhotoBrowserExample.framework */; };
8909B55B1BC792150060A053 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55A1BC792150060A053 /* AppDelegate.swift */; };
8909B55D1BC792150060A053 /* FromLocalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55C1BC792150060A053 /* FromLocalViewController.swift */; };
8909B5601BC792150060A053 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8909B55E1BC792150060A053 /* Main.storyboard */; };
@ -29,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 */
@ -71,8 +71,6 @@
/* Begin PBXFileReference section */
212705881C92C69C00466223 /* FromCameraRollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromCameraRollViewController.swift; sourceTree = "<group>"; };
241888FD69B198BD34C3C0BB /* Pods-SKPhotoBrowserExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SKPhotoBrowserExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-SKPhotoBrowserExample/Pods-SKPhotoBrowserExample.release.xcconfig"; sourceTree = "<group>"; };
4646E40B4CEC62B4734AB729 /* Pods-SKPhotoBrowserExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SKPhotoBrowserExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SKPhotoBrowserExample/Pods-SKPhotoBrowserExample.debug.xcconfig"; 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>"; };
@ -95,7 +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>"; };
BB686E31A24C3FFC5B05ECF8 /* Pods_SKPhotoBrowserExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SKPhotoBrowserExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
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 */
@ -104,30 +103,21 @@
buildActionMask = 2147483647;
files = (
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */,
88ABF7F872EE84010BCE3BD6 /* Pods_SKPhotoBrowserExample.framework in Frameworks */,
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4462E0EB8EBBBA6309EEEEB9 /* Pods */ = {
isa = PBXGroup;
children = (
4646E40B4CEC62B4734AB729 /* Pods-SKPhotoBrowserExample.debug.xcconfig */,
241888FD69B198BD34C3C0BB /* Pods-SKPhotoBrowserExample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
8909B54E1BC792150060A053 = {
isa = PBXGroup;
children = (
8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */,
8909B5591BC792150060A053 /* SKPhotoBrowserExample */,
8909B5581BC792150060A053 /* Products */,
D1D8ECD45B290372235B8168 /* Pods */,
C646D2C27A5B9CCC003A9BD1 /* Frameworks */,
4462E0EB8EBBBA6309EEEEB9 /* Pods */,
);
sourceTree = "<group>";
};
@ -219,11 +209,19 @@
isa = PBXGroup;
children = (
7C2188E912BB106233AA4AB2 /* Pods.framework */,
BB686E31A24C3FFC5B05ECF8 /* Pods_SKPhotoBrowserExample.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 */
@ -231,13 +229,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 8909B5691BC792150060A053 /* Build configuration list for PBXNativeTarget "SKPhotoBrowserExample" */;
buildPhases = (
E821EE59F7DDA23D9BBBB509 /* [CP] Check Pods Manifest.lock */,
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */,
8909B5531BC792150060A053 /* Sources */,
8909B5541BC792150060A053 /* Frameworks */,
8909B5551BC792150060A053 /* Resources */,
A6A7B7841C9578E30025AC07 /* Embed Frameworks */,
B9953EC3B1AD6BDFDD63D74C /* [CP] Embed Pods Frameworks */,
554ADE9B9A609F47590589FC /* [CP] Copy Pods Resources */,
575DBD5855AB711E5773767A /* Embed Pods Frameworks */,
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */,
);
buildRules = (
);
@ -255,7 +253,7 @@
8909B54F1BC792150060A053 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0800;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = suzuki_keishi;
TargetAttributes = {
8909B5561BC792150060A053 = {
@ -331,49 +329,49 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
554ADE9B9A609F47590589FC /* [CP] Copy Pods Resources */ = {
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SKPhotoBrowserExample/Pods-SKPhotoBrowserExample-resources.sh\"\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B9953EC3B1AD6BDFDD63D74C /* [CP] Embed Pods Frameworks */ = {
575DBD5855AB711E5773767A /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SKPhotoBrowserExample/Pods-SKPhotoBrowserExample-frameworks.sh\"\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
E821EE59F7DDA23D9BBBB509 /* [CP] Check Pods Manifest.lock */ = {
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Check Pods Manifest.lock";
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 # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
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 */
@ -433,10 +431,8 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -480,10 +476,8 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -502,7 +496,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@ -510,31 +503,31 @@
};
8909B56A1BC792150060A053 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4646E40B4CEC62B4734AB729 /* Pods-SKPhotoBrowserExample.debug.xcconfig */;
baseConfigurationReference = B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = SKPhotoBrowserExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Debug;
};
8909B56B1BC792150060A053 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 241888FD69B198BD34C3C0BB /* Pods-SKPhotoBrowserExample.release.xcconfig */;
baseConfigurationReference = DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = SKPhotoBrowserExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Release;
};

View File

@ -14,30 +14,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

View File

@ -14,22 +14,22 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
@IBOutlet weak var collectionView: UICollectionView!
fileprivate let imageManager = PHCachingImageManager.default()
private let imageManager = PHCachingImageManager.defaultManager()
fileprivate var assets: [PHAsset] = []
private var assets: [PHAsset] = []
fileprivate lazy var requestOptions: PHImageRequestOptions = {
private lazy var requestOptions: PHImageRequestOptions = {
let options = PHImageRequestOptions()
options.deliveryMode = .opportunistic
options.resizeMode = .fast
options.deliveryMode = .Opportunistic
options.resizeMode = .Fast
return options
}()
fileprivate lazy var bigRequestOptions: PHImageRequestOptions = {
private lazy var bigRequestOptions: PHImageRequestOptions = {
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.resizeMode = .fast
options.deliveryMode = .HighQualityFormat
options.resizeMode = .Fast
return options
}()
@ -58,21 +58,21 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
// MARK: UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return assets.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "exampleCollectionViewCell", for: indexPath)
let asset = assets[(indexPath as NSIndexPath).row]
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath)
let asset = assets[indexPath.row]
if let cell = cell as? AssetExampleCollectionViewCell {
if let id = cell.requestId {
@ -91,16 +91,16 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? ExampleCollectionViewCell else {
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
return
}
guard let originImage = cell.exampleImageView.image else {
return
}
func open(_ images: [UIImage]) {
func open(images: [UIImage]) {
let photoImages: [SKPhotoProtocol] = images.map({ return SKPhoto.photoWithImage($0) })
let browser = SKPhotoBrowser(originImage: cell.exampleImageView.image!, photos: photoImages, animatedFromView: cell)
@ -110,17 +110,17 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
// browser.bounceAnimation = true
// browser.displayDeleteButton = true
// browser.displayAction = false
self.present(browser, animated: true, completion: {})
self.presentViewController(browser, animated: true, completion: {})
}
var fetchedImages: [UIImage] = Array<UIImage>(repeating: UIImage(), count: assets.count)
var fetchedImages: [UIImage] = Array<UIImage>(count: assets.count, repeatedValue: UIImage())
var fetched = 0
assets.forEach { (asset) -> () in
_ = requestImageForAsset(asset, options:bigRequestOptions, completion: { [weak self] (image, requestId) -> () in
requestImageForAsset(asset, options:bigRequestOptions, completion: { [weak self] (image, requestId) -> () in
if let image = image, let index = self?.assets.index(of: asset) {
if let image = image, index = self?.assets.indexOf(asset) {
fetchedImages[index] = image
}
fetched += 1
@ -132,55 +132,55 @@ class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UI
}
}
fileprivate func fetchAssets() {
private func fetchAssets() {
let options = PHFetchOptions()
let limit = 8
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.Image.rawValue)
options.fetchLimit = limit
let result = PHAsset.fetchAssets(with: options)
let result = PHAsset.fetchAssetsWithOptions(options)
let amount = min(result.count, limit)
self.assets = result.objects(at: IndexSet(integersIn: NSRange(location: 0, length: amount).toRange() ?? 0..<0))
self.assets = result.objectsAtIndexes(NSIndexSet(indexesInRange: NSRange(location: 0, length: amount))) as? [PHAsset] ?? []
}
fileprivate func requestImageForAsset(_ asset: PHAsset, options: PHImageRequestOptions, completion: @escaping (_ image: UIImage?, _ requestId: PHImageRequestID?) -> ()) -> PHImageRequestID {
private func requestImageForAsset(asset: PHAsset, options: PHImageRequestOptions, completion: (image: UIImage?, requestId: PHImageRequestID?) -> ()) -> PHImageRequestID {
let scale = UIScreen.main.scale
let scale = UIScreen.mainScreen().scale
let targetSize: CGSize
if options.deliveryMode == .highQualityFormat {
if options.deliveryMode == .HighQualityFormat {
targetSize = CGSize(width: 600 * scale, height: 600 * scale)
} else {
targetSize = CGSize(width: 182 * scale, height: 182 * scale)
}
requestOptions.isSynchronous = false
requestOptions.synchronous = false
// Workaround because PHImageManager.requestImageForAsset doesn't work for burst images
if asset.representsBurst {
return imageManager.requestImageData(for: asset, options: options) { data, _, _, dict in
return imageManager.requestImageDataForAsset(asset, options: options) { data, _, _, dict in
let image = data.flatMap { UIImage(data: $0) }
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
completion(image, requestId?.int32Value)
completion(image: image, requestId: requestId?.intValue)
}
} else {
return imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, dict in
return imageManager.requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options) { image, dict in
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
completion(image, requestId?.int32Value)
completion(image: image, requestId: requestId?.intValue)
}
}
}
override var prefersStatusBarHidden: Bool {
override func prefersStatusBarHidden() -> Bool {
return false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}

View File

@ -25,28 +25,28 @@ class FromLocalViewController: UIViewController, UICollectionViewDataSource, UIC
super.didReceiveMemoryWarning()
}
override var prefersStatusBarHidden: Bool {
override func prefersStatusBarHidden() -> Bool {
return false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
// MARK: - UICollectionViewDataSource
extension FromLocalViewController {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
@objc(collectionView:cellForItemAtIndexPath:) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "exampleCollectionViewCell", for: indexPath) as? ExampleCollectionViewCell else {
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath) as? ExampleCollectionViewCell else {
return UICollectionViewCell()
}
cell.exampleImageView.image = UIImage(named: "image\((indexPath as NSIndexPath).row % 10).jpg")
cell.exampleImageView.image = UIImage(named: "image\(indexPath.row % 10).jpg")
// cell.exampleImageView.contentMode = .ScaleAspectFill
return cell
}
@ -55,8 +55,8 @@ extension FromLocalViewController {
// MARK: - UICollectionViewDelegate
extension FromLocalViewController {
@objc(collectionView:didSelectItemAtIndexPath:) func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let cell = collectionView.cellForItem(at: indexPath) as? ExampleCollectionViewCell else {
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
return
}
guard let originImage = cell.exampleImageView.image else {
@ -70,14 +70,14 @@ extension FromLocalViewController {
browser.delegate = self
// browser.updateCloseButton(UIImage(named: "image1.jpg")!)
present(browser, animated: true, completion: {})
presentViewController(browser, animated: true, completion: {})
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
if UIDevice.current.userInterfaceIdiom == .pad {
return CGSize(width: UIScreen.main.bounds.size.width / 2 - 5, height: 300)
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 300)
} else {
return CGSize(width: UIScreen.main.bounds.size.width / 2 - 5, height: 200)
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 200)
}
}
}
@ -86,45 +86,45 @@ extension FromLocalViewController {
// MARK: - SKPhotoBrowserDelegate
extension FromLocalViewController {
func didShowPhotoAtIndex(_ index: Int) {
collectionView.visibleCells.forEach({$0.isHidden = false})
collectionView.cellForItem(at: IndexPath(item: index, section: 0))?.isHidden = true
func didShowPhotoAtIndex(index: Int) {
collectionView.visibleCells().forEach({$0.hidden = false})
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
}
func willDismissAtPageIndex(_ index: Int) {
collectionView.visibleCells.forEach({$0.isHidden = false})
collectionView.cellForItem(at: IndexPath(item: index, section: 0))?.isHidden = true
func willDismissAtPageIndex(index: Int) {
collectionView.visibleCells().forEach({$0.hidden = false})
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
}
func willShowActionSheet(_ photoIndex: Int) {
func willShowActionSheet(photoIndex: Int) {
// do some handle if you need
}
func didDismissAtPageIndex(_ index: Int) {
collectionView.cellForItem(at: IndexPath(item: index, section: 0))?.isHidden = false
func didDismissAtPageIndex(index: Int) {
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = false
}
func didDismissActionSheetWithButtonIndex(_ buttonIndex: Int, photoIndex: Int) {
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
// handle dismissing custom actions
}
func removePhoto(_ browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
reload()
}
func viewForPhoto(_ browser: SKPhotoBrowser, index: Int) -> UIView? {
return collectionView.cellForItem(at: IndexPath(item: index, section: 0))
func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView? {
return collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))
}
}
// MARK: - private
private extension FromLocalViewController {
func setupTestData() {
private func setupTestData() {
images = createLocalPhotos()
}
func setupCollectionView() {
private func setupCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
}
@ -166,4 +166,4 @@ var caption = ["Lorem Ipsum is simply dummy text of the printing and typesetting
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
]
]

View File

@ -18,31 +18,31 @@ class FromWebViewController: UIViewController, SKPhotoBrowserDelegate {
super.viewDidLoad()
SKCache.sharedCache.imageCache = CustomImageCache()
imageView.sd_setImage(with: URL(string: "https://placehold.jp/1500x1500.png")) {
guard let url = $0.3?.absoluteString else { return }
SKCache.sharedCache.setImage($0.0!, forKey: url)
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) {
@IBAction func pushButton(sender: AnyObject) {
let browser = SKPhotoBrowser(photos: createWebPhotos())
browser.initializePageIndex(0)
browser.delegate = self
present(browser, animated: true, completion: nil)
presentViewController(browser, animated: true, completion: nil)
}
}
// MARK: - SKPhotoBrowserDelegate
extension FromWebViewController {
func didDismissAtPageIndex(_ index: Int) {
func didDismissAtPageIndex(index: Int) {
}
func didDismissActionSheetWithButtonIndex(_ buttonIndex: Int, photoIndex: Int) {
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
}
func removePhoto(_ browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
SKCache.sharedCache.removeImageForKey("somekey")
reload()
}
@ -66,19 +66,19 @@ class CustomImageCache: SKImageCacheable {
init() {
let cache = SDImageCache(namespace: "com.suzuki.custom.cache")
self.cache = cache!
self.cache = cache
}
func imageForKey(_ key: String) -> UIImage? {
guard let image = cache.imageFromDiskCache(forKey: key) else { return nil }
func imageForKey(key: String) -> UIImage? {
guard let image = cache.imageFromDiskCacheForKey(key) else { return nil }
return image
}
func setImage(_ image: UIImage, forKey key: String) {
cache.store(image, forKey: key)
func setImage(image: UIImage, forKey key: String) {
cache.storeImage(image, forKey: key)
}
func removeImageForKey(_ key: String) {
func removeImageForKey(key: String) {
}
}

View File

@ -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 users photo library</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
@ -51,9 +53,5 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>Used to get photos</string>
<key>NSCameraUsageDescription</key>
<string>Used to take photos</string>
</dict>
</plist>