Compare commits

...

274 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
suzuki_keishi de1f040c91 update example for xcode8.0 2016-09-09 18:30:38 +09:00
suzuki_keishi c9e3749b0b update podspec for 3.1.0 2016-09-09 15:42:49 +09:00
suzuki_keishi e55631cdec Merge branch 'master' of github.com:suzuki-0000/SKPhotoBrowser 2016-09-09 15:42:06 +09:00
suzuki_keishi d1b266c5b3 update changelog. 2016-09-09 15:41:58 +09:00
keishi suzuki 2e5706c601 Merge pull request #141 from barrault01/master
[Urgent] Update for Xcode GM
2016-09-09 15:41:46 +09:00
suzuki_keishi 06cd9c082d Merge branch 'master' of github.com:suzuki-0000/SKPhotoBrowser 2016-09-09 15:39:01 +09:00
keishi suzuki a3b47e543e Merge pull request #140 from fumitoito/fixSwiftlintWarnings
fix swiftlint warnings
2016-09-09 15:38:29 +09:00
suzuki_keishi 4c068d83be Issue with multiple actionButtonTitles. 2016-09-09 15:38:12 +09:00
Antoine Barrault 6ae7ad024e update for Xcode GM 2016-09-08 10:48:01 -03:00
fumitoito 5017c33a78 fix swiftlint errors
- use swift constructor
- use struct extension properties
- do NOT repeat `let` statement in conditional binding
- separate methods, to avoid Cyclomatic Complexity warning
2016-09-06 17:48:05 +09:00
keishi suzuki 08aeb6a147 goes 3.0.2 2016-09-06 17:10:04 +09:00
keishi suzuki a53fad69a4 Merge pull request #139 from suzuki-0000/feature/v3.0.2
Feature/v3.0.2
2016-09-06 17:09:43 +09:00
suzuki_keishi 95755537c3 update for #133. 2016-09-06 17:06:50 +09:00
suzuki_keishi 2dce338349 Merge branch 'master' of github.com:suzuki-0000/SKPhotoBrowser into feature/#137 2016-09-06 17:01:49 +09:00
suzuki_keishi 850d584b57 bug fix for Impossible to zoom when resolution is 1024x768 #134. 2016-09-06 17:01:41 +09:00
keishi suzuki 8fcdfcf535 update changelog 2016-09-06 10:57:42 +09:00
keishi suzuki d5b42c8b47 Merge pull request #138 from rp4rk/feature/resize-buttons
Resizing for Buttons
2016-09-06 10:48:24 +09:00
Ryan Park 3d46160ef1 Added size parameter to updateDeleteButton/updateCloseButton methods. Added updateFrameSize method to SKButton to allow for button size changes. 2016-09-05 12:20:01 +01:00
suzuki_keishi 9befa4cc10 Skip loading image if already loaded #135. 2016-09-05 17:34:03 +09:00
keishi suzuki bde450fcff Merge pull request #135 from madsb/master
Skip loading image if already loaded
2016-09-05 17:32:06 +09:00
Mads Bjerre b93e85dad0 Skip loading image if already loaded 2016-08-31 14:29:58 +02:00
keishi suzuki a091e2e94e update 2016-08-30 17:34:30 +09:00
suzuki_keishi a9561d61d7 update read me v3.0.x 2016-08-30 17:12:16 +09:00
keishi suzuki f633789c65 update for 3.0.0 2016-08-30 17:09:48 +09:00
keishi suzuki dca1f9dd4b remove template 2016-08-30 17:03:05 +09:00
keishi suzuki 679a5683f7 Merge pull request #127 from suzuki-0000/feature/clean
Refactoring for swift3.0, clean code for massive view controller .  #125
2016-08-30 16:53:01 +09:00
suzuki_keishi c113f68b63 update podspec. for #125. 2016-08-30 16:41:06 +09:00
suzuki_keishi d25f78954d update changelog.md 2016-08-30 16:36:11 +09:00
suzuki_keishi fa6b95eebe merge latest master. 2016-08-30 16:22:35 +09:00
suzuki_keishi b4d911bee3 update podspec for swift2.3 2016-08-30 16:04:07 +09:00
keishi suzuki bf45bca847 Merge pull request #130 from gustavogervasio/swift2.3
swift 2.3
2016-08-30 16:03:33 +09:00
suzuki_keishi d6ecf61405 update example, readme. 2016-08-30 15:51:55 +09:00
suzuki_keishi 934831b4bf Improve compile time. 2016-08-30 14:33:54 +09:00
Gustavo Gervasio 4a76fa9b30 add support to swift 2.3 2016-08-29 14:39:39 -03:00
suzuki_keishi 0d0aa326da compile-time-optimization. 2016-08-25 12:53:44 +09:00
keishi suzuki a0a8e9119e Merge pull request #128 from orkoden/feature/compile-time-optimization
Improve compile time significantly by adding types
2016-08-25 12:49:09 +09:00
Jörg Bühmann 31e3378c9a Improve compile time significantly by adding types 2016-08-24 12:18:50 +02:00
suzuki_keishi 9b77a81734 clean code. 2016-08-23 15:12:46 +09:00
suzuki_keishi 50fe55684f clean browser. 2016-08-23 14:54:09 +09:00
suzuki_keishi 2362808151 clean browser. 2016-08-23 14:35:58 +09:00
suzuki_keishi 6d6a93a1ae clean, refactor, update readme. 2016-08-23 14:02:01 +09:00
suzuki_keishi cb53afd3a2 clean, and issue #108. 2016-08-19 17:24:32 +09:00
suzuki_keishi 733b97cba5 clean. 2016-08-18 17:29:13 +09:00
suzuki_keishi 0dd21f9ef7 clean. 2016-08-18 16:53:05 +09:00
suzuki_keishi 90ec128c11 clean, refactor. 2016-08-18 16:26:00 +09:00
suzuki_keishi a066d956ff [add]change log. 2016-08-18 11:06:39 +09:00
suzuki_keishi d2e350c1d3 [refactor]update for swift3.0, new function. 2016-08-12 20:27:30 +09:00
suzuki_keishi 96fed7a499 [update]refactor concreate buttons. 2016-08-12 13:46:27 +09:00
suzuki_keishi 823fe89a1c [upate]update custom button. 2016-08-10 11:50:22 +09:00
suzuki_keishi cdef28539e clean, refactor buttons code. 2016-08-10 11:24:30 +09:00
suzuki_keishi 8d2d24ff4f [WIP]clean, refactor at animation. 2016-08-09 21:39:35 +09:00
suzuki_keishi 4b897dcae0 [update]refactor for new impl. 2016-08-09 18:55:18 +09:00
keishi suzuki 6be29d1413 Merge pull request #121 from suzuki-0000/feature/clean
clean, and update example.
2016-08-05 19:59:59 +09:00
suzuki_keishi 99ed57e73e [update]example for latest impl. 2016-08-05 19:56:57 +09:00
suzuki_keishi 711b492f0b [update]refactor code, example. 2016-08-05 19:16:48 +09:00
suzuki_keishi f0b3224892 [update]fadeview clean. 2016-08-05 18:51:42 +09:00
suzuki_keishi d931f4e4ec [update]clean caption View. 2016-08-05 11:50:03 +09:00
suzuki_keishi 770e711e4c [update]clean detecting view. 2016-08-05 11:41:01 +09:00
suzuki_keishi a2386af3cf update pod spec. 2016-08-05 10:59:14 +09:00
keishi suzuki f9d117d5cf Merge pull request #118 from orazz/master
If image downloaded then not show activityindicator
2016-08-05 10:53:22 +09:00
orazz 862f8a528c If image downloaded then not show activityindicator 2016-07-30 15:34:51 +05:00
suzuki_keishi e78d452f33 [update]#117. 2016-07-27 14:19:54 +09:00
keishi suzuki b2c0c81018 Merge pull request #117 from VahanMargaryan/master
fixed issue when animatedFromView not has a superview but has superlayer
2016-07-27 14:19:12 +09:00
Vahan 5a0cb8034a fixed issue when animatedFromView not has a superview but has superlayer 2016-07-25 12:05:58 +04:00
suzuki_keishi 28775b2672 podspec -> 2.0.0. 2016-06-15 10:32:12 +09:00
keishi suzuki a5cc088191 Merge pull request #112 from kevinwo/master
Implement optional custom cache
2016-06-15 10:31:12 +09:00
Kevin Wolkober 698558391e Rename SKCachable file to SKCacheable 2016-06-13 19:15:39 +08:00
Kevin Wolkober 0d9cd03bd5 Make cached response data return optional 2016-06-13 18:54:58 +08:00
Kevin Wolkober 3e6e64b1ea Implement custom request/response cache get/set 2016-06-13 18:50:13 +08:00
Kevin Wolkober eb8107dc89 Add cache get/set with URL request/response 2016-06-13 18:41:04 +08:00
Kevin Wolkober 2fcb2282e5 Rename and make SKCacheable public 2016-06-13 18:17:34 +08:00
Kevin Wolkober 2aacfdf5fb Migrate UIImage cache category to new SKCache
- Create cache protocol for allowing custom caches to be set
2016-06-13 18:11:11 +08:00
suzuki_keishi e1db66eedd [update]update podspec. 2016-06-06 11:42:33 +09:00
keishi suzuki b1db89acbf Merge pull request #109 from PhilippeRiegert/add_scroll_delegate
add a delegate to notify when the user scroll to an index
2016-06-06 11:41:57 +09:00
Philippe Riegert 3e2a115127 add the new delegate in the doc 2016-05-28 00:15:51 -04:00
Philippe Riegert 71ff3d48b7 add missing delegate in the doc 2016-05-28 00:15:31 -04:00
Philippe Riegert 933d7726a4 add a delegate to notify when the user scroll to an index 2016-05-27 21:45:46 -04:00
suzuki_keishi e1fba6eae5 [update]podspec. 2016-05-25 16:07:10 +09:00
keishi suzuki 73d07e1c9d Merge pull request #106 from cometas/master
activity indicator bugfixes
2016-05-25 16:06:10 +09:00
suzuki_keishi fa6231d036 [update]podspec. 2016-05-25 16:03:24 +09:00
Andreas Siegrist 9c353f4907 fixed a bug where the activity indicator was only visible in the first page of multiple images 2016-05-23 23:28:03 +02:00
Rummler b2340f2547 Merge pull request #102 from krummler/master
Fixed unit test and problems running when being bridged to objective-c
2016-05-18 17:10:55 +02:00
Kevin Rummler d4035ffdf4 Fixed unit test and problems running when being bridged to objective-c 2016-05-14 20:05:05 +02:00
suzuki_keishi 0f4b1ba546 [update]podspec. 2016-05-13 19:47:06 +09:00
keishi suzuki 464882c72e Merge pull request #99 from ghysrc/master
Feature of single tap to dismiss
2016-05-13 19:24:17 +09:00
ghysrc ba03773f3e change to determineAndClose 2016-05-13 16:07:53 +08:00
ghysrc 6b64f75c8d add single tap to dismiss 2016-05-13 15:18:26 +08:00
ghysrc f162983b70 add single tap to dismiss 2016-05-13 15:10:44 +08:00
suzuki_keishi d4df387efe [update]podspec. 2016-05-13 15:43:29 +09:00
suzuki_keishi adfceff42f #94. and minor refactor. 2016-05-13 15:42:44 +09:00
suzuki_keishi d5d95e0a47 [update]some minor refactor. 2016-05-13 15:26:14 +09:00
suzuki_keishi 8455fb1d3c [update]some minor refactor. 2016-05-13 15:15:15 +09:00
Alexsander Khitev 3cff5fbfca Merge pull request #95 from alexsanderkhitev/master
update podspec
2016-04-29 00:26:34 +04:00
Alexsander Khitev cbc00517af update podspec 2016-04-29 00:25:51 +04:00
Alexsander Khitev 248365e3b2 Merge pull request #93 from barrault01/patch-2
Fix Readme
2016-04-29 00:20:19 +04:00
ant_one 852d331198 Fix Readme 2016-04-25 11:35:50 -03:00
Alexsander Khitev 4e2b64b893 Merge pull request #91 from alexsanderkhitev/master
Update podspec
2016-04-22 20:35:30 +04:00
Alexsander Khitev 3a2ec6ae34 Update podspec 2016-04-22 20:34:58 +04:00
Alexsander Khitev 5f5e0b7950 Merge pull request #90 from barrault01/fixe-issue-89
Fixe issue 89
2016-04-22 20:33:38 +04:00
Antoine Barrault 5d635c77d7 fix issue #89 2016-04-22 09:41:10 -03:00
Antoine Barrault d5b59756ef add test to reproduce issue 2016-04-22 09:04:51 -03:00
Alexsander Khitev 035fc449b1 Merge pull request #87 from hebertialmeida/master
Using SKPhotoProtocol to enable usage from SKLocalPhoto and SKPhoto
2016-04-19 21:12:21 +04:00
Heberti Almeida 8faf42b9ed Using SKPhotoProtocol to enable usage from SKLocalPhoto and SKPhoto 2016-04-19 12:08:06 -03:00
Alexsander Khitev ca7e215999 Merge pull request #86 from hebertialmeida/master
Fix crash on initialisation
2016-04-18 23:52:36 +04:00
Heberti Almeida 471a2bf340 Fix crash on initialisation 2016-04-18 16:31:59 -03:00
Alexsander Khitev 84891b9e06 Merge pull request #85 from alexsanderkhitev/master
update podspec
2016-04-18 17:32:02 +04:00
Alexsander Khitev 55a9ab6947 update podspec 2016-04-18 17:31:20 +04:00
Alexsander Khitev b4c090c174 Merge pull request #83 from slav123/master
fixes crash on iPad
2016-04-18 17:29:12 +04:00
Slawomir Jasinski d86f610a1a fixes crash on iPad 2016-04-18 13:09:08 +02:00
Rummler dc5cf306e1 Merge pull request #82 from barrault01/master
Add SKLocalPhoto protocol to use SKPhotoBrower with local files
2016-04-15 09:28:47 +02:00
Antoine Barrault 3204bbb5cb change convenience init for SKPhotoBrowser to only take object that conform to SKPhotoProtocol 2016-04-13 10:15:03 -03:00
Antoine Barrault c4d04f9ff6 add local files example to READMe 2016-04-13 09:56:53 -03:00
Antoine Barrault 1a05d44f2d add SKLocalPhoto to support local photo from file 2016-04-13 09:51:23 -03:00
Alexsander Khitev 9d925c80cc Merge pull request #80 from alexsanderkhitev/master
the new algorithm for zoom which gives the same result for double touch or pinch
2016-04-05 18:55:31 +04:00
Alexsander Khitev 3a399a85d7 update podspec 2016-04-05 18:44:58 +04:00
Alexsander Khitev 9777879583 here was a mistake, when we opened the browser, and then turned the device activity light remained in the same place that looked incorrectly https://www.dropbox.com/s/mc55lvapyqaetpo/Simulator%20Screen%20Shot%20Apr%205%2C%202016%2C%202.29.37%20PM.png?dl=0 2016-04-05 18:41:13 +04:00
Alexsander Khitev 1af051b8a2 I think that the result should be the same after double touch or pinch, it was different 2016-04-05 18:14:12 +04:00
Alexsander Khitev 46554ec6c0 some changes 2016-04-05 16:07:36 +04:00
Alexsander Khitev 12f5bd9f23 to comment the old algorithm 2016-04-05 14:50:19 +04:00
Alexsander Khitev 8e31335272 stability for new algorithm 2016-04-05 14:26:14 +04:00
Alexsander Khitev f317f60119 new algorithm 2016-04-05 13:25:24 +04:00
Alexsander Khitev e5b2be2b44 Merge pull request #79 from alexsanderkhitev/master
the second try to fix the #68 bug
2016-04-04 12:45:43 +04:00
Alexsander Khitev 98ec4dfbe2 update podspec 2016-04-04 12:44:45 +04:00
Alexsander Khitev 48191878ad the second try to fix the #68 bug 2016-04-04 12:43:22 +04:00
Alexsander Khitev 494ed88d26 Merge pull request #78 from alexsanderkhitev/master
added unit tests
2016-04-02 22:20:22 +04:00
Alexsander Khitev ecd87ca1f5 added unit tests 2016-04-02 22:11:14 +04:00
Alexsander Khitev 699e11922a Merge pull request #77 from alexsanderkhitev/master
some changes
2016-04-02 13:29:09 +04:00
Alexsander Khitev c025d3ba17 update podspec 2016-04-02 13:25:55 +04:00
Alexsander Khitev 522fd3317b some changes 2016-04-02 13:25:21 +04:00
Alexsander Khitev d96d70c791 I made some changes 2016-04-02 13:10:55 +04:00
Alexsander Khitev a6faf17120 some changes 2016-04-02 12:29:09 +04:00
Rummler c8b5d86364 Merge pull request #71 from zxming/master
the new version for reuse underlyingImage as holderImage
2016-04-02 07:19:24 +02:00
Rummler 04cb08fe04 Merge pull request #76 from krummler/master
Fixes crashing if image has not been loaded yet
2016-04-02 07:11:38 +02:00
Alexsander Khitev 5b511a3cb5 Merge pull request #74 from alexsanderkhitev/master
update podspec
2016-04-01 23:43:20 +04:00
Alexsander Khitev 0d17355a65 update podspec 2016-04-01 23:42:40 +04:00
Alexsander Khitev 01fa381365 Merge pull request #73 from alexsanderkhitev/master
it fixed the #68 bug
2016-04-01 23:41:58 +04:00
Alexsander Khitev e48381d860 some changes. It must have to fix the #68 bug 2016-04-01 23:37:05 +04:00
Alexsander Khitev 951b159698 some changes 2016-04-01 22:21:13 +04:00
Rummler b1e2b73df4 Merge pull request #72 from komitake/issue-didDismissActionSheetWithButtonIndex
Fixed delegate didDismissActionSheetWithButtonIndex was not called
2016-04-01 17:08:39 +02:00
胥冥 286251f8a5 fixed bug: the indicator may not disappear when loading local image
修复加载本地图片指示器可能不消失的 bug
原因:原 GCD 异步获取对应 page 的操作在全局队列中,仍会及时执行比对 photo 的方法从而导致可能获取失败;
方案:将对比获取方法放在主队列中,让其在主体代码执行完成后才异步执行即可
2016-04-01 10:21:06 +08:00
komitake 9fe3bbd808 Fixed delegate didDismissActionSheetWithButtonIndex was not called 2016-04-01 03:10:19 +09:00
胥冥 09090280ea 删除中文注释
发现此框架对部分图片有不能缓存的问题
2016-03-31 23:57:59 +08:00
胥冥 a6e56b7374 修复本地图片加载无法隐藏指示器的bug
替换几张图片和加入相应的图片url用来测试网络加载
2016-03-31 23:33:18 +08:00
胥冥 7efe309e88 合并了部分suzuki的文件 2016-03-31 22:37:24 +08:00
胥冥 d0fdf685c5 Merge branch 'pr/1' 2016-03-31 22:23:59 +08:00
胥冥 6cf157a9c7 使用协议中原有的 underingImage 作为在网络加载时的占位图
原holderImage因对协议进行了更改,处理的不是很好,试着通过原有属性来改造(现存问题:加载本地图片时指示器无法消除)
2016-03-31 22:16:02 +08:00
Kevin Rummler da8af38a64 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser 2016-03-31 10:48:57 +02:00
Alexsander Khitev 74dee840f0 Merge pull request #67 from alexsanderkhitev/master
update podspec
2016-03-30 12:10:11 +04:00
Alexsander Khitev 5126ce0b2e update podspec 2016-03-30 12:09:15 +04:00
Alexsander Khitev bf92b503a6 Merge pull request #66 from matbeich/master
Fixed error while dismissing empty image
2016-03-30 12:08:07 +04:00
Bogdan Matveev 0d1a884b9b fixed error while dismissing empty image 2016-03-29 00:33:06 +03:00
Kevin Rummler 51bb8e4b5c prevent crash when closing before image has been loaded 2016-03-25 12:41:41 +01:00
Alexsander Khitev d6bb2bbb37 Merge pull request #65 from alexsanderkhitev/master
Some changes
2016-03-24 21:15:45 +04:00
Alexsander Khitev 0cc108549f update podspec 2016-03-24 21:14:53 +04:00
Alexsander Khitev d2254ef0c1 some changes 2016-03-24 21:14:21 +04:00
Alexsander Khitev 975c81307e Merge pull request #63 from alexsandersky/master
fixed bug with captionView when we are deleting photo
2016-03-24 00:40:01 +04:00
Alexsander Khitev 9f84f1e8d1 update podspec 2016-03-24 00:38:44 +04:00
Alexsander Khitev b68ec817d3 bug fix with caption view 2016-03-24 00:32:46 +04:00
Alexsander Khitev 6be05c875d Merge pull request #62 from alexsandersky/master
update for new swift style
2016-03-23 22:49:15 +04:00
Alexsander Khitev 3da73fd698 update podspec 2016-03-23 22:48:14 +04:00
Alexsander Khitev 3cd3b00d30 update for new swift style 2016-03-23 22:45:40 +04:00
Alexsander Khitev 897c5f5cff Merge pull request #61 from alexsandersky/master
some changes
2016-03-23 17:45:02 +04:00
Alexsander Khitev 51b82a500c some changes 2016-03-23 17:41:58 +04:00
Alexsander Khitev 75ceae5342 Merge pull request #60 from alexsandersky/master
update for new swift
2016-03-23 17:41:02 +04:00
Alexsander Khitev 4c8e04e902 update for new swift 2016-03-23 17:39:19 +04:00
胥冥 31914903c6 1. add holderImage 2.bug fix
1. 增加加载网络图片情况下的占位图参数
2. 修复加载过程中退出崩溃的BUG
3. 修复退出后有短暂无响应时间的BUG
2016-03-23 16:50:06 +08:00
suzuki_keishi 7ef4f5b381 [update]podspec. 2016-03-18 10:57:39 +09:00
keishi suzuki 18d8aec3ce Merge pull request #57 from suzuki-0000/bugfix/st
[bugfix]#43 SKPhotoBrowser can't be destroied fixed.
2016-03-18 10:57:12 +09:00
suzuki_keishi 3bacde025b [bugfix]#43 SKPhotoBrowser can't be destroied fixed. 2016-03-18 10:55:55 +09:00
suzuki_keishi 5b7b2f7f01 [update]minor update for example. readme. 2016-03-17 19:43:52 +09:00
Rummler cc91489501 Merge pull request #53 from krummler/cameraroll
Images from camera roll
2016-03-17 08:41:57 +01:00
Kevin Rummler 23677c500a Merge branch 'master' of github.com:krummler/SKPhotoBrowser into cameraroll
# Conflicts:
#	SKPhotoBrowser/SKPhotoBrowser.swift
2016-03-16 17:28:38 +01:00
Rummler 36a136b190 Merge pull request #56 from krummler/master
Small fix in demo App, updated gif example with new features and fixes
2016-03-16 17:25:11 +01:00
Kevin Rummler 28f41e39e0 Small fix in demo App, updated gif example with new features and fixes 2016-03-16 17:22:37 +01:00
Rummler e3a5267edf Merge pull request #55 from krummler/background-effects
Added background effects
2016-03-16 16:57:29 +01:00
Kevin Rummler 5a8895151b higher limit to soften the effect 2016-03-16 16:56:14 +01:00
Kevin Rummler a93f945fcc Added visibility-effect when dragging down to show view behind 2016-03-16 16:47:24 +01:00
Kevin Rummler b0c49daf37 Merge branch 'master' of github.com:krummler/SKPhotoBrowser into cameraroll 2016-03-16 15:52:25 +01:00
Kevin Rummler f443df3ddc Merge branch 'master' of github.com:krummler/SKPhotoBrowser into background-effects 2016-03-16 15:52:14 +01:00
Rummler 2f64ce274c Merge pull request #54 from krummler/master
Added first documentation to the Delegate
2016-03-16 15:51:52 +01:00
Kevin Rummler 53dc8beca2 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser 2016-03-16 15:50:56 +01:00
Kevin Rummler 8ee5d26f52 Added docs for the SKPhotoBrowserDelegate 2016-03-16 15:50:16 +01:00
Kevin Rummler 9abcde463b Fix for re-showing the statusBar 2016-03-16 09:58:44 +01:00
Kevin Rummler 56e94e2682 moved background to reusable view 2016-03-16 09:27:54 +01:00
Rummler 6e3944ec3b Merge pull request #51 from krummler/master 2016-03-15 18:32:33 +01:00
Kevin Rummler f6ad0193bb Merge branch 'master' of github.com:krummler/SKPhotoBrowser into cameraroll
# Conflicts:
#	SKPhotoBrowser/SKPhotoBrowser.swift
2016-03-15 18:01:42 +01:00
Kevin Rummler d376c18e9e Fix for opening images with certain aspect ratios 2016-03-15 18:00:36 +01:00
Kevin Rummler 35d30f2539 Split extensions into own files, added rotation check for images with rotations (like portait images from the camera) 2016-03-15 17:20:07 +01:00
suzuki_keishi cc0210d22b [update]pod spec. 2016-03-15 11:55:48 +09:00
keishi suzuki 8d430abce4 Merge pull request #50 from krummler/master
Enable ability to override statusBar style
2016-03-15 11:55:15 +09:00
Kevin Rummler fb034384b3 For for topOffset which I forgot, remove views when clearing cache, replace underlying image when closing 2016-03-14 18:02:59 +01:00
Kevin Rummler 2926e31d7f Enable ability to override statusBar style, fixed but where dragging shows statusbar, even if it shouldnt 2016-03-14 17:20:08 +01:00
Kevin Rummler d46275e705 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser into cameraroll
# Conflicts:
#	SKPhotoBrowserExample/SKPhotoBrowserExample.xcodeproj/project.pbxproj
2016-03-14 13:32:57 +01:00
Alexsandersky 1d0356bdd5 Merge pull request #49 from alexsanderskywork/master
this new algorithm resizes the current image size after device rotation
2016-03-13 20:05:03 +04:00
Alexsander Khitev d329c86aee this new algorithm resizes the current image after device rotation 2016-03-13 19:58:37 +04:00
Alexsander Khitev cc1aa7d3d4 new algorithm for resize image 2016-03-13 19:52:24 +04:00
Alexsander Khitev 689b312e10 I fixed the bug which was after the device rotation and it showed a wrong image size 2016-03-13 18:56:49 +04:00
Alexsandersky 91ebfcb186 Merge pull request #48 from alexsanderskywork/master
fixed the bug which was when slide between images
2016-03-13 18:33:57 +04:00
Alexsander Khitev c85abfe802 removed old functions 2016-03-13 18:32:27 +04:00
Alexsander Khitev 2252b14095 I removed prints 2016-03-13 18:15:52 +04:00
Alexsander Khitev 5bf7142819 I fixed the bug which was when slide between images 2016-03-13 18:14:37 +04:00
Alexsander Khitev 8bdb4270f2 it works with first image 2016-03-13 17:56:07 +04:00
Alexsander Khitev 82aced57c3 I removed no longer needed the value and the function 2016-03-13 16:45:14 +04:00
Alexsander Khitev 22fbd1a00d force tile pages when view isn't active and remove todo of prepareForReuse 2016-03-13 16:00:36 +04:00
Alexsander Khitev 2bc9c33c60 I changed var photo in SKZoomingScrollView and we now can to use prepareForReuse and we don't get the optional value error 2016-03-13 14:53:12 +04:00
Alexsander Khitev 79f672711b Changed prepareForReuse function. I added SKPhotoBrowser.framework in the SKPhotoBrowserExample in the Embed frameworks that we can to test on a real device. If I don't add it in the Embed Frameworks I get the "Reason Image" error. 2016-03-13 14:34:53 +04:00
Alexsander Khitev a9c89dd09e Added recycledPages 2016-03-13 14:28:01 +04:00
Alexsandersky 5d64af677e Merge pull request #47 from alexsanderskywork/master
Some changes.
2016-03-12 23:53:12 +04:00
Alexsander 4d5a0acc2a some changes in SKPhotoBrowser 2016-03-12 23:43:40 +04:00
Alexsander 7912b216e4 I changed SKPhotoBrowser. I removed several no longer necessary functions and variables. I also added new functions which calculate new frame for buttons after a rotation of the device. 2016-03-12 23:11:32 +04:00
Alexsandersky 1a159e50ea Merge pull request #46 from alexsanderskywork/master
Some changes.
2016-03-12 03:03:34 +04:00
Alexsander 56282228fb Some changes. Removed code which not right worked when device is rotating. It is necessary to fix. 2016-03-12 03:01:27 +04:00
Alexsandersky 4395a261aa Update SKPhotoBrowser.podspec 2016-03-11 23:36:59 +04:00
Alexsandersky cb5d4cfc82 Merge pull request #44 from krummler/master
Added delegate method to allow animating closing to other frames
2016-03-11 23:36:28 +04:00
Kevin Rummler c643d56b9e Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser 2016-03-11 13:44:26 +01:00
Kevin Rummler 759c714b4a added option to animate closing from other cells 2016-03-11 13:43:50 +01:00
Alexsandersky a4ed7da7f5 Update SKPhotoBrowser.podspec 2016-03-11 15:09:43 +04:00
Alexsandersky 2859e0fac6 Merge pull request #40 from krummler/master
Several tweaks and impovements
2016-03-11 15:08:17 +04:00
Kevin Rummler 6cb27a9a04 Adding tab with camera roll 2016-03-11 11:47:09 +01:00
Kevin Rummler ea38ab29a8 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser
# Conflicts:
#	SKPhotoBrowser/SKPhotoBrowser.swift
2016-03-11 09:45:09 +01:00
Alexsandersky 2c4fae7b41 Merge pull request #42 from alexsanderskywork/master
More stability
2016-03-11 07:03:36 +04:00
Alexsandersky dd4e0a643d More stability 2016-03-11 07:01:34 +04:00
Alexsandersky d9367496cc more stability 2016-03-11 06:59:55 +04:00
Kevin Rummler 80958e7742 Fix for broken fade-effect when using the browser in a more layered application 2016-03-10 12:07:48 +01:00
Kevin Rummler 874f0bba98 Grabbing first visiblePage suffices when opening the gallery 2016-03-10 09:36:49 +01:00
Kevin Rummler aff4ee03d7 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser 2016-03-10 09:33:34 +01:00
Alexsandersky 162bfbb2c6 Merge pull request #41 from alexsanderskywork/master
Some changes
2016-03-10 11:31:59 +04:00
Alexsandersky 69c6ca9136 Update pod spec 2016-03-10 11:27:59 +04:00
Alexsandersky 7f2db66cdc some fix 2016-03-10 11:21:20 +04:00
Kevin Rummler 5026253f08 Merge branch 'master' of https://github.com/suzuki-0000/SKPhotoBrowser
# Conflicts:
#	SKPhotoBrowser/SKPhotoBrowser.swift
2016-03-09 17:08:19 +01:00
Kevin Rummler 30ef41ee61 Several tweaks and impovements
Add support for cornerRadius transition
Option to bounce transition
Animate properly from zoomed/panned state
2016-03-09 17:02:51 +01:00
Alexsandersky 66125de128 Merge pull request #39 from alexsanderskywork/master
Version 1.6.3
2016-03-09 18:36:45 +04:00
Alexsandersky 1370ffdb7d update to version 1.6.3 Fixed bug with rotation device 2016-03-09 18:32:43 +04:00
Alexsandersky 9e77727da9 changed visiblePages to from Set<SKZoomingScrollView> to [SKZoomingScrollView] it is more convenient for us to find the index.
I fixed bug with rotation device. But I don't like this animation and need to fix when we return to previous page
2016-03-09 18:21:13 +04:00
Alexsandersky ecc129c323 resize current image after rotation device 2016-03-09 17:54:13 +04:00
Alexsandersky da03786f9a removed deleted array from SKPhotoBrowser because it was not needed 2016-03-09 16:50:25 +04:00
Alexsandersky 7da3dc388e I added possibility which to enable and disable the zoom in the black area 2016-03-09 13:58:20 +04:00
Alexsandersky 5a1de600a7 Update README.md 2016-03-09 00:43:52 +04:00
Alexsandersky 29d7892246 change version to 1.6.2 2016-03-09 00:41:56 +04:00
Alexsandersky e9a0daa664 [clean]using swiftlint.
[clean]using swiftlint.
2016-03-09 00:40:44 +04:00
Alexsandersky dfb813470c Update README.md
changed readme
2016-03-09 00:21:21 +04:00
Alexsandersky 00c1b51402 Update SKPhotoBrowser.podspec
Update version from 1.6.0 to 1.6.1
2016-03-09 00:04:38 +04:00
Alexsandersky f02652f1d2 Merge pull request #37 from alexsanderskywork/master
I added CustomDeleteButton but there is a mistake with rotation device
2016-03-08 23:58:21 +04:00
Alexsandersky 63760b8fb9 I added CustomDeleteButton but there is a mistake with rotation device 2016-03-08 23:56:02 +04:00
suzuki_keishi 840f26216a [update]readme, pod ver. 2016-03-07 13:35:27 +09:00
keishi suzuki ba3bfa7309 Merge pull request #36 from alexsanderskywork/master
CustomCloseButton (customDoneButton)
2016-03-07 13:30:45 +09:00
Alexsandersky 37a14f4807 changed imageEdgeInsets for closeButton and deleteButton. I made two categories of imageEdgeInsets for iPhone and for iPad because the icons were very small on iPad 2016-03-07 02:12:50 +04:00
Alexsandersky 02409ae398 added autoresize to closeButton and marked where we should to fix 2016-03-07 01:25:30 +04:00
Alexsandersky bc2fc4f3b4 added autoresize to deleteButton 2016-03-07 01:09:32 +04:00
Alexsandersky 85a2343b3e several changes. I added autoresizing mask to customCloseButton. But the effect is the same as from my functions. only iPads better 2016-03-07 00:51:05 +04:00
Alexsandersky 91b9e31fa2 changed delete button width 2016-03-06 22:11:15 +04:00
Alexsandersky 4919335451 change rotation functions 2016-03-06 21:48:14 +04:00
Alexsandersky baa2a844f3 I added change frame while device change orientation only if the custom close button constraints are nil 2016-03-06 21:39:31 +04:00
Alexsandersky 045f38e18e I added functions which are changing frames of custom close button while device orientation is changing 2016-03-06 20:36:24 +04:00
Alexsandersky 6e14d7d2d5 delete local bundle 2016-03-06 19:47:52 +04:00
Alexsandersky 205f4fedf5 moved NSNotificationCenter to setup function 2016-03-06 19:32:41 +04:00
Alexsandersky 1b39bae749 it change delete button frame while rotation of device 2016-03-06 17:11:13 +04:00
Alexsandersky df2b47507a I made customCloseButton private(set) that another developer can to set NSLayoutConstraint 2016-03-06 16:51:53 +04:00
Alexsandersky f7bafd3773 I added the setting of custonCloseButton and simple example for using this.
let browser = SKPhotoBrowser(photos: photoArray)
       browser.delegate = self
        browser.isForceStatusBarHidden = true
        browser.displayBackAndForwardButton = false
        browser.displayAction = false
        browser.displayCounterLabel = false
        browser.displayDeleteButton = true
        browser.displayToolbar = false
        //
        browser.displayCustomCloseButton = true
        browser.customCloseButtonImage = UIImage(named: "closeButton.png")
        browser.customCloseButtonShowFrame = CGRect(x: view.frame.width / 2 - 25, y: view.frame.height - 58, width: 50, height: 50)
        browser.customCloseButtonHideFrame = CGRect(x: view.frame.width / 2 - 25, y: view.frame.height - 58, width: 50, height: 50)
         presentViewController(browser, animated: true, completion: nil)
2016-03-06 15:28:16 +04:00
Alexsandersky 7ccbec4d1b I changed the setting of the done button. I rename DoneButton to CloseButton. I made separate setting of close button like for DeleteButton because if we added CloseButton on View and hided it we could not to touch on black area that when we going to zoom the image. 2016-03-06 13:56:03 +04:00
43 changed files with 3284 additions and 1115 deletions

186
CHANGELOG.md Normal file
View File

@ -0,0 +1,186 @@
# Change Log
## 3.1.2
Released on 16-9-2016
#### Fixed
- Scrolling performance slowed #145
## 3.1.1
Released on 15-9-2016
#### Fixed
- Example crash in xcode8 fixed
- Provides various UI configuration options via SKPhotoBrowserOptions. #144
## 3.1.0
Released on 9-2016
#### Fixed
- Issue with multiple actionButtonTitles #137
- fix swiftlint warnings #140
- Update for Xcode 8 GM (swift 2.3). #141
## 3.0.2
Released on 9-2016
#### Fixed
- Issue with multiple actionButtonTitles #137
- Impossible to zoom when resolution is 1024x768 #134
- Crash bug at zooming scrool view #133
## 3.0.1
Released on 9-2016
#### Fixed
- Skip loading image if already loaded #135
Released on 8-2016
#### Some Interface is removed, changed this version.
- status bar handling is removed.
- custom button handling interface is chagned.
- custom option goes internal/private. use option via SKPhotoBrowserOptions.
#### Add
- Add changelog
#### Fixed
- prepare for swift3.0.
- refactoring code for new implement.
- Parent View disappears when dismissed. #120
- Glitch when origin imageview is not correct size #108
- Problems with the "long" photo #116
#### Remove
- Statusbar handling.
- Some public property to internal for improving
## 2.0.x
Released on 8-2016
#### Added
- Migrate UIImage cache category to new SKCache
#### Fixed
- Make cached response data return optional
- Fixed issue when animatedFromView not has a superview but has superlayer
- Fixed when image downloaded then not show activityindicator
- Update for Swift2.3
---
## 1.9.x
Released on 6-2016
#### Added
- Delegate to notify when the user scroll to an index
- Single tap to dismiss
#### Fixed
- Fixed a bug where the activity indicator was only visible
- Fixed unit test and problems running when being bridged
---
## 1.8.x
Released on 4-2016
#### Added
- Using SKPhotoProtocol to enable usage from SKLocalPhoto
- SKLocalPhoto to support local photo from file
#### Fixed
- Bug when animation when tap.
- The indicator may not disappear when loading local image
- Event crash when closing before image has been loaded
- Fix crash on initialisation
---
## 1.7.x
Released on 3-2016
#### Added
- Enable ability to override statusBar style
#### Fixed
- Update for swift2.0
- Bug when zooming small image
- Prevent crash when closing before image has been loaded
---
## 1.6.x
Released on 2016-3
#### Fixed
- Change maxScale to 1.0 it works perfectly.
- Fixed the bug which was after the device rotation
---
## 1.5.x
Released on 2016-3
#### Added
- Delete Button
#### Fixed
- Change maxScale to 1.0 it works perfectly.
- Rew algorithm for maxScale.
- Changed UIActionSheet to UIAlertController with ActionSheet style
---
## 1.4.x
Released on 2-2016
#### Added
- Delegate add for actionbutton.
- DidShowPhotoAtIndex delegate goes to optional.
#### Fixed
- Zooming bug fixed.
---
## 1.3.x
Released on 1-2016
#### Added
- Added action functionality similar to IDMPhotoBrowser.
- Add extra caption for share
#### Fixed
- Bug fixed for mail crash
---
## 1.2.x
Released on 10-2015
#### Added
- SKPhotoProtocol is implemented.
#### Fixed
- Double tap bug fixed
---
## 1.1.x
Released on 10-2015
#### Fixed
- some property make private.
- layout bug fixed when zoom.
## 1.0.0
Released on 10-2015

138
README.md
View File

@ -1,22 +1,25 @@
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)
[![Swift 2.0](https://img.shields.io/badge/Swift-2.0-orange.svg?style=flat)](https://developer.apple.com/swift/)
Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift2.0, based on [IDMPhotoBrowser](https://github.com/ideaismobile/IDMPhotoBrowser), [MWPhotoBrowser](https://github.com/mwaterfall/MWPhotoBrowser).
Simple PhotoBrowser/Viewer inspired by facebook, twitter photo browsers written by swift, based on [IDMPhotoBrowser](https://github.com/ideaismobile/IDMPhotoBrowser), [MWPhotoBrowser](https://github.com/mwaterfall/MWPhotoBrowser).
## Note
- released v3.0.x and this version goes some breaking changes. please check [CHANGELOG](https://github.com/suzuki-0000/SKPhotoBrowser/blob/master/CHANGELOG.md).
## features
- Can display one or more images by providing either `UIImage` objects, or string of URL array.
- Display one or more images by providing either `UIImage` objects, or string of URL array.
- Photos can be zoomed and panned, and optional captions can be displayed
- Minimalistic Facebook-like interface, swipe up/down to dismiss
- has simple ability to custom photobrowser. (hide/show statusbar, some toolbar for controls, swipe control)
- Handling and caching photos from web
- Landscape handling.
- Ability to custom control. (hide/ show toolbar for controls, / swipe control)
- Handling and caching photos from web
- Landscape handling
- Delete photo support(by offbye). By set displayDelete=true show a delete icon in statusbar, deleted indexes can be obtain from delegate func didDeleted
![sample](Screenshots/example01.gif)
![sample](Screenshots/example02.gif)
## Requirements
- iOS 8.0+
@ -43,31 +46,45 @@ github "suzuki-0000/SKPhotoBrowser"
Add the code directly into your project.
##Usage
See the code snippet below for an example of how to implement, or example project would be easy to understand.
See the code snippet below for an example of how to implement, or see the example project.
from UIImages:
```swift
// add SKPhoto Array from UIImage
// 1. create SKPhoto Array from UIImage
var images = [SKPhoto]()
let photo = SKPhoto.photoWithImage(UIImage())// add some UIImage
images.append(photo)
// create PhotoBrowser Instance, and present.
// 2. create PhotoBrowser Instance, and present from your viewController.
let browser = SKPhotoBrowser(photos: images)
browser.initializePageIndex(0)
browser.delegate = self
presentViewController(browser, animated: true, completion: {})
```
from web URLs:
from URLs:
```swift
// URL pattern snippet
// 1. create URL Array
var images = [SKPhoto]()
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150x150.png")
photo.shouldCachePhotoURLImage = false // you can use image cache by true(NSCache)
images.append(photo)
// create PhotoBrowser Instance, and present.
// 2. create PhotoBrowser Instance, and present.
let browser = SKPhotoBrowser(photos: images)
browser.initializePageIndex(0)
presentViewController(browser, animated: true, completion: {})
```
from local files:
```swift
// 1. create images from local files
var images = [SKLocalPhoto]()
let photo = SKLocalPhoto.photoWithImageURL("..some_local_path/150x150.png")
images.append(photo)
// 2. create PhotoBrowser Instance, and present.
let browser = SKPhotoBrowser(photos: images)
browser.initializePageIndex(0)
presentViewController(browser, animated: true, completion: {})
```
@ -77,6 +94,7 @@ If you want to use zooming effect from an existing view, use another initializer
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItemAtIndexPath(indexPath)
let originImage = cell.exampleImageView.image // some image for baseImage
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.initializePageIndex(indexPath.row)
presentViewController(browser, animated: true, completion: {})
@ -86,24 +104,65 @@ func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath i
### Custom
#### Toolbar
You can customize the toolbar(back/forward, counter, some action) button.
- displayCounterLabel (default is true)
- displayBackAndForwardButton (default is true)
- displayAction (default is true)
If you dont want the toolbar at all, you can set displayToolbar = false (default is true)
You can customize Toolbar via SKPhotoBrowserOptions.
```swift
SKPhotoBrowserOptions.displayToolbar = false // all tool bar will be hidden
SKPhotoBrowserOptions.displayCounterLabel = false // counter label will be hidden
SKPhotoBrowserOptions.displayBackAndForwardButton = false // back / forward button will be hidden
SKPhotoBrowserOptions.displayAction = false // action button will be hidden
SKPhotoBrowserOptions.displayDeleteButton = true // delete button will be shown
SKPhotoBrowserOptions.displayHorizontalScrollIndicator = false // horizontal scroll bar will be hidden
SKPhotoBrowserOptions.displayVerticalScrollIndicator = false // vertical scroll bar will be hidden
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.displayToolbar = false // all tool bar will be hidden
browser.displayCounterLabel = false // counter label will be hidden
browser.displayBackAndForwardButton = false // back / forward button will be hidden
browser.displayAction = false // action button will be hidden
browser.displayDeleteButton = true // delete button will be shown
```
#### Delete
You can delete your photo for your own hanlding
#### Colors
You can customize text, icon and background colors via SKPhotoBrowserOptions
```swift
SKPhotoBrowserOptions.backgroundColor = UIColor.whiteColor() // browser view will be white
SKPhotoBrowserOptions.textAndIconColor = UIColor.blackColor() // text and icons will be black
SKPhotoBrowserOptions.toolbarTextShadowColor = UIColor.clearColor() // shadow of toolbar text will be removed
SKPhotoBrowserOptions.toolbarFont = UIFont(name: "Futura", size: 16.0) // font of toolbar will be 'Futura'
SKPhotoBrowserOptions.captionFont = UIFont(name: "Helvetica", size: 18.0) // font of toolbar will be 'Helvetica'
```
#### Images
You can customize the padding of displayed images via SKPhotoBrowserOptions
```swift
SKPhotoBrowserOptions.imagePaddingX = 50 // image padding left and right will be 25
SKPhotoBrowserOptions.imagePaddingY = 50 // image padding top and bottom will be 25
```
#### Statusbar
You can customize the visibility of the Statusbar in browser view via SKPhotoBrowserOptions
```swift
SKPhotoBrowserOptions.displayStatusbar = false // status bar will be hidden
```
#### Custom Cache From Web URL
You can use SKCacheable protocol if others are adaptable. (SKImageCacheable or SKRequestResponseCacheable)
```swift
e.g. SDWebImage
// 1. create custon cache. implement function for protocol
class CustomImageCache: SKImageCacheable { var cache: SDImageCache }
// 2. replace SKCache instance with custom cache
SKCache.sharedCache.imageCache = CustomImageCache()
```
#### CustomButton Image
Close, Delete buttons are able to change image and frame.
``` swift
browser.updateCloseButton(UIImage())
browser.updateUpdateButton(UIImage())
```
#### Delete Photo
You can delete your photo for your own handling. detect button tap from `removePhoto` delegate function.
#### Photo Captions
Photo captions can be displayed simply bottom of PhotoBrowser. by setting the `caption` property on specific photos:
@ -116,24 +175,19 @@ images.append(photo)
#### SwipeGesture
vertical swipe can enable/disable:
``` swift
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.disableVerticalSwipe = true
```
#### StatusBar
you can hide statusbar forcely using property:
``` swift
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.isForceStatusBarHidden = true
SKPhotoBrowserOptions.disableVerticalSwipe = true
```
#### Delegate
There's some trigger point you can handle using delegate. those are optional.
- didShowPhotoAtIndex(index:Int)
- willDismissAtPageIndex(index:Int)
- willShowActionSheet(photoIndex: Int)
- didDismissAtPageIndex(index:Int)
- didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int)
- didScrollToIndex(index: Int)
- removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void))
- viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView?
```swift
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
@ -154,6 +208,18 @@ func didDismissAtPageIndex(index: Int) {
```
#### 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
SKPhotoBrowserOptions.bounceAnimation = true // default false
```
## Photos from
- [Unsplash](https://unsplash.com)

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "SKPhotoBrowser"
s.version = "1.5.1"
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,6 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
0AE527521DABB87500619FAD /* SKNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE527511DABB87500619FAD /* SKNavigationBar.swift */; };
210E53ED1C986D3A008DD5E3 /* UIView+Radius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210E53EC1C986D3A008DD5E3 /* UIView+Radius.swift */; };
210E53EF1C986D57008DD5E3 /* UIImage+Rotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210E53EE1C986D57008DD5E3 /* UIImage+Rotation.swift */; };
26C97AD51D0EB6870039F6CB /* SKCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C97AD41D0EB6870039F6CB /* SKCache.swift */; };
26C97AD71D0EB6A00039F6CB /* SKCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C97AD61D0EB6A00039F6CB /* SKCacheTests.swift */; };
26C97AD91D0EB8BB0039F6CB /* SKCacheable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26C97AD81D0EB8BB0039F6CB /* SKCacheable.swift */; };
8909B5341BC791280060A053 /* SKPhotoBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8909B5331BC791280060A053 /* SKPhotoBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; };
8909B5431BC791510060A053 /* SKCaptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B53B1BC791510060A053 /* SKCaptionView.swift */; };
8909B5441BC791510060A053 /* SKDetectingImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B53C1BC791510060A053 /* SKDetectingImageView.swift */; };
@ -16,9 +22,35 @@
8909B5491BC791510060A053 /* SKPhotoBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B5411BC791510060A053 /* SKPhotoBrowser.swift */; };
8909B54A1BC791510060A053 /* SKZoomingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B5421BC791510060A053 /* SKZoomingScrollView.swift */; };
8909B54D1BC7916E0060A053 /* SKPhotoBrowser.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */; };
890A6F201D5D9E53003B01F0 /* SKToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */; };
8917B1B01D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */; };
8917B1B41D5A14B0000CE1C4 /* SKButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */; };
89C24A821D657AD1005F09A9 /* SKPhotoBrowserOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */; };
89C24A841D657AFE005F09A9 /* SKPagingScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */; };
89D0BA471D5994A8002A811B /* SKAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0BA461D5994A8002A811B /* SKAnimator.swift */; };
89D0BA491D59966B002A811B /* SKMesurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0BA481D59966B002A811B /* SKMesurement.swift */; };
8CA6C6521CBE76E80054D3C2 /* SKLocalPhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */; };
A64B89361CB04222000071B9 /* SKPhotoBrowserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A64B89351CB04222000071B9 /* SKPhotoBrowserTests.swift */; };
A64B89381CB04222000071B9 /* SKPhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5301BC791280060A053 /* SKPhotoBrowser.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
A64B89391CB04222000071B9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8909B5271BC791280060A053 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8909B52F1BC791280060A053;
remoteInfo = SKPhotoBrowser;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0AE527511DABB87500619FAD /* SKNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKNavigationBar.swift; sourceTree = "<group>"; };
210E53EC1C986D3A008DD5E3 /* UIView+Radius.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIView+Radius.swift"; path = "extensions/UIView+Radius.swift"; sourceTree = "<group>"; };
210E53EE1C986D57008DD5E3 /* UIImage+Rotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+Rotation.swift"; path = "extensions/UIImage+Rotation.swift"; sourceTree = "<group>"; };
26C97AD41D0EB6870039F6CB /* SKCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKCache.swift; sourceTree = "<group>"; };
26C97AD61D0EB6A00039F6CB /* SKCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKCacheTests.swift; sourceTree = "<group>"; };
26C97AD81D0EB8BB0039F6CB /* SKCacheable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKCacheable.swift; sourceTree = "<group>"; };
8909B5301BC791280060A053 /* SKPhotoBrowser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SKPhotoBrowser.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8909B5331BC791280060A053 /* SKPhotoBrowser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SKPhotoBrowser.h; sourceTree = "<group>"; };
8909B5351BC791280060A053 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -30,6 +62,17 @@
8909B5411BC791510060A053 /* SKPhotoBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowser.swift; sourceTree = "<group>"; };
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKZoomingScrollView.swift; sourceTree = "<group>"; };
8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = SKPhotoBrowser.bundle; sourceTree = "<group>"; };
890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKToolbar.swift; sourceTree = "<group>"; };
8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserDelegate.swift; sourceTree = "<group>"; };
8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKButtons.swift; sourceTree = "<group>"; };
89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserOptions.swift; sourceTree = "<group>"; };
89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKPagingScrollView.swift; sourceTree = "<group>"; };
89D0BA461D5994A8002A811B /* SKAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKAnimator.swift; sourceTree = "<group>"; };
89D0BA481D59966B002A811B /* SKMesurement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKMesurement.swift; sourceTree = "<group>"; };
8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKLocalPhoto.swift; sourceTree = "<group>"; };
A64B89331CB04222000071B9 /* SKPhotoBrowserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SKPhotoBrowserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A64B89351CB04222000071B9 /* SKPhotoBrowserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKPhotoBrowserTests.swift; sourceTree = "<group>"; };
A64B89371CB04222000071B9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -40,13 +83,31 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
A64B89301CB04222000071B9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A64B89381CB04222000071B9 /* SKPhotoBrowser.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
210E53EB1C986D1C008DD5E3 /* extensions */ = {
isa = PBXGroup;
children = (
210E53EC1C986D3A008DD5E3 /* UIView+Radius.swift */,
210E53EE1C986D57008DD5E3 /* UIImage+Rotation.swift */,
);
name = extensions;
sourceTree = "<group>";
};
8909B5261BC791280060A053 = {
isa = PBXGroup;
children = (
8909B5321BC791280060A053 /* SKPhotoBrowser */,
A64B89341CB04222000071B9 /* SKPhotoBrowserTests */,
8909B5311BC791280060A053 /* Products */,
);
sourceTree = "<group>";
@ -55,6 +116,7 @@
isa = PBXGroup;
children = (
8909B5301BC791280060A053 /* SKPhotoBrowser.framework */,
A64B89331CB04222000071B9 /* SKPhotoBrowserTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -62,20 +124,42 @@
8909B5321BC791280060A053 /* SKPhotoBrowser */ = {
isa = PBXGroup;
children = (
89D0BA461D5994A8002A811B /* SKAnimator.swift */,
8917B1B31D5A14B0000CE1C4 /* SKButtons.swift */,
26C97AD41D0EB6870039F6CB /* SKCache.swift */,
26C97AD81D0EB8BB0039F6CB /* SKCacheable.swift */,
8909B53B1BC791510060A053 /* SKCaptionView.swift */,
8909B53C1BC791510060A053 /* SKDetectingImageView.swift */,
8909B53D1BC791510060A053 /* SKDetectingView.swift */,
8909B53E1BC791510060A053 /* SKIndicatorView.swift */,
8CA6C6511CBE76E80054D3C2 /* SKLocalPhoto.swift */,
89C24A831D657AFE005F09A9 /* SKPagingScrollView.swift */,
8909B53F1BC791510060A053 /* SKPhoto.swift */,
8909B5411BC791510060A053 /* SKPhotoBrowser.swift */,
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */,
89C24A811D657AD1005F09A9 /* SKPhotoBrowserOptions.swift */,
890A6F1F1D5D9E53003B01F0 /* SKToolbar.swift */,
0AE527511DABB87500619FAD /* SKNavigationBar.swift */,
8909B5331BC791280060A053 /* SKPhotoBrowser.h */,
8917B1AF1D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift */,
89D0BA481D59966B002A811B /* SKMesurement.swift */,
8909B5421BC791510060A053 /* SKZoomingScrollView.swift */,
8909B5351BC791280060A053 /* Info.plist */,
210E53EB1C986D1C008DD5E3 /* extensions */,
8909B54C1BC7916E0060A053 /* SKPhotoBrowser.bundle */,
);
path = SKPhotoBrowser;
sourceTree = "<group>";
};
A64B89341CB04222000071B9 /* SKPhotoBrowserTests */ = {
isa = PBXGroup;
children = (
26C97AD61D0EB6A00039F6CB /* SKCacheTests.swift */,
A64B89351CB04222000071B9 /* SKPhotoBrowserTests.swift */,
A64B89371CB04222000071B9 /* Info.plist */,
);
path = SKPhotoBrowserTests;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -109,18 +193,41 @@
productReference = 8909B5301BC791280060A053 /* SKPhotoBrowser.framework */;
productType = "com.apple.product-type.framework";
};
A64B89321CB04222000071B9 /* SKPhotoBrowserTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = A64B893D1CB04222000071B9 /* Build configuration list for PBXNativeTarget "SKPhotoBrowserTests" */;
buildPhases = (
A64B892F1CB04222000071B9 /* Sources */,
A64B89301CB04222000071B9 /* Frameworks */,
A64B89311CB04222000071B9 /* Resources */,
);
buildRules = (
);
dependencies = (
A64B893A1CB04222000071B9 /* PBXTargetDependency */,
);
name = SKPhotoBrowserTests;
productName = SKPhotoBrowserTests;
productReference = A64B89331CB04222000071B9 /* SKPhotoBrowserTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
8909B5271BC791280060A053 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = suzuki_keishi;
TargetAttributes = {
8909B52F1BC791280060A053 = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0800;
};
A64B89321CB04222000071B9 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
};
};
};
@ -137,6 +244,7 @@
projectRoot = "";
targets = (
8909B52F1BC791280060A053 /* SKPhotoBrowser */,
A64B89321CB04222000071B9 /* SKPhotoBrowserTests */,
);
};
/* End PBXProject section */
@ -150,6 +258,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
A64B89311CB04222000071B9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@ -173,18 +288,48 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26C97AD91D0EB8BB0039F6CB /* SKCacheable.swift in Sources */,
89C24A841D657AFE005F09A9 /* SKPagingScrollView.swift in Sources */,
890A6F201D5D9E53003B01F0 /* SKToolbar.swift in Sources */,
8909B5441BC791510060A053 /* SKDetectingImageView.swift in Sources */,
8909B54A1BC791510060A053 /* SKZoomingScrollView.swift in Sources */,
8CA6C6521CBE76E80054D3C2 /* SKLocalPhoto.swift in Sources */,
89D0BA471D5994A8002A811B /* SKAnimator.swift in Sources */,
210E53ED1C986D3A008DD5E3 /* UIView+Radius.swift in Sources */,
8917B1B01D5A13DE000CE1C4 /* SKPhotoBrowserDelegate.swift in Sources */,
89D0BA491D59966B002A811B /* SKMesurement.swift in Sources */,
8909B5471BC791510060A053 /* SKPhoto.swift in Sources */,
8909B5461BC791510060A053 /* SKIndicatorView.swift in Sources */,
8917B1B41D5A14B0000CE1C4 /* SKButtons.swift in Sources */,
89C24A821D657AD1005F09A9 /* SKPhotoBrowserOptions.swift in Sources */,
26C97AD51D0EB6870039F6CB /* SKCache.swift in Sources */,
0AE527521DABB87500619FAD /* SKNavigationBar.swift in Sources */,
210E53EF1C986D57008DD5E3 /* UIImage+Rotation.swift in Sources */,
8909B5431BC791510060A053 /* SKCaptionView.swift in Sources */,
8909B5491BC791510060A053 /* SKPhotoBrowser.swift in Sources */,
8909B5451BC791510060A053 /* SKDetectingView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A64B892F1CB04222000071B9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
26C97AD71D0EB6A00039F6CB /* SKCacheTests.swift in Sources */,
A64B89361CB04222000071B9 /* SKPhotoBrowserTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
A64B893A1CB04222000071B9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8909B52F1BC791280060A053 /* SKPhotoBrowser */;
targetProxy = A64B89391CB04222000071B9 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
8909B5361BC791280060A053 /* Debug */ = {
isa = XCBuildConfiguration;
@ -291,6 +436,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@ -309,6 +455,33 @@
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.SKPhotoBrowser;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 2.3;
};
name = Release;
};
A64B893B1CB04222000071B9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
INFOPLIST_FILE = SKPhotoBrowserTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "Alexsander-Khitev.SKPhotoBrowserTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
A64B893C1CB04222000071B9 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
INFOPLIST_FILE = SKPhotoBrowserTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "Alexsander-Khitev.SKPhotoBrowserTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Release;
};
@ -333,6 +506,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A64B893D1CB04222000071B9 /* Build configuration list for PBXNativeTarget "SKPhotoBrowserTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A64B893B1CB04222000071B9 /* Debug */,
A64B893C1CB04222000071B9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 8909B5271BC791280060A053 /* Project object */;

View File

@ -0,0 +1,189 @@
//
// SKAnimator.swift
// SKPhotoBrowser
//
// Created by keishi suzuki on 2016/08/09.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
@objc public protocol SKPhotoBrowserAnimatorDelegate {
func willPresent(browser: SKPhotoBrowser)
func willDismiss(browser: SKPhotoBrowser)
}
class SKAnimator: NSObject, SKPhotoBrowserAnimatorDelegate {
var resizableImageView: UIImageView?
var senderOriginImage: UIImage!
var senderViewOriginalFrame: CGRect = .zero
var senderViewForAnimation: UIView?
var finalImageViewFrame: CGRect = .zero
var bounceAnimation: Bool = false
var animationDuration: NSTimeInterval {
if SKPhotoBrowserOptions.bounceAnimation {
return 0.5
}
return 0.35
}
var animationDamping: CGFloat {
if SKPhotoBrowserOptions.bounceAnimation {
return 0.8
}
return 1
}
func willPresent(browser: SKPhotoBrowser) {
guard let appWindow = UIApplication.sharedApplication().delegate?.window else {
return
}
guard let window = appWindow else {
return
}
guard let sender = browser.delegate?.viewForPhoto?(browser, index: browser.initialPageIndex) ?? senderViewForAnimation else {
presentAnimation(browser)
return
}
let photo = browser.photoAtIndex(browser.currentPageIndex)
let imageFromView = (senderOriginImage ?? browser.getImageFromView(sender)).rotateImageByOrientation()
let imageRatio = imageFromView.size.width / imageFromView.size.height
senderViewOriginalFrame = calcOriginFrame(sender)
finalImageViewFrame = calcFinalFrame(imageRatio)
resizableImageView = UIImageView(image: imageFromView)
resizableImageView!.frame = senderViewOriginalFrame
resizableImageView!.clipsToBounds = true
resizableImageView!.contentMode = photo.contentMode
if sender.layer.cornerRadius != 0 {
let duration = (animationDuration * Double(animationDamping))
resizableImageView!.layer.masksToBounds = true
resizableImageView!.addCornerRadiusAnimation(sender.layer.cornerRadius, to: 0, duration: duration)
}
window.addSubview(resizableImageView!)
presentAnimation(browser)
}
func willDismiss(browser: SKPhotoBrowser) {
guard let sender = browser.delegate?.viewForPhoto?(browser, index: browser.currentPageIndex),
image = browser.photoAtIndex(browser.currentPageIndex).underlyingImage,
scrollView = browser.pageDisplayedAtIndex(browser.currentPageIndex) else {
senderViewForAnimation?.hidden = false
browser.dismissPhotoBrowser(animated: false)
return
}
senderViewForAnimation = sender
browser.view.hidden = true
browser.backgroundView.hidden = false
browser.backgroundView.alpha = 1
senderViewOriginalFrame = calcOriginFrame(sender)
let photo = browser.photoAtIndex(browser.currentPageIndex)
let contentOffset = scrollView.contentOffset
let scrollFrame = scrollView.photoImageView.frame
let offsetY = scrollView.center.y - (scrollView.bounds.height/2)
let frame = CGRect(
x: scrollFrame.origin.x - contentOffset.x,
y: scrollFrame.origin.y + contentOffset.y + offsetY,
width: scrollFrame.width,
height: scrollFrame.height)
// resizableImageView.image = scrollView.photo?.underlyingImage?.rotateImageByOrientation()
resizableImageView!.image = image.rotateImageByOrientation()
resizableImageView!.frame = frame
resizableImageView!.alpha = 1.0
resizableImageView!.clipsToBounds = true
resizableImageView!.contentMode = photo.contentMode
if let view = senderViewForAnimation where view.layer.cornerRadius != 0 {
let duration = (animationDuration * Double(animationDamping))
resizableImageView!.layer.masksToBounds = true
resizableImageView!.addCornerRadiusAnimation(0, to: view.layer.cornerRadius, duration: duration)
}
dismissAnimation(browser)
}
}
private extension SKAnimator {
func calcOriginFrame(sender: UIView) -> CGRect {
if let senderViewOriginalFrameTemp = sender.superview?.convertRect(sender.frame, toView:nil) {
return senderViewOriginalFrameTemp
} else if let senderViewOriginalFrameTemp = sender.layer.superlayer?.convertRect(sender.frame, toLayer: nil) {
return senderViewOriginalFrameTemp
} else {
return .zero
}
}
func calcFinalFrame(imageRatio: CGFloat) -> CGRect {
if SKMesurement.screenRatio < imageRatio {
let width = SKMesurement.screenWidth
let height = width / imageRatio
let yOffset = (SKMesurement.screenHeight - height) / 2
return CGRect(x: 0, y: yOffset, width: width, height: height)
} else {
let height = SKMesurement.screenHeight
let width = height * imageRatio
let xOffset = (SKMesurement.screenWidth - width) / 2
return CGRect(x: xOffset, y: 0, width: width, height: height)
}
}
}
private extension SKAnimator {
func presentAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
browser.view.hidden = true
browser.view.alpha = 0.0
UIView.animateWithDuration(
animationDuration,
delay: 0,
usingSpringWithDamping:animationDamping,
initialSpringVelocity:0,
options:.CurveEaseInOut,
animations: {
browser.showButtons()
browser.backgroundView.alpha = 1.0
self.resizableImageView?.frame = self.finalImageViewFrame
},
completion: { (Bool) -> Void in
UIApplication.sharedApplication().setStatusBarHidden(!SKPhotoBrowserOptions.displayStatusbar, withAnimation: .Fade)
browser.view.hidden = false
browser.view.alpha = 1.0
browser.backgroundView.hidden = true
self.resizableImageView?.alpha = 0.0
})
}
func dismissAnimation(browser: SKPhotoBrowser, completion: (Void -> Void)? = nil) {
UIView.animateWithDuration(
animationDuration,
delay:0,
usingSpringWithDamping:animationDamping,
initialSpringVelocity:0,
options:.CurveEaseInOut,
animations: {
browser.backgroundView.alpha = 0.0
self.resizableImageView?.layer.frame = self.senderViewOriginalFrame
},
completion: { (Bool) -> () in
browser.dismissPhotoBrowser(animated: true) {
self.resizableImageView?.removeFromSuperview()
}
})
}
}

View File

@ -0,0 +1,90 @@
//
// SKButtons.swift
// SKPhotoBrowser
//
// Created by on 2016/08/09.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
// helpers which often used
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
class SKButton: UIButton {
var showFrame: CGRect!
var hideFrame: CGRect!
var insets: UIEdgeInsets {
return UI_USER_INTERFACE_IDIOM() == .Phone
? UIEdgeInsets(top: 15.25, left: 15.25, bottom: 15.25, right: 15.25) : UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
}
var size: CGSize = CGSize(width: 44, height: 44)
var margin: CGFloat = 5
var buttonTopOffset: CGFloat { return 5 }
func setup(imageName: String) {
backgroundColor = .clearColor()
tintColor = SKPhotoBrowserOptions.textAndIconColor
imageEdgeInsets = insets
// clipsToBounds = true
translatesAutoresizingMaskIntoConstraints = true
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
setImage(image, forState: .Normal)
}
func updateFrame() { }
func setFrameSize(size: CGSize) {
let newRect = CGRect(x: margin, y: buttonTopOffset, width: size.width, height: size.height)
self.frame = newRect
showFrame = newRect
hideFrame = CGRect(x: margin, y: -20, width: size.width, height: size.height)
}
}
class SKCloseButton: SKButton {
let imageName = "btn_common_close_wh"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup(imageName)
showFrame = CGRect(x: margin, y: buttonTopOffset, width: size.width, height: size.height)
hideFrame = CGRect(x: margin, y: -20, width: size.width, height: size.height)
}
override func updateFrame() {
}
}
class SKDeleteButton: SKButton {
let imageName = "btn_common_delete_wh"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup(imageName)
showFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: buttonTopOffset, width: size.width, height: size.height)
hideFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: -20, width: size.width, height: size.height)
}
override func updateFrame() {
}
override func setFrameSize(size: CGSize) {
let newRect = CGRect(x: SKMesurement.screenWidth - size.width, y: buttonTopOffset, width: size.width, height: size.height)
self.frame = newRect
showFrame = newRect
hideFrame = CGRect(x: SKMesurement.screenWidth - size.width, y: -20, width: size.width, height: size.height)
}
}

View File

@ -0,0 +1,81 @@
//
// SKCache.swift
// SKPhotoBrowser
//
// Created by Kevin Wolkober on 6/13/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
public class SKCache {
public static let sharedCache = SKCache()
public var imageCache: SKCacheable
init() {
self.imageCache = SKDefaultImageCache()
}
public func imageForKey(key: String) -> UIImage? {
guard let cache = imageCache as? SKImageCacheable else {
return nil
}
return cache.imageForKey(key)
}
public func setImage(image: UIImage, forKey key: String) {
guard let cache = imageCache as? SKImageCacheable else {
return
}
cache.setImage(image, forKey: key)
}
public func removeImageForKey(key: String) {
guard let cache = imageCache as? SKImageCacheable else {
return
}
cache.removeImageForKey(key)
}
public func imageForRequest(request: NSURLRequest) -> UIImage? {
guard let cache = imageCache as? SKRequestResponseCacheable else {
return nil
}
if let response = cache.cachedResponseForRequest(request) {
return UIImage(data: response.data)
}
return nil
}
public func setImageData(data: NSData, response: NSURLResponse, request: NSURLRequest) {
guard let cache = imageCache as? SKRequestResponseCacheable else {
return
}
let cachedResponse = NSCachedURLResponse(response: response, data: data)
cache.storeCachedResponse(cachedResponse, forRequest: request)
}
}
class SKDefaultImageCache: SKImageCacheable {
var cache: NSCache
init() {
cache = NSCache()
}
func imageForKey(key: String) -> UIImage? {
return cache.objectForKey(key) as? UIImage
}
func setImage(image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key)
}
func removeImageForKey(key: String) {
cache.removeObjectForKey(key)
}
}

View File

@ -0,0 +1,21 @@
//
// SKCacheable.swift
// SKPhotoBrowser
//
// Created by Kevin Wolkober on 6/13/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
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)
}
public protocol SKRequestResponseCacheable: SKCacheable {
func cachedResponseForRequest(request: NSURLRequest) -> NSCachedURLResponse?
func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest request: NSURLRequest)
}

View File

@ -9,14 +9,9 @@
import UIKit
public class SKCaptionView: UIView {
final let screenBound = UIScreen.mainScreen().bounds
private var screenWidth: CGFloat { return screenBound.size.width }
private var screenHeight: CGFloat { return screenBound.size.height }
private var photo: SKPhotoProtocol!
private var photo: SKPhotoProtocol?
private var photoLabel: UILabel!
private var photoLabelPadding: CGFloat = 10
private var fadeView: UIView = UIView()
private var gradientLayer = CAGradientLayer()
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
@ -33,36 +28,6 @@ public class SKCaptionView: UIView {
setup()
}
func setup() {
opaque = false
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
// setup background first
fadeView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(fadeView)
// add layer at fadeView
gradientLayer.colors = [UIColor(white: 0.0, alpha: 0.0).CGColor, UIColor(white: 0.0, alpha: 0.8).CGColor]
fadeView.layer.insertSublayer(gradientLayer, atIndex: 0)
photoLabel = UILabel(frame: CGRect(x: photoLabelPadding, y: 0,
width: bounds.size.width - (photoLabelPadding * 2), height: bounds.size.height))
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
photoLabel.opaque = false
photoLabel.backgroundColor = .clearColor()
photoLabel.textColor = .whiteColor()
photoLabel.textAlignment = .Center
photoLabel.lineBreakMode = .ByTruncatingTail
photoLabel.numberOfLines = 3
photoLabel.shadowColor = UIColor(white: 0.0, alpha: 0.5)
photoLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
photoLabel.font = UIFont.systemFontOfSize(17.0)
if let cap = photo.caption {
photoLabel.text = cap
}
addSubview(photoLabel)
}
public override func sizeThatFits(size: CGSize) -> CGSize {
guard let text = photoLabel.text else {
return CGSize.zero
@ -72,19 +37,39 @@ public class SKCaptionView: UIView {
}
let font: UIFont = photoLabel.font
let width: CGFloat = size.width - (photoLabelPadding * 2)
let width: CGFloat = size.width - photoLabelPadding * 2
let height: CGFloat = photoLabel.font.lineHeight * CGFloat(photoLabel.numberOfLines)
let attributedText = NSAttributedString(string: text, attributes: [NSFontAttributeName: font])
let textSize = attributedText.boundingRectWithSize(CGSize(width: width, height: height),
options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil).size
let textSize = attributedText.boundingRectWithSize(CGSize(width: width, height: height), options: .UsesLineFragmentOrigin, context: nil).size
return CGSize(width: textSize.width, height: textSize.height + photoLabelPadding * 2)
}
}
private extension SKCaptionView {
func setup() {
opaque = false
autoresizingMask = [.FlexibleWidth, .FlexibleTopMargin, .FlexibleRightMargin, .FlexibleLeftMargin]
// setup photoLabel
setupPhotoLabel()
}
public override func layoutSubviews() {
fadeView.frame = frame
gradientLayer.frame = frame
func setupPhotoLabel() {
photoLabel = UILabel(frame: CGRect(x: photoLabelPadding, y: 0, width: bounds.size.width - (photoLabelPadding * 2), height: bounds.size.height))
photoLabel.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
photoLabel.opaque = false
photoLabel.backgroundColor = .clearColor()
photoLabel.textColor = SKPhotoBrowserOptions.textAndIconColor
photoLabel.textAlignment = .Center
photoLabel.lineBreakMode = .ByTruncatingTail
photoLabel.numberOfLines = 3
photoLabel.shadowColor = UIColor(white: 0.0, alpha: 0.5)
photoLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
photoLabel.font = SKPhotoBrowserOptions.captionFont
photoLabel.text = photo?.caption
addSubview(photoLabel)
}
}

View File

@ -9,8 +9,8 @@
import UIKit
@objc protocol SKDetectingImageViewDelegate {
func handleImageViewSingleTap(view: UIImageView, touch: UITouch)
func handleImageViewDoubleTap(view: UIImageView, touch: UITouch)
func handleImageViewSingleTap(touchPoint: CGPoint)
func handleImageViewDoubleTap(touchPoint: CGPoint)
}
class SKDetectingImageView: UIImageView {
@ -18,29 +18,33 @@ class SKDetectingImageView: UIImageView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func handleDoubleTap(recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewDoubleTap(recognizer.locationInView(self))
}
func handleSingleTap(recognizer: UITapGestureRecognizer) {
delegate?.handleImageViewSingleTap(recognizer.locationInView(self))
}
}
private extension SKDetectingImageView {
func setup() {
userInteractionEnabled = true
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
let touch = touches.first!
switch touch.tapCount {
case 1 : handleSingleTap(touch)
case 2 : handleDoubleTap(touch)
default: break
}
nextResponder()
}
func handleSingleTap(touch: UITouch) {
delegate?.handleImageViewSingleTap(self, touch: touch)
}
func handleDoubleTap(touch: UITouch) {
delegate?.handleImageViewDoubleTap(self, touch: touch)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
doubleTap.numberOfTapsRequired = 2
addGestureRecognizer(doubleTap)
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
singleTap.requireGestureRecognizerToFail(doubleTap)
addGestureRecognizer(singleTap)
}
}

View File

@ -13,20 +13,23 @@ import UIKit
func handleDoubleTap(view: UIView, touch: UITouch)
}
class SKDetectingView: UIView {
weak var delegate: SKDetectingViewDelegate?
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesEnded(touches, withEvent: event)
defer {
nextResponder()
}
let touch = touches.first!
guard let touch = touches.first else {
return
}
switch touch.tapCount {
case 1 : handleSingleTap(touch)
case 2 : handleDoubleTap(touch)
default: break
}
nextResponder()
}
func handleSingleTap(touch: UITouch) {

View File

@ -9,15 +9,13 @@
import UIKit
class SKIndicatorView: UIActivityIndicatorView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
center = CGPoint(x: frame.width/2, y: frame.height/2)
activityIndicatorViewStyle = .WhiteLarge
center = CGPoint(x: frame.width / 2, y: frame.height / 2)
activityIndicatorViewStyle = SKPhotoBrowserOptions.backgroundColor.isEqual(UIColor.whiteColor()) ? .Gray : .WhiteLarge
}
}

View File

@ -0,0 +1,70 @@
//
// SKLocalPhoto.swift
// SKPhotoBrowser
//
// Created by Antoine Barrault on 13/04/2016.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
// MARK: - SKLocalPhoto
public class SKLocalPhoto: NSObject, SKPhotoProtocol {
public var underlyingImage: UIImage!
public var photoURL: String!
public var contentMode: UIViewContentMode = .ScaleToFill
public var shouldCachePhotoURLImage: Bool = false
public var caption: String!
public var index: Int = 0
override init() {
super.init()
}
convenience init(url: String) {
self.init()
photoURL = url
}
convenience init(url: String, holder: UIImage?) {
self.init()
photoURL = url
underlyingImage = holder
}
public func checkCache() {}
public func loadUnderlyingImageAndNotify() {
if underlyingImage != nil && photoURL == nil {
loadUnderlyingImageComplete()
}
if photoURL != nil {
// Fetch Image
if NSFileManager.defaultManager().fileExistsAtPath(photoURL) {
if let data = NSFileManager.defaultManager().contentsAtPath(photoURL) {
self.loadUnderlyingImageComplete()
if let image = UIImage(data: data) {
self.underlyingImage = image
self.loadUnderlyingImageComplete()
}
}
}
}
}
public func loadUnderlyingImageComplete() {
NSNotificationCenter.defaultCenter().postNotificationName(SKPHOTO_LOADING_DID_END_NOTIFICATION, object: self)
}
// MARK: - class func
public class func photoWithImageURL(url: String) -> SKLocalPhoto {
return SKLocalPhoto(url: url)
}
public class func photoWithImageURL(url: String, holder: UIImage?) -> SKLocalPhoto {
return SKLocalPhoto(url: url, holder: holder)
}
}

View File

@ -0,0 +1,30 @@
//
// SKMesurement.swift
// SKPhotoBrowser
//
// Created by on 2016/08/09.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
import UIKit
struct SKMesurement {
static let isPhone: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Phone
static let isPad: Bool = UIDevice.currentDevice().userInterfaceIdiom == .Pad
static var statusBarH: CGFloat {
return UIApplication.sharedApplication().statusBarFrame.height
}
static var screenHeight: CGFloat {
return UIScreen.mainScreen().bounds.height
}
static var screenWidth: CGFloat {
return UIScreen.mainScreen().bounds.width
}
static var screenScale: CGFloat {
return UIScreen.mainScreen().scale
}
static var screenRatio: CGFloat {
return screenWidth / screenHeight
}
}

View File

@ -0,0 +1,55 @@
//
// SKNavigationBar.swift
// SKPhotoBrowser
//
// Created by Григорий Уланов on 10.10.16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
class SKNavigationBar: UINavigationBar {
var showFrame: CGRect!
var hideFrame: CGRect!
private static let toolBarHeight: CGFloat = 64.0
private weak var browser: SKPhotoBrowser?
convenience init(browser: SKPhotoBrowser) {
self.init(frame: CGRect.zero)
self.browser = browser
translucent = false
tintColor = UIColor.whiteColor()
barTintColor = UIColor.blackColor()
titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
let navigationItem = UINavigationItem()
pushNavigationItem(navigationItem, animated: false)
}
func updateNavigationBar(currentPageIndex: Int) {
guard let browser = browser else { return }
if browser.numberOfPhotos > 1 {
self.topItem?.title = "\(currentPageIndex + 1) \(SKPhotoBrowserOptions.navigationBarCounterSepatator) \(browser.numberOfPhotos)"
} else {
self.topItem?.title = nil
}
}
func setNewFrame(rect: CGRect) {
self.frame = rect
showFrame = rect
hideFrame = CGRect(x: rect.origin.x, y: rect.origin.y - 20, width: rect.size.width, height: rect.size.height)
}
func updateFrame(parentSize: CGSize) {
let newRect = CGRect(x: 0, y: 0, width: parentSize.width, height: SKNavigationBar.toolBarHeight)
setNewFrame(newRect)
}
}

View File

@ -0,0 +1,238 @@
//
// SKPagingScrollView.swift
// SKPhotoBrowser
//
// Created by on 2016/08/18.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
class SKPagingScrollView: UIScrollView {
let pageIndexTagOffset: Int = 1000
let sideMargin: CGFloat = 10
private var visiblePages = [SKZoomingScrollView]()
private var recycledPages = [SKZoomingScrollView]()
private weak var browser: SKPhotoBrowser?
var numberOfPhotos: Int {
return browser?.photos.count ?? 0
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
pagingEnabled = true
showsHorizontalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
showsVerticalScrollIndicator = SKPhotoBrowserOptions.displayHorizontalScrollIndicator
}
convenience init(frame: CGRect, browser: SKPhotoBrowser) {
self.init(frame: frame)
self.browser = browser
updateFrame(bounds, currentPageIndex: browser.currentPageIndex)
}
func reload() {
visiblePages.forEach({$0.removeFromSuperview()})
visiblePages.removeAll()
recycledPages.removeAll()
}
func loadAdjacentPhotosIfNecessary(photo: SKPhotoProtocol, currentPageIndex: Int) {
guard let browser = browser, page = pageDisplayingAtPhoto(photo) else {
return
}
let pageIndex = (page.tag - pageIndexTagOffset)
if currentPageIndex == pageIndex {
// Previous
if pageIndex > 0 {
let previousPhoto = browser.photos[pageIndex - 1]
if previousPhoto.underlyingImage == nil {
previousPhoto.loadUnderlyingImageAndNotify()
}
}
// Next
if pageIndex < numberOfPhotos - 1 {
let nextPhoto = browser.photos[pageIndex + 1]
if nextPhoto.underlyingImage == nil {
nextPhoto.loadUnderlyingImageAndNotify()
}
}
}
}
func deleteImage() {
// index equals 0 because when we slide between photos delete button is hidden and user cannot to touch on delete button. And visible pages number equals 0
if numberOfPhotos > 0 {
visiblePages[0].captionView?.removeFromSuperview()
}
}
func animate(frame: CGRect) {
setContentOffset(CGPoint(x: frame.origin.x - sideMargin, y: 0), animated: true)
}
func updateFrame(bounds: CGRect, currentPageIndex: Int) {
var frame = bounds
frame.origin.x -= sideMargin
frame.size.width += (2 * sideMargin)
self.frame = frame
if visiblePages.count > 0 {
for page in visiblePages {
let pageIndex = page.tag - pageIndexTagOffset
page.frame = frameForPageAtIndex(pageIndex)
page.setMaxMinZoomScalesForCurrentBounds()
if page.captionView != nil {
page.captionView.frame = frameForCaptionView(page.captionView, index: pageIndex)
}
}
}
updateContentSize()
updateContentOffset(currentPageIndex)
}
func updateContentSize() {
contentSize = CGSize(width: bounds.size.width * CGFloat(numberOfPhotos), height: bounds.size.height)
}
func updateContentOffset(index: Int) {
let pageWidth = bounds.size.width
let newOffset = CGFloat(index) * pageWidth
contentOffset = CGPoint(x: newOffset, y: 0)
}
func tilePages() {
guard let browser = browser else { return }
let firstIndex: Int = getFirstIndex()
let lastIndex: Int = getLastIndex()
visiblePages
.filter({ $0.tag - pageIndexTagOffset < firstIndex || $0.tag - pageIndexTagOffset < lastIndex })
.forEach { page in
recycledPages.append(page)
page.prepareForReuse()
page.removeFromSuperview()
}
let visibleSet: Set<SKZoomingScrollView> = Set(visiblePages)
let visibleSetWithoutRecycled: Set<SKZoomingScrollView> = visibleSet.subtract(recycledPages)
visiblePages = Array(visibleSetWithoutRecycled)
while recycledPages.count > 2 {
recycledPages.removeFirst()
}
for index: Int in firstIndex...lastIndex {
if visiblePages.filter({ $0.tag - pageIndexTagOffset == index }).count > 0 {
continue
}
let page: SKZoomingScrollView = SKZoomingScrollView(frame: frame, browser: browser)
page.frame = frameForPageAtIndex(index)
page.tag = index + pageIndexTagOffset
page.photo = browser.photos[index]
visiblePages.append(page)
addSubview(page)
// if exists caption, insert
if let captionView: SKCaptionView = createCaptionView(index) {
captionView.frame = frameForCaptionView(captionView, index: index)
captionView.alpha = browser.areControlsHidden() ? 0 : 1
addSubview(captionView)
// ref val for control
page.captionView = captionView
}
}
}
func frameForCaptionView(captionView: SKCaptionView, index: Int) -> CGRect {
let pageFrame = frameForPageAtIndex(index)
let captionSize = captionView.sizeThatFits(CGSize(width: pageFrame.size.width, height: 0))
let navHeight = browser?.navigationController?.navigationBar.frame.size.height ?? 44
return CGRect(x: pageFrame.origin.x, y: pageFrame.size.height - captionSize.height - navHeight,
width: pageFrame.size.width, height: captionSize.height)
}
func pageDisplayedAtIndex(index: Int) -> SKZoomingScrollView? {
for page in visiblePages {
if page.tag - pageIndexTagOffset == index {
return page
}
}
return nil
}
func pageDisplayingAtPhoto(photo: SKPhotoProtocol) -> SKZoomingScrollView? {
for page in visiblePages {
if page.photo === photo {
return page
}
}
return nil
}
func getCaptionViews() -> Set<SKCaptionView> {
var captionViews = Set<SKCaptionView>()
visiblePages
.filter({ $0.captionView != nil })
.forEach {
captionViews.insert($0.captionView)
}
return captionViews
}
}
private extension SKPagingScrollView {
func frameForPageAtIndex(index: Int) -> CGRect {
var pageFrame = bounds
pageFrame.size.width -= (2 * 10)
pageFrame.origin.x = (bounds.size.width * CGFloat(index)) + sideMargin
return pageFrame
}
func createCaptionView(index: Int) -> SKCaptionView? {
guard let photo = browser?.photoAtIndex(index) where photo.caption != nil else {
return nil
}
return SKCaptionView(photo: photo)
}
func getFirstIndex() -> Int {
let firstIndex = Int(floor((bounds.minX + sideMargin * 2) / bounds.width))
if firstIndex < 0 {
return 0
}
if firstIndex > numberOfPhotos - 1 {
return numberOfPhotos - 1
}
return firstIndex
}
func getLastIndex() -> Int {
let lastIndex = Int(floor((bounds.maxX - sideMargin * 2 - 1) / bounds.width))
if lastIndex < 0 {
return 0
}
if lastIndex > numberOfPhotos - 1 {
return numberOfPhotos - 1
}
return lastIndex
}
}

View File

@ -8,10 +8,11 @@
import UIKit
public protocol SKPhotoProtocol: NSObjectProtocol {
@objc public protocol SKPhotoProtocol: NSObjectProtocol {
var underlyingImage: UIImage! { get }
var caption: String! { get }
var index: Int? { get set}
var index: Int { get set}
var contentMode: UIViewContentMode { get set }
func loadUnderlyingImageAndNotify()
func checkCache()
}
@ -21,9 +22,10 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
public var underlyingImage: UIImage!
public var photoURL: String!
public var contentMode: UIViewContentMode = .ScaleAspectFill
public var shouldCachePhotoURLImage: Bool = false
public var caption: String!
public var index: Int?
public var index: Int = 0
override init() {
super.init()
@ -39,33 +41,60 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
photoURL = url
}
convenience init(url: String, holder: UIImage?) {
self.init()
photoURL = url
underlyingImage = holder
}
public func checkCache() {
if photoURL != nil && shouldCachePhotoURLImage {
if let img = UIImage.sharedSKPhotoCache().objectForKey(photoURL) as? UIImage {
guard let photoURL = photoURL else {
return
}
guard shouldCachePhotoURLImage else {
return
}
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
let request = NSURLRequest(URL: NSURL(string: photoURL)!)
if let img = SKCache.sharedCache.imageForRequest(request) {
underlyingImage = img
}
} else {
if let img = SKCache.sharedCache.imageForKey(photoURL) {
underlyingImage = img
}
}
}
public func loadUnderlyingImageAndNotify() {
if underlyingImage != nil {
loadUnderlyingImageComplete()
return
}
if photoURL != nil {
// Fetch Image
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
if let nsURL = NSURL(string: photoURL) {
session.dataTaskWithURL(nsURL, completionHandler: { [weak self](response: NSData?, data: NSURLResponse?, error: NSError?) in
var task: NSURLSessionDataTask!
task = session.dataTaskWithURL(nsURL, completionHandler: { [weak self](response: NSData?, data: NSURLResponse?, error: NSError?) in
if let _self = self {
if error != nil {
dispatch_async(dispatch_get_main_queue()) {
_self.loadUnderlyingImageComplete()
}
}
if let res = response, let image = UIImage(data: res) {
if let res = response, image = UIImage(data: res) {
if _self.shouldCachePhotoURLImage {
UIImage.sharedSKPhotoCache().setObject(image, forKey: _self.photoURL)
if SKCache.sharedCache.imageCache is SKRequestResponseCacheable {
SKCache.sharedCache.setImageData(response!, response: data!, request: task.originalRequest!)
} else {
SKCache.sharedCache.setImage(image, forKey: _self.photoURL)
}
}
dispatch_async(dispatch_get_main_queue()) {
_self.underlyingImage = image
@ -74,7 +103,8 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
}
session.finishTasksAndInvalidate()
}
}).resume()
})
task.resume()
}
}
}
@ -83,25 +113,20 @@ public class SKPhoto: NSObject, SKPhotoProtocol {
NSNotificationCenter.defaultCenter().postNotificationName(SKPHOTO_LOADING_DID_END_NOTIFICATION, object: self)
}
// MARK: - class func
public class func photoWithImage(image: UIImage) -> SKPhoto {
return SKPhoto(image: image)
}
public class func photoWithImageURL(url: String) -> SKPhoto {
return SKPhoto(url: url)
}
}
// MARK: - extension UIImage
public extension UIImage {
private class func sharedSKPhotoCache() -> NSCache! {
struct StaticSharedSKPhotoCache {
static var sharedCache: NSCache? = nil
static var onceToken: dispatch_once_t = 0
}
dispatch_once(&StaticSharedSKPhotoCache.onceToken) {
StaticSharedSKPhotoCache.sharedCache = NSCache()
}
return StaticSharedSKPhotoCache.sharedCache!
// MARK: - Static Function
extension SKPhoto {
public static func photoWithImage(image: UIImage) -> SKPhoto {
return SKPhoto(image: image)
}
public static func photoWithImageURL(url: String) -> SKPhoto {
return SKPhoto(url: url)
}
public static func photoWithImageURL(url: String, holder: UIImage?) -> SKPhoto {
return SKPhoto(url: url, holder: holder)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
//
// SKPhotoBrowserDelegate.swift
// SKPhotoBrowser
//
// Created by on 2016/08/09.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
@objc public protocol SKPhotoBrowserDelegate {
/**
Tells the delegate that the browser started displaying a new photo
- Parameter index: the index of the new photo
*/
optional func didShowPhotoAtIndex(index: Int)
/**
Tells the delegate the browser will start to dismiss
- Parameter index: the index of the current photo
*/
optional func willDismissAtPageIndex(index: Int)
/**
Tells the delegate that the browser will start showing the `UIActionSheet`
- Parameter photoIndex: the index of the current photo
*/
optional func willShowActionSheet(photoIndex: Int)
/**
Tells the delegate that the browser has been dismissed
- Parameter index: the index of the current photo
*/
optional func didDismissAtPageIndex(index: Int)
/**
Tells the delegate that the browser did dismiss the UIActionSheet
- Parameter buttonIndex: the index of the pressed button
- Parameter photoIndex: the index of the current photo
*/
optional func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int)
/**
Tells the delegate that the browser did scroll to index
- Parameter index: the index of the photo where the user had scroll
*/
optional func didScrollToIndex(index: Int)
/**
Tells the delegate the user removed a photo, when implementing this call, be sure to call reload to finish the deletion process
- Parameter browser: reference to the calling SKPhotoBrowser
- Parameter index: the index of the removed photo
- Parameter reload: function that needs to be called after finishing syncing up
*/
optional func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void))
/**
Asks the delegate for the view for a certain photo. Needed to detemine the animation when presenting/closing the browser.
- Parameter browser: reference to the calling SKPhotoBrowser
- Parameter index: the index of the removed photo
- Returns: the view to animate to
*/
optional func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView?
}

View File

@ -0,0 +1,46 @@
//
// SKPhotoBrowserOptions.swift
// SKPhotoBrowser
//
// Created by on 2016/08/18.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
public struct SKPhotoBrowserOptions {
public static var displayStatusbar: Bool = false
public static var displayAction: Bool = true
public static var shareExtraCaption: String? = nil
public static var actionButtonTitles: [String]?
public static var displayToolbar: Bool = true
public static var displayCounterLabel: Bool = true
public static var displayBackAndForwardButton: Bool = true
public static var disableVerticalSwipe: Bool = false
public static var displayCloseButton: Bool = true
public static var displayNavigationBar: Bool = true
public static var displayDeleteButton: Bool = false
public static var displayHorizontalScrollIndicator: Bool = true
public static var displayVerticalScrollIndicator: Bool = true
public static var bounceAnimation: Bool = false
public static var enableZoomBlackArea: Bool = true
public static var enableSingleTapDismiss: Bool = false
public static var backgroundColor = UIColor.blackColor()
public static var textAndIconColor = UIColor.whiteColor()
public static var toolbarTextShadowColor = UIColor.darkTextColor()
public static var navigationBarCounterSepatator: String = "из"
public static var toolbarFont = UIFont(name: "Helvetica", size: 16.0)
public static var captionFont = UIFont.systemFontOfSize(17.0)
// FIXED: Scrolling performance slowed #145
// public static var imagePaddingX: CGFloat = 0
// public static var imagePaddingY: CGFloat = 0
}

View File

@ -0,0 +1,165 @@
//
// SKToolbar.swift
// SKPhotoBrowser
//
// Created by on 2016/08/12.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import Foundation
// helpers which often used
private let bundle = NSBundle(forClass: SKPhotoBrowser.self)
class SKToolbar: UIToolbar {
var toolCounterLabel: UILabel!
var toolCounterButton: UIBarButtonItem!
var toolPreviousButton: UIBarButtonItem!
var toolNextButton: UIBarButtonItem!
var toolActionButton: UIBarButtonItem!
private weak var browser: SKPhotoBrowser?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(frame: CGRect, browser: SKPhotoBrowser) {
self.init(frame: frame)
self.browser = browser
setupApperance()
setupPreviousButton()
setupNextButton()
setupCounterLabel()
setupActionButton()
setupToolbar()
}
func updateToolbar(currentPageIndex: Int) {
guard let browser = browser else { return }
if browser.numberOfPhotos > 1 {
toolCounterLabel.text = "\(currentPageIndex + 1) / \(browser.numberOfPhotos)"
} else {
toolCounterLabel.text = nil
}
toolPreviousButton.enabled = (currentPageIndex > 0)
toolNextButton.enabled = (currentPageIndex < browser.numberOfPhotos - 1)
}
}
private extension SKToolbar {
func setupApperance() {
backgroundColor = .clearColor()
clipsToBounds = true
translucent = true
setBackgroundImage(UIImage(), forToolbarPosition: .Any, barMetrics: .Default)
// toolbar
if !SKPhotoBrowserOptions.displayToolbar {
hidden = true
}
}
func setupToolbar() {
guard let browser = browser else { return }
let flexSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: self, action: nil)
var items = [UIBarButtonItem]()
items.append(flexSpace)
if browser.numberOfPhotos > 1 && SKPhotoBrowserOptions.displayBackAndForwardButton {
items.append(toolPreviousButton)
}
if SKPhotoBrowserOptions.displayCounterLabel {
items.append(flexSpace)
items.append(toolCounterButton)
items.append(flexSpace)
} else {
items.append(flexSpace)
}
if browser.numberOfPhotos > 1 && SKPhotoBrowserOptions.displayBackAndForwardButton {
items.append(toolNextButton)
}
items.append(flexSpace)
if SKPhotoBrowserOptions.displayAction {
items.append(toolActionButton)
}
setItems(items, animated: false)
}
func setupPreviousButton() {
let previousBtn = SKPreviousButton(frame: frame)
previousBtn.addTarget(browser, action: #selector(SKPhotoBrowser.gotoPreviousPage), forControlEvents: .TouchUpInside)
toolPreviousButton = UIBarButtonItem(customView: previousBtn)
}
func setupNextButton() {
let nextBtn = SKNextButton(frame: frame)
nextBtn.addTarget(browser, action: #selector(SKPhotoBrowser.gotoNextPage), forControlEvents: .TouchUpInside)
toolNextButton = UIBarButtonItem(customView: nextBtn)
}
func setupCounterLabel() {
toolCounterLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 95, height: 40))
toolCounterLabel.textAlignment = .Center
toolCounterLabel.backgroundColor = .clearColor()
toolCounterLabel.font = SKPhotoBrowserOptions.toolbarFont
toolCounterLabel.textColor = SKPhotoBrowserOptions.textAndIconColor
toolCounterLabel.shadowColor = SKPhotoBrowserOptions.toolbarTextShadowColor
toolCounterLabel.shadowOffset = CGSize(width: 0.0, height: 1.0)
toolCounterButton = UIBarButtonItem(customView: toolCounterLabel)
}
func setupActionButton() {
toolActionButton = UIBarButtonItem(barButtonSystemItem: .Action, target: browser, action: #selector(SKPhotoBrowser.actionButtonPressed))
toolActionButton.tintColor = SKPhotoBrowserOptions.textAndIconColor
}
}
class SKToolbarButton: UIButton {
let insets: UIEdgeInsets = UIEdgeInsets(top: 13.25, left: 17.25, bottom: 13.25, right: 17.25)
func setup(imageName: String) {
backgroundColor = .clearColor()
tintColor = SKPhotoBrowserOptions.textAndIconColor
imageEdgeInsets = insets
translatesAutoresizingMaskIntoConstraints = true
autoresizingMask = [.FlexibleBottomMargin, .FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin]
contentMode = .Center
let image = UIImage(named: "SKPhotoBrowser.bundle/images/\(imageName)",
inBundle: bundle, compatibleWithTraitCollection: nil)?.imageWithRenderingMode(.AlwaysTemplate) ?? UIImage()
setImage(image, forState: .Normal)
}
}
class SKPreviousButton: SKToolbarButton {
let imageName = "btn_common_back_wh"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
setup(imageName)
}
}
class SKNextButton: SKToolbarButton {
let imageName = "btn_common_forward_wh"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
setup(imageName)
}
}

View File

@ -8,19 +8,20 @@
import UIKit
public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectingViewDelegate, SKDetectingImageViewDelegate {
public class SKZoomingScrollView: UIScrollView {
var captionView: SKCaptionView!
var photo: SKPhotoProtocol! {
didSet {
photoImageView.image = nil
displayImage()
if photo != nil {
displayImage(complete: false)
}
}
}
private weak var photoBrowser: SKPhotoBrowser!
private(set) var photoImageView: SKDetectingImageView!
private weak var photoBrowser: SKPhotoBrowser?
private var tapView: SKDetectingView!
private var photoImageView: SKDetectingImageView!
private var indicatorView: SKIndicatorView!
required public init?(coder aDecoder: NSCoder) {
@ -39,19 +40,22 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
setup()
}
deinit {
photoBrowser = nil
}
func setup() {
// tap
tapView = SKDetectingView(frame: bounds)
tapView.delegate = self
tapView.backgroundColor = UIColor.clearColor()
tapView.backgroundColor = .clearColor()
tapView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
addSubview(tapView)
// image
photoImageView = SKDetectingImageView(frame: frame)
photoImageView.delegate = self
photoImageView.contentMode = .ScaleAspectFill
photoImageView.contentMode = .Bottom
photoImageView.backgroundColor = .clearColor()
addSubview(photoImageView)
@ -69,9 +73,10 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
}
// MARK: - override
public override func layoutSubviews() {
tapView.frame = bounds
indicatorView.frame = bounds
super.layoutSubviews()
@ -98,7 +103,6 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
}
public func setMaxMinZoomScalesForCurrentBounds() {
maximumZoomScale = 1
minimumZoomScale = 1
zoomScale = 1
@ -113,31 +117,39 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
let xScale = boundsSize.width / imageSize.width
let yScale = boundsSize.height / imageSize.height
let minScale: CGFloat = min(xScale, yScale)
var maxScale: CGFloat!
let deviceScreenWidth = UIScreen.mainScreen().bounds.width
var maxScale: CGFloat = 1.0
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 {
if deviceScreenWidth / 2 > photoImageView.frame.width {
maxScale = 3.0
// I think that we should to get coefficient between device screen width and image width and assign it to maxScale. I made two mode that we will get the same result for different device orientations.
if UIApplication.sharedApplication().statusBarOrientation.isPortrait {
maxScale = deviceScreenHeight / photoImageView.frame.width
} else {
maxScale = 2.0
maxScale = deviceScreenWidth / photoImageView.frame.width
}
} else {
} else if photoImageView.frame.width > deviceScreenWidth {
maxScale = 1.0
} else {
// here if photoImageView.frame.width == deviceScreenWidth
maxScale = 2.5
}
maximumZoomScale = maxScale
minimumZoomScale = minScale
zoomScale = minScale
// on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
// maximum zoom scale to 0.5
maxScale = maxScale / UIScreen.mainScreen().scale
// After changing this value, we still never use more
/*
maxScale = maxScale / scale
if maxScale < minScale {
maxScale = minScale * 2
}
*/
// reset position
photoImageView.frame = CGRect(x: 0, y: 0, width: photoImageView.frame.size.width, height: photoImageView.frame.size.height)
@ -146,25 +158,50 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
public func prepareForReuse() {
photo = nil
if captionView != nil {
captionView.removeFromSuperview()
captionView = nil
}
}
// MARK: - image
public func displayImage() {
public func displayImage(complete flag: Bool) {
// reset scale
maximumZoomScale = 1
minimumZoomScale = 1
zoomScale = 1
contentSize = CGSize.zero
let image = photoBrowser.imageForPhoto(photo)
if let image = image {
// indicator
if !flag {
if photo.underlyingImage == nil {
indicatorView.startAnimating()
}
photo.loadUnderlyingImageAndNotify()
} else {
indicatorView.stopAnimating()
}
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
photoImageViewFrame.size = image.size
@ -174,11 +211,7 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
contentSize = photoImageViewFrame.size
setMaxMinZoomScalesForCurrentBounds()
} else {
// indicator
indicatorView.startAnimating()
}
setNeedsLayout()
}
@ -186,63 +219,97 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
indicatorView.stopAnimating()
}
// MARK: - handle tap
public func handleDoubleTap(touchPoint: CGPoint) {
NSObject.cancelPreviousPerformRequestsWithTarget(photoBrowser)
if let photoBrowser = photoBrowser {
NSObject.cancelPreviousPerformRequestsWithTarget(photoBrowser)
}
if zoomScale > minimumZoomScale {
// zoom out
setZoomScale(minimumZoomScale, animated: true)
} else {
// zoom in
var newZoom: CGFloat = zoomScale * 3.13
// I think that the result should be the same after double touch or pinch
/* var newZoom: CGFloat = zoomScale * 3.13
if newZoom >= maximumZoomScale {
newZoom = maximumZoomScale
}
zoomToRect(zoomRectForScrollViewWith(newZoom, touchPoint:touchPoint), animated:true)
*/
let zoomRect = zoomRectForScrollViewWith(maximumZoomScale, touchPoint: touchPoint)
zoomToRect(zoomRect, animated: true)
}
// delay control
photoBrowser.hideControlsAfterDelay()
photoBrowser?.hideControlsAfterDelay()
}
public func zoomRectForScrollViewWith(scale: CGFloat, touchPoint: CGPoint) -> CGRect {
let w = frame.size.width / scale
let h = frame.size.height / scale
let x = touchPoint.x - (w / 2.0)
let y = touchPoint.y - (h / 2.0)
return CGRect(x: x, y: y, width: w, height: h)
}
// MARK: - UIScrollViewDelegate
}
// MARK: - UIScrollViewDelegate
extension SKZoomingScrollView: UIScrollViewDelegate {
public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return photoImageView
}
public func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) {
photoBrowser.cancelControlHiding()
photoBrowser?.cancelControlHiding()
}
public func scrollViewDidZoom(scrollView: UIScrollView) {
setNeedsLayout()
layoutIfNeeded()
}
// MARK: - SKDetectingViewDelegate
}
// MARK: - SKDetectingImageViewDelegate
extension SKZoomingScrollView: SKDetectingViewDelegate {
func handleSingleTap(view: UIView, touch: UITouch) {
photoBrowser.toggleControls()
guard let browser = photoBrowser else {
return
}
guard SKPhotoBrowserOptions.enableZoomBlackArea == true else {
return
}
if browser.areControlsHidden() == false && SKPhotoBrowserOptions.enableSingleTapDismiss == true {
browser.determineAndClose()
} else {
browser.toggleControls()
}
}
func handleDoubleTap(view: UIView, touch: UITouch) {
let needPoint = getViewFramePercent(view, touch: touch)
handleDoubleTap(needPoint)
if SKPhotoBrowserOptions.enableZoomBlackArea == true {
let needPoint = getViewFramePercent(view, touch: touch)
handleDoubleTap(needPoint)
}
}
}
// MARK: - SKDetectingImageViewDelegate
extension SKZoomingScrollView: SKDetectingImageViewDelegate {
func handleImageViewSingleTap(touchPoint: CGPoint) {
guard let browser = photoBrowser else {
return
}
if SKPhotoBrowserOptions.enableSingleTapDismiss {
browser.determineAndClose()
} else {
browser.toggleControls()
}
}
private func getViewFramePercent(view: UIView, touch: UITouch) -> CGPoint {
func handleImageViewDoubleTap(touchPoint: CGPoint) {
handleDoubleTap(touchPoint)
}
}
private extension SKZoomingScrollView {
func getViewFramePercent(view: UIView, touch: UITouch) -> CGPoint {
let oneWidthViewPercent = view.bounds.width / 100
let viewTouchPoint = touch.locationInView(view)
let viewWidthTouch = viewTouchPoint.x
@ -263,12 +330,12 @@ public class SKZoomingScrollView: UIScrollView, UIScrollViewDelegate, SKDetectin
return allPoint
}
// MARK: - SKDetectingImageViewDelegate
func handleImageViewSingleTap(view: UIImageView, touch: UITouch) {
photoBrowser.toggleControls()
func zoomRectForScrollViewWith(scale: CGFloat, touchPoint: CGPoint) -> CGRect {
let w = frame.size.width / scale
let h = frame.size.height / scale
let x = touchPoint.x - (h / max(UIScreen.mainScreen().scale, 2.0))
let y = touchPoint.y - (w / max(UIScreen.mainScreen().scale, 2.0))
return CGRect(x: x, y: y, width: w, height: h)
}
func handleImageViewDoubleTap(view: UIImageView, touch: UITouch) {
handleDoubleTap(touch.locationInView(view))
}
}
}

View File

@ -0,0 +1,82 @@
//
// UIImage+Rotation.swift
// SKPhotoBrowser
//
// Created by K Rummler on 15/03/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
extension UIImage {
func rotateImageByOrientation() -> UIImage {
// No-op if the orientation is already correct
guard self.imageOrientation != .Up else {
return self
}
let transform = calculateAffineTransform()
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
let ctx = CGBitmapContextCreate(nil, Int(self.size.width), Int(self.size.height),
CGImageGetBitsPerComponent(self.CGImage!), 0,
CGImageGetColorSpace(self.CGImage!)!,
CGImageGetBitmapInfo(self.CGImage!).rawValue)
CGContextConcatCTM(ctx!, transform)
switch self.imageOrientation {
case .Left, .LeftMirrored, .Right, .RightMirrored:
CGContextDrawImage(ctx!, CGRect(x: 0, y: 0, width: size.height, height: size.width), self.CGImage!)
default:
CGContextDrawImage(ctx!, CGRect(x: 0, y: 0, width: size.width, height: size.height), self.CGImage!)
}
// And now we just create a new UIImage from the drawing context
if let cgImage = CGBitmapContextCreateImage(ctx!) {
return UIImage(CGImage: cgImage)
} else {
return self
}
}
private func calculateAffineTransform() -> CGAffineTransform {
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
var transform = CGAffineTransformIdentity
switch self.imageOrientation {
case .Down, .DownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
case .Left, .LeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
case .Right, .RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
default:
break
}
switch self.imageOrientation {
case .UpMirrored, .DownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0)
transform = CGAffineTransformScale(transform, -1, 1)
case .LeftMirrored, .RightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0)
transform = CGAffineTransformScale(transform, -1, 1)
default:
break
}
return transform
}
}

View File

@ -0,0 +1,21 @@
//
// UIView+Radius.swift
// SKPhotoBrowser
//
// Created by K Rummler on 15/03/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
extension UIView {
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.addAnimation(animation, forKey: "cornerRadius")
self.layer.cornerRadius = to
}
}

View File

@ -0,0 +1,5 @@
platform :ios, '8.0'
use_frameworks!
pod 'SDWebImage', '~>3.8'

View File

@ -0,0 +1,12 @@
PODS:
- SDWebImage (3.8.1):
- SDWebImage/Core (= 3.8.1)
- SDWebImage/Core (3.8.1)
DEPENDENCIES:
- SDWebImage (~> 3.8)
SPEC CHECKSUMS:
SDWebImage: 35f9627a3e44b4f292a8a8ce6a531fa488239b91
COCOAPODS: 0.39.0

View File

@ -7,24 +7,28 @@
objects = {
/* Begin PBXBuildFile section */
212705891C92C69C00466223 /* FromCameraRollViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212705881C92C69C00466223 /* FromCameraRollViewController.swift */; };
8909B55B1BC792150060A053 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55A1BC792150060A053 /* AppDelegate.swift */; };
8909B55D1BC792150060A053 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55C1BC792150060A053 /* ViewController.swift */; };
8909B55D1BC792150060A053 /* FromLocalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8909B55C1BC792150060A053 /* FromLocalViewController.swift */; };
8909B5601BC792150060A053 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8909B55E1BC792150060A053 /* Main.storyboard */; };
8909B5621BC792150060A053 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5611BC792150060A053 /* Assets.xcassets */; };
8909B5651BC792150060A053 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5631BC792150060A053 /* LaunchScreen.storyboard */; };
8909B5731BC792AF0060A053 /* SKPhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5711BC792570060A053 /* SKPhotoBrowser.framework */; };
8909B5811BC792DC0060A053 /* image0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5751BC792DC0060A053 /* image0.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5821BC792DC0060A053 /* image1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5761BC792DC0060A053 /* image1.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5831BC792DC0060A053 /* image10.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5771BC792DC0060A053 /* image10.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5841BC792DC0060A053 /* image12.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5781BC792DC0060A053 /* image12.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5851BC792DC0060A053 /* image2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5791BC792DC0060A053 /* image2.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5861BC792DC0060A053 /* image3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57A1BC792DC0060A053 /* image3.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5871BC792DC0060A053 /* image4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57B1BC792DC0060A053 /* image4.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5881BC792DC0060A053 /* image5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57C1BC792DC0060A053 /* image5.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5891BC792DC0060A053 /* image6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57D1BC792DC0060A053 /* image6.jpg */; settings = {ASSET_TAGS = (); }; };
8909B58A1BC792DC0060A053 /* image7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57E1BC792DC0060A053 /* image7.jpg */; settings = {ASSET_TAGS = (); }; };
8909B58B1BC792DC0060A053 /* image8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57F1BC792DC0060A053 /* image8.jpg */; settings = {ASSET_TAGS = (); }; };
8909B58C1BC792DC0060A053 /* image9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5801BC792DC0060A053 /* image9.jpg */; settings = {ASSET_TAGS = (); }; };
8909B5811BC792DC0060A053 /* image0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5751BC792DC0060A053 /* image0.jpg */; };
8909B5821BC792DC0060A053 /* image1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5761BC792DC0060A053 /* image1.jpg */; };
8909B5831BC792DC0060A053 /* image10.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5771BC792DC0060A053 /* image10.jpg */; };
8909B5841BC792DC0060A053 /* image12.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5781BC792DC0060A053 /* image12.jpg */; };
8909B5851BC792DC0060A053 /* image2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5791BC792DC0060A053 /* image2.jpg */; };
8909B5861BC792DC0060A053 /* image3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57A1BC792DC0060A053 /* image3.jpg */; };
8909B5871BC792DC0060A053 /* image4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57B1BC792DC0060A053 /* image4.jpg */; };
8909B5881BC792DC0060A053 /* image5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57C1BC792DC0060A053 /* image5.jpg */; };
8909B5891BC792DC0060A053 /* image6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57D1BC792DC0060A053 /* image6.jpg */; };
8909B58A1BC792DC0060A053 /* image7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57E1BC792DC0060A053 /* image7.jpg */; };
8909B58B1BC792DC0060A053 /* image8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B57F1BC792DC0060A053 /* image8.jpg */; };
8909B58C1BC792DC0060A053 /* image9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 8909B5801BC792DC0060A053 /* image9.jpg */; };
89FC5DBB1D54A04900F1BE52 /* FromWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */; };
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5711BC792570060A053 /* SKPhotoBrowser.framework */; };
A6A7B7811C9578E30025AC07 /* SKPhotoBrowser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8909B5711BC792570060A053 /* SKPhotoBrowser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C2188E912BB106233AA4AB2 /* Pods.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -35,12 +39,42 @@
remoteGlobalIDString = 8909B5301BC791280060A053;
remoteInfo = SKPhotoBrowser;
};
89FC5DBD1D54A04A00F1BE52 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A64B89331CB04222000071B9;
remoteInfo = SKPhotoBrowserTests;
};
A6A7B7821C9578E30025AC07 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8909B52F1BC791280060A053;
remoteInfo = SKPhotoBrowser;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
A6A7B7841C9578E30025AC07 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
A6A7B7811C9578E30025AC07 /* SKPhotoBrowser.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
212705881C92C69C00466223 /* FromCameraRollViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromCameraRollViewController.swift; sourceTree = "<group>"; };
7C2188E912BB106233AA4AB2 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8909B5571BC792150060A053 /* SKPhotoBrowserExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SKPhotoBrowserExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
8909B55A1BC792150060A053 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
8909B55C1BC792150060A053 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
8909B55C1BC792150060A053 /* FromLocalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FromLocalViewController.swift; sourceTree = "<group>"; };
8909B55F1BC792150060A053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
8909B5611BC792150060A053 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
8909B5641BC792150060A053 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@ -58,6 +92,9 @@
8909B57E1BC792DC0060A053 /* image7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image7.jpg; sourceTree = "<group>"; };
8909B57F1BC792DC0060A053 /* image8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image8.jpg; sourceTree = "<group>"; };
8909B5801BC792DC0060A053 /* image9.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = image9.jpg; sourceTree = "<group>"; };
89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromWebViewController.swift; sourceTree = "<group>"; };
B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -65,7 +102,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8909B5731BC792AF0060A053 /* SKPhotoBrowser.framework in Frameworks */,
A6A7B7801C9578E30025AC07 /* SKPhotoBrowser.framework in Frameworks */,
DDFD24AF2FBA5A60984D378D /* Pods.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -78,6 +116,8 @@
8909B56C1BC792570060A053 /* SKPhotoBrowser.xcodeproj */,
8909B5591BC792150060A053 /* SKPhotoBrowserExample */,
8909B5581BC792150060A053 /* Products */,
D1D8ECD45B290372235B8168 /* Pods */,
C646D2C27A5B9CCC003A9BD1 /* Frameworks */,
);
sourceTree = "<group>";
};
@ -93,12 +133,10 @@
isa = PBXGroup;
children = (
8909B55A1BC792150060A053 /* AppDelegate.swift */,
8909B55C1BC792150060A053 /* ViewController.swift */,
8909B55E1BC792150060A053 /* Main.storyboard */,
8909B5611BC792150060A053 /* Assets.xcassets */,
8909B5631BC792150060A053 /* LaunchScreen.storyboard */,
8909B5661BC792150060A053 /* Info.plist */,
8909B5741BC792DC0060A053 /* SampleImages */,
A6EBAD761CAFB87A00F80595 /* Controllers */,
A6EBAD771CAFB89300F80595 /* UI */,
A6EBAD781CAFB8A500F80595 /* Resource */,
A6EBAD791CAFB96C00F80595 /* Info */,
);
path = SKPhotoBrowserExample;
sourceTree = "<group>";
@ -107,6 +145,7 @@
isa = PBXGroup;
children = (
8909B5711BC792570060A053 /* SKPhotoBrowser.framework */,
89FC5DBE1D54A04A00F1BE52 /* SKPhotoBrowserTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@ -130,6 +169,59 @@
path = SampleImages;
sourceTree = "<group>";
};
A6EBAD761CAFB87A00F80595 /* Controllers */ = {
isa = PBXGroup;
children = (
8909B55C1BC792150060A053 /* FromLocalViewController.swift */,
212705881C92C69C00466223 /* FromCameraRollViewController.swift */,
89FC5DBA1D54A04900F1BE52 /* FromWebViewController.swift */,
);
name = Controllers;
sourceTree = "<group>";
};
A6EBAD771CAFB89300F80595 /* UI */ = {
isa = PBXGroup;
children = (
8909B55E1BC792150060A053 /* Main.storyboard */,
8909B5631BC792150060A053 /* LaunchScreen.storyboard */,
);
name = UI;
sourceTree = "<group>";
};
A6EBAD781CAFB8A500F80595 /* Resource */ = {
isa = PBXGroup;
children = (
8909B5741BC792DC0060A053 /* SampleImages */,
8909B5611BC792150060A053 /* Assets.xcassets */,
);
name = Resource;
sourceTree = "<group>";
};
A6EBAD791CAFB96C00F80595 /* Info */ = {
isa = PBXGroup;
children = (
8909B5661BC792150060A053 /* Info.plist */,
);
name = Info;
sourceTree = "<group>";
};
C646D2C27A5B9CCC003A9BD1 /* Frameworks */ = {
isa = PBXGroup;
children = (
7C2188E912BB106233AA4AB2 /* Pods.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
D1D8ECD45B290372235B8168 /* Pods */ = {
isa = PBXGroup;
children = (
B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */,
DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -137,13 +229,18 @@
isa = PBXNativeTarget;
buildConfigurationList = 8909B5691BC792150060A053 /* Build configuration list for PBXNativeTarget "SKPhotoBrowserExample" */;
buildPhases = (
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */,
8909B5531BC792150060A053 /* Sources */,
8909B5541BC792150060A053 /* Frameworks */,
8909B5551BC792150060A053 /* Resources */,
A6A7B7841C9578E30025AC07 /* Embed Frameworks */,
575DBD5855AB711E5773767A /* Embed Pods Frameworks */,
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
A6A7B7831C9578E30025AC07 /* PBXTargetDependency */,
);
name = SKPhotoBrowserExample;
productName = SKPhotoBrowserExample;
@ -161,6 +258,7 @@
TargetAttributes = {
8909B5561BC792150060A053 = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 0800;
};
};
};
@ -196,6 +294,13 @@
remoteRef = 8909B5701BC792570060A053 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
89FC5DBE1D54A04A00F1BE52 /* SKPhotoBrowserTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = SKPhotoBrowserTests.xctest;
remoteRef = 89FC5DBD1D54A04A00F1BE52 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@ -223,18 +328,76 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2ECF43C4E99CDE3D98AE6842 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
showEnvVarsInLog = 0;
};
575DBD5855AB711E5773767A /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F19A62C34B2F0CFF753F30C3 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8909B5531BC792150060A053 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8909B55D1BC792150060A053 /* ViewController.swift in Sources */,
212705891C92C69C00466223 /* FromCameraRollViewController.swift in Sources */,
8909B55D1BC792150060A053 /* FromLocalViewController.swift in Sources */,
8909B55B1BC792150060A053 /* AppDelegate.swift in Sources */,
89FC5DBB1D54A04900F1BE52 /* FromWebViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
A6A7B7831C9578E30025AC07 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = SKPhotoBrowser;
targetProxy = A6A7B7821C9578E30025AC07 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
8909B55E1BC792150060A053 /* Main.storyboard */ = {
isa = PBXVariantGroup;
@ -340,23 +503,31 @@
};
8909B56A1BC792150060A053 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B3E081D4F2C8623852C459F5 /* Pods.debug.xcconfig */;
buildSettings = {
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.SKPhotoBrowserExample;
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
8909B56B1BC792150060A053 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DD077DBD0C164C96BCC7494F /* Pods.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = SKPhotoBrowserExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.SKPhotoBrowserExample;
PRODUCT_BUNDLE_IDENTIFIER = com.keishi.suzuki.aSKPhotoBrowserExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Release;
};

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:SKPhotoBrowserExample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
@ -15,7 +15,6 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="14F1021" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="qgG-zu-Htx">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<!--FromLocal-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="FromLocalViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
@ -17,7 +17,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="ZcN-ut-rvO">
<rect key="frame" x="0.0" y="20" width="600" height="580"/>
<rect key="frame" x="0.0" y="20" width="600" height="531"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="D0c-dd-EhH">
<size key="itemSize" width="182" height="182"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
@ -32,7 +32,7 @@
<rect key="frame" x="0.0" y="0.0" width="182" height="182"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="K7z-9G-al0">
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="K7z-9G-al0">
<rect key="frame" x="0.0" y="0.0" width="182" height="182"/>
</imageView>
</subviews>
@ -58,6 +58,7 @@
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="ZcN-ut-rvO" secondAttribute="bottom" id="jxM-4I-Axo"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="FromLocal" id="KFf-qk-pEk"/>
<simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
<connections>
<outlet property="collectionView" destination="ZcN-ut-rvO" id="bJb-LN-o9w"/>
@ -65,7 +66,140 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="234" y="445"/>
<point key="canvasLocation" x="1678" y="-667"/>
</scene>
<!--FromWeb-->
<scene sceneID="mNO-ib-nK2">
<objects>
<viewController id="wTD-ba-cWw" customClass="FromWebViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="GH3-21-jcQ"/>
<viewControllerLayoutGuide type="bottom" id="y5T-GC-T6k"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="pNX-nL-SLw">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZK0-HE-9Bs">
<rect key="frame" x="220" y="253" width="161" height="94"/>
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="40"/>
<state key="normal" title="Present"/>
<connections>
<action selector="pushButton:" destination="wTD-ba-cWw" eventType="touchUpInside" id="xIc-2O-Gmb"/>
</connections>
</button>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yxe-Wd-8iE">
<rect key="frame" x="180" y="78" width="240" height="128"/>
<constraints>
<constraint firstAttribute="height" constant="128" id="a4K-nx-MJW"/>
<constraint firstAttribute="width" constant="240" id="tzG-IV-N9z"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="top" secondItem="yxe-Wd-8iE" secondAttribute="bottom" constant="50" id="8Aq-op-KMh"/>
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="centerY" secondItem="pNX-nL-SLw" secondAttribute="centerY" id="9jd-Un-cAK"/>
<constraint firstItem="ZK0-HE-9Bs" firstAttribute="centerX" secondItem="pNX-nL-SLw" secondAttribute="centerX" id="h6U-H1-RV4"/>
<constraint firstItem="yxe-Wd-8iE" firstAttribute="centerX" secondItem="pNX-nL-SLw" secondAttribute="centerX" id="xA1-uS-dJw"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="FromWeb" id="2lE-np-9H3"/>
<connections>
<outlet property="imageView" destination="yxe-Wd-8iE" id="aMH-Df-3r0"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jdP-ty-Ygs" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1678" y="34"/>
</scene>
<!--CameraRoll-->
<scene sceneID="V6Y-ws-thI">
<objects>
<viewController id="aTQ-Do-sRD" customClass="FromCameraRollViewController" customModule="SKPhotoBrowserExample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="9hr-eY-Ckr"/>
<viewControllerLayoutGuide type="bottom" id="l1U-c1-FNM"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="FhP-Hc-46e">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="f1m-3Z-8mE">
<rect key="frame" x="0.0" y="20" width="600" height="531"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Vak-iW-bLh">
<size key="itemSize" width="182" height="182"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="exampleCollectionViewCell" id="6Rl-Fl-Pik" customClass="AssetExampleCollectionViewCell" customModule="SKPhotoBrowserExample" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="182" height="182"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="182" height="182"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="NkL-63-CXI">
<rect key="frame" x="0.0" y="0.0" width="182" height="182"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstItem="NkL-63-CXI" firstAttribute="leading" secondItem="6Rl-Fl-Pik" secondAttribute="leading" id="4VA-fI-wdu"/>
<constraint firstItem="NkL-63-CXI" firstAttribute="top" secondItem="6Rl-Fl-Pik" secondAttribute="top" id="DOR-QZ-jxS"/>
<constraint firstAttribute="bottom" secondItem="NkL-63-CXI" secondAttribute="bottom" id="iWN-eh-eWk"/>
<constraint firstAttribute="trailing" secondItem="NkL-63-CXI" secondAttribute="trailing" id="rcG-Ap-R4Q"/>
</constraints>
<connections>
<outlet property="exampleImageView" destination="NkL-63-CXI" id="eY7-KA-Za7"/>
</connections>
</collectionViewCell>
</cells>
<connections>
<outlet property="dataSource" destination="aTQ-Do-sRD" id="9dn-je-VUj"/>
<outlet property="delegate" destination="aTQ-Do-sRD" id="mHq-Ur-KJl"/>
</connections>
</collectionView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="l1U-c1-FNM" firstAttribute="top" secondItem="f1m-3Z-8mE" secondAttribute="bottom" id="D1M-80-UYO"/>
<constraint firstAttribute="trailing" secondItem="f1m-3Z-8mE" secondAttribute="trailing" id="UY9-Uk-tuW"/>
<constraint firstItem="f1m-3Z-8mE" firstAttribute="leading" secondItem="FhP-Hc-46e" secondAttribute="leading" id="kWJ-eS-7O0"/>
<constraint firstItem="f1m-3Z-8mE" firstAttribute="top" secondItem="9hr-eY-Ckr" secondAttribute="bottom" id="qdD-8q-n5J"/>
</constraints>
</view>
<tabBarItem key="tabBarItem" title="CameraRoll" id="fDV-zv-lCk"/>
<connections>
<outlet property="collectionView" destination="f1m-3Z-8mE" id="W6f-GL-fNz"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="X3n-FY-lyb" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1678" y="694"/>
</scene>
<!--Tab Bar Controller-->
<scene sceneID="MxJ-Af-928">
<objects>
<tabBarController id="qgG-zu-Htx" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="TLg-GF-sn7"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="qXa-Ne-beE">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tabBar>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="viewControllers" id="TmI-eA-2RC"/>
<segue destination="wTD-ba-cWw" kind="relationship" relationship="viewControllers" id="prf-dw-yqt"/>
<segue destination="aTQ-Do-sRD" kind="relationship" relationship="viewControllers" id="j9q-XO-aL9"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="j93-V4-LxV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="726" y="160"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,189 @@
//
// CameraRollCollectionViewController.swift
// SKPhotoBrowserExample
//
// Created by K Rummler on 11/03/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import UIKit
import Photos
import SKPhotoBrowser
class FromCameraRollViewController: UIViewController, SKPhotoBrowserDelegate, UICollectionViewDataSource, UICollectionViewDelegate {
@IBOutlet weak var collectionView: UICollectionView!
private let imageManager = PHCachingImageManager.defaultManager()
private var assets: [PHAsset] = []
private lazy var requestOptions: PHImageRequestOptions = {
let options = PHImageRequestOptions()
options.deliveryMode = .Opportunistic
options.resizeMode = .Fast
return options
}()
private lazy var bigRequestOptions: PHImageRequestOptions = {
let options = PHImageRequestOptions()
options.deliveryMode = .HighQualityFormat
options.resizeMode = .Fast
return options
}()
override func viewDidLoad() {
super.viewDidLoad()
fetchAssets()
collectionView?.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return assets.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath)
let asset = assets[indexPath.row]
if let cell = cell as? AssetExampleCollectionViewCell {
if let id = cell.requestId {
imageManager.cancelImageRequest(id)
cell.requestId = nil
}
cell.requestId = requestImageForAsset(asset, options: requestOptions) { image, requestId in
if requestId == cell.requestId || cell.requestId == nil {
cell.exampleImageView.image = image
}
}
}
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
return
}
guard let originImage = cell.exampleImageView.image else {
return
}
func open(images: [UIImage]) {
let photoImages: [SKPhotoProtocol] = images.map({ return SKPhoto.photoWithImage($0) })
let browser = SKPhotoBrowser(originImage: cell.exampleImageView.image!, photos: photoImages, animatedFromView: cell)
browser.initializePageIndex(indexPath.row)
browser.delegate = self
// browser.bounceAnimation = true
// browser.displayDeleteButton = true
// browser.displayAction = false
self.presentViewController(browser, animated: true, completion: {})
}
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
if let image = image, index = self?.assets.indexOf(asset) {
fetchedImages[index] = image
}
fetched += 1
if self?.assets.count == fetched {
open(fetchedImages)
}
})
}
}
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.fetchLimit = limit
let result = PHAsset.fetchAssetsWithOptions(options)
let amount = min(result.count, limit)
self.assets = result.objectsAtIndexes(NSIndexSet(indexesInRange: NSRange(location: 0, length: amount))) as? [PHAsset] ?? []
}
private func requestImageForAsset(asset: PHAsset, options: PHImageRequestOptions, completion: (image: UIImage?, requestId: PHImageRequestID?) -> ()) -> PHImageRequestID {
let scale = UIScreen.mainScreen().scale
let targetSize: CGSize
if options.deliveryMode == .HighQualityFormat {
targetSize = CGSize(width: 600 * scale, height: 600 * scale)
} else {
targetSize = CGSize(width: 182 * scale, height: 182 * scale)
}
requestOptions.synchronous = false
// Workaround because PHImageManager.requestImageForAsset doesn't work for burst images
if asset.representsBurst {
return imageManager.requestImageDataForAsset(asset, options: options) { data, _, _, dict in
let image = data.flatMap { UIImage(data: $0) }
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
completion(image: image, requestId: requestId?.intValue)
}
} else {
return imageManager.requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options) { image, dict in
let requestId = dict?[PHImageResultRequestIDKey] as? NSNumber
completion(image: image, requestId: requestId?.intValue)
}
}
}
override func prefersStatusBarHidden() -> Bool {
return false
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
class AssetExampleCollectionViewCell: ExampleCollectionViewCell {
var requestId: PHImageRequestID?
}

View File

@ -0,0 +1,169 @@
//
// ViewController.swift
// SKPhotoBrowserExample
//
// Created by suzuki_keishi on 2015/10/06.
// Copyright © 2015 suzuki_keishi. All rights reserved.
//
import UIKit
import SKPhotoBrowser
class FromLocalViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, SKPhotoBrowserDelegate {
@IBOutlet weak var collectionView: UICollectionView!
var images = [SKPhotoProtocol]()
override func viewDidLoad() {
super.viewDidLoad()
setupTestData()
setupCollectionView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prefersStatusBarHidden() -> Bool {
return false
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
// MARK: - UICollectionViewDataSource
extension FromLocalViewController {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath) as? ExampleCollectionViewCell else {
return UICollectionViewCell()
}
cell.exampleImageView.image = UIImage(named: "image\(indexPath.row % 10).jpg")
// cell.exampleImageView.contentMode = .ScaleAspectFill
return cell
}
}
// MARK: - UICollectionViewDelegate
extension FromLocalViewController {
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
return
}
guard let originImage = cell.exampleImageView.image else {
return
}
// SKPhotoBrowserOptions.displayToolbar = false
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.initializePageIndex(indexPath.row)
browser.delegate = self
// browser.updateCloseButton(UIImage(named: "image1.jpg")!)
presentViewController(browser, animated: true, completion: {})
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 300)
} else {
return CGSize(width: UIScreen.mainScreen().bounds.size.width / 2 - 5, height: 200)
}
}
}
// MARK: - SKPhotoBrowserDelegate
extension FromLocalViewController {
func didShowPhotoAtIndex(index: Int) {
collectionView.visibleCells().forEach({$0.hidden = false})
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
}
func willDismissAtPageIndex(index: Int) {
collectionView.visibleCells().forEach({$0.hidden = false})
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = true
}
func willShowActionSheet(photoIndex: Int) {
// do some handle if you need
}
func didDismissAtPageIndex(index: Int) {
collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))?.hidden = false
}
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
// handle dismissing custom actions
}
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
reload()
}
func viewForPhoto(browser: SKPhotoBrowser, index: Int) -> UIView? {
return collectionView.cellForItemAtIndexPath(NSIndexPath(forItem: index, inSection: 0))
}
}
// MARK: - private
private extension FromLocalViewController {
private func setupTestData() {
images = createLocalPhotos()
}
private func setupCollectionView() {
collectionView.delegate = self
collectionView.dataSource = self
}
func createLocalPhotos() -> [SKPhotoProtocol] {
return (0..<10).map { (i: Int) -> SKPhotoProtocol in
let photo = SKPhoto.photoWithImage(UIImage(named: "image\(i%10).jpg")!)
photo.caption = caption[i%10]
// photo.contentMode = .ScaleAspectFill
return photo
}
}
}
class ExampleCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var exampleImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
exampleImageView.image = nil
layer.cornerRadius = 25.0
layer.masksToBounds = true
}
override func prepareForReuse() {
exampleImageView.image = nil
}
}
var caption = ["Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
]

View File

@ -0,0 +1,84 @@
//
// FromWebViewController.swift
// SKPhotoBrowserExample
//
// Created by suzuki_keishi on 2015/10/06.
// Copyright © 2015 suzuki_keishi. All rights reserved.
//
import UIKit
import SKPhotoBrowser
import SDWebImage
class FromWebViewController: UIViewController, SKPhotoBrowserDelegate {
@IBOutlet weak var imageView: UIImageView!
var images = [SKPhotoProtocol]()
override func viewDidLoad() {
super.viewDidLoad()
SKCache.sharedCache.imageCache = CustomImageCache()
imageView.sd_setImageWithURL(NSURL(string: "https://placehold.jp/1500x1500.png")) {
guard let url = $0.3.absoluteString else { return }
SKCache.sharedCache.setImage($0.0, forKey: url)
}
}
@IBAction func pushButton(sender: AnyObject) {
let browser = SKPhotoBrowser(photos: createWebPhotos())
browser.initializePageIndex(0)
browser.delegate = self
presentViewController(browser, animated: true, completion: nil)
}
}
// MARK: - SKPhotoBrowserDelegate
extension FromWebViewController {
func didDismissAtPageIndex(index: Int) {
}
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
}
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
SKCache.sharedCache.removeImageForKey("somekey")
reload()
}
}
// MARK: - private
private extension FromWebViewController {
func createWebPhotos() -> [SKPhotoProtocol] {
return (0..<10).map { (i: Int) -> SKPhotoProtocol in
let photo = SKPhoto.photoWithImageURL("https://placehold.jp/150\(i)x150\(i).png")
photo.caption = caption[i%10]
photo.shouldCachePhotoURLImage = true
return photo
}
}
}
class CustomImageCache: SKImageCacheable {
var cache: SDImageCache
init() {
let cache = SDImageCache(namespace: "com.suzuki.custom.cache")
self.cache = cache
}
func imageForKey(key: String) -> UIImage? {
guard let image = cache.imageFromDiskCacheForKey(key) else { return nil }
return image
}
func setImage(image: UIImage, forKey key: String) {
cache.storeImage(image, forKey: key)
}
func removeImageForKey(key: String) {
}
}

View File

@ -2,8 +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>UIStatusBarHidden</key>
<false/>
<key>NSPhotoLibraryUsageDescription</key>
<string>for example, this app accesses the users photo library</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
@ -32,11 +32,14 @@
<array>
<string>armv7</string>
</array>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
@ -45,5 +48,10 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 KiB

After

Width:  |  Height:  |  Size: 198 KiB

View File

@ -1,133 +0,0 @@
//
// ViewController.swift
// SKPhotoBrowserExample
//
// Created by suzuki_keishi on 2015/10/06.
// Copyright © 2015 suzuki_keishi. All rights reserved.
//
import UIKit
import SKPhotoBrowser
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, SKPhotoBrowserDelegate {
@IBOutlet weak var collectionView: UICollectionView!
var images = [SKPhoto]()
var caption = ["Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
"Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.",
"It has survived not only five centuries, but also the leap into electronic typesetting",
"remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
]
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<30 {
let photo = SKPhoto.photoWithImage(UIImage(named: "image\(i%10).jpg")!)
photo.caption = caption[i%10]
images.append(photo)
}
setupTableView()
}
private func setupTableView() {
collectionView.delegate = self
collectionView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func prefersStatusBarHidden() -> Bool {
return false
}
// MARK: - UICollectionViewDataSource
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("exampleCollectionViewCell", forIndexPath: indexPath) as? ExampleCollectionViewCell else {
return UICollectionViewCell()
}
cell.exampleImageView.image = images[indexPath.row].underlyingImage
return cell
}
// MARK: - UICollectionViewDelegate
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
guard let cell = collectionView.cellForItemAtIndexPath(indexPath) as? ExampleCollectionViewCell else {
return
}
guard let originImage = cell.exampleImageView.image else {
return
}
let browser = SKPhotoBrowser(originImage: originImage, photos: images, animatedFromView: cell)
browser.initializePageIndex(indexPath.row)
browser.delegate = self
// Can hide the action button by setting to false
browser.displayAction = true
// delete action(you must write `removePhoto` delegate, what resource you want to delete)
// browser.displayDeleteButton = true
// Optional action button titles (if left off, it uses activity controller
// browser.actionButtonTitles = ["Do One Action", "Do Another Action"]
presentViewController(browser, animated: true, completion: {})
}
// MARK: - SKPhotoBrowserDelegate
func didShowPhotoAtIndex(index: Int) {
// do some handle if you need
}
func willDismissAtPageIndex(index: Int) {
// do some handle if you need
}
func willShowActionSheet(photoIndex: Int) {
// do some handle if you need
}
func didDismissAtPageIndex(index: Int) {
// do some handle if you need
}
func didDismissActionSheetWithButtonIndex(buttonIndex: Int, photoIndex: Int) {
// handle dismissing custom actions
}
func removePhoto(browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
// do some handle if you need
}
}
class ExampleCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var exampleImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
exampleImageView.image = nil
}
override func prepareForReuse() {
exampleImageView.image = nil
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,70 @@
//
// SKCacheTests.swift
// SKPhotoBrowser
//
// Created by Kevin Wolkober on 6/13/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import XCTest
@testable import SKPhotoBrowser
class SKCacheTests: XCTestCase {
var cache: SKCache!
let image = UIImage()
let key = "test_image"
override func setUp() {
super.setUp()
self.cache = SKCache()
}
override func tearDown() {
self.cache = nil
super.tearDown()
}
func testInit() {
XCTAssertNotNil(self.cache.imageCache)
XCTAssert(self.cache.imageCache is SKDefaultImageCache, "Default image cache should be loaded on init")
}
func testDefaultCacheImageForKey() {
// given
let cache = (self.cache.imageCache as? SKDefaultImageCache)!.cache
cache.setObject(self.image, forKey: self.key)
// when
let cachedImage = self.cache.imageForKey(self.key)
// then
XCTAssertNotNil(cachedImage)
}
func testDefaultCacheSetImageForKey() {
// when
self.cache.setImage(self.image, forKey: self.key)
// then
let cache = (self.cache.imageCache as? SKDefaultImageCache)!.cache
let cachedImage = cache.objectForKey(self.key) as? UIImage
XCTAssertNotNil(cachedImage)
}
func testDefaultCacheRemoveImageForKey() {
// given
let cache = (self.cache.imageCache as? SKDefaultImageCache)!.cache
cache.setObject(self.image, forKey: self.key)
// when
self.cache.removeImageForKey(self.key)
// then
let cachedImage = self.cache.imageForKey(self.key)
XCTAssertNil(cachedImage)
}
}

View File

@ -0,0 +1,52 @@
//
// SKPhotoBrowserTests.swift
// SKPhotoBrowserTests
//
// Created by Alexsander on 4/2/16.
// Copyright © 2016 suzuki_keishi. All rights reserved.
//
import XCTest
@testable import SKPhotoBrowser
class FakeSKPhotoBrowser: SKPhotoBrowser {
override func setup () {
}
}
class SKPhotoBrowserTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testSKPhotoArray() {
var images = [SKPhoto]()
let photo = SKPhoto.photoWithImage(UIImage())// add some UIImage
images.append(photo)
let _ = FakeSKPhotoBrowser(photos: images)
}
func testSKLocalPhotoArray() {
var images = [SKLocalPhoto]()
let photo = SKLocalPhoto.photoWithImageURL("")
images.append(photo)
let _ = FakeSKPhotoBrowser(photos: images)
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.2 MiB

BIN
Screenshots/example02.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB