├── PhotoBrowserDemo
├── .gitignore
├── PhotoBrowserDemo copy-Info.plist
├── PhotoBrowserDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata
│ │ │ └── admin.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ ├── admin.xcuserdatad
│ │ └── xcschemes
│ │ │ ├── PhotoBrowserCustomCoreDemo.xcscheme
│ │ │ ├── PhotoBrowserDemo.xcscheme
│ │ │ └── xcschememanagement.plist
│ │ └── heroims.xcuserdatad
│ │ └── xcschemes
│ │ ├── PhotoBrowserCustomCoreDemo.xcscheme
│ │ ├── PhotoBrowserDemo.xcscheme
│ │ └── xcschememanagement.plist
├── PhotoBrowserDemo.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ ├── admin.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── heroims.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
├── PhotoBrowserDemo
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Custom Images
│ │ ├── PhotoBrowser_customArrowLeft.png
│ │ ├── PhotoBrowser_customArrowLeft@2x.png
│ │ ├── PhotoBrowser_customArrowLeftSelected.png
│ │ ├── PhotoBrowser_customArrowLeftSelected@2x.png
│ │ ├── PhotoBrowser_customArrowRight.png
│ │ ├── PhotoBrowser_customArrowRight@2x.png
│ │ ├── PhotoBrowser_customArrowRightSelected.png
│ │ ├── PhotoBrowser_customArrowRightSelected@2x.png
│ │ ├── PhotoBrowser_customDoneButton.png
│ │ └── PhotoBrowser_customDoneButton@2x.png
│ ├── Info.plist
│ ├── Photos
│ │ ├── photo1l.jpg
│ │ ├── photo1m.jpg
│ │ ├── photo2l.jpg
│ │ ├── photo2m.jpg
│ │ ├── photo3l.jpg
│ │ ├── photo3m.jpg
│ │ ├── photo4l.jpg
│ │ ├── photo4m.jpg
│ │ └── photo5l.gif
│ ├── ViewController.h
│ ├── ViewController.m
│ ├── ZYQPhotoBrowser+Custom.h
│ ├── ZYQPhotoBrowser+Custom.m
│ └── main.m
├── PhotoBrowserDemoTests
│ ├── Info.plist
│ └── PhotoBrowserDemoTests.m
├── PhotoBrowserDemoUITests
│ ├── Info.plist
│ └── PhotoBrowserDemoUITests.m
├── Podfile
└── Podfile.lock
├── README.md
├── ZYQPhotoBrowser-NoDependency.podspec
├── ZYQPhotoBrowser.podspec
└── ZYQPhotoBrowser
├── ZYQCaptionView.h
├── ZYQCaptionView.m
├── ZYQPBConstants.h
├── ZYQPBLocalizations.bundle
├── de.lproj
│ └── Localizable.strings
├── en.lproj
│ └── Localizable.strings
├── es.lproj
│ └── Localizable.strings
├── fr.lproj
│ └── Localizable.strings
├── ja.lproj
│ └── Localizable.strings
├── nl.lproj
│ └── Localizable.strings
├── pt.lproj
│ └── Localizable.strings
├── ru.lproj
│ └── Localizable.strings
└── zh-Hans.lproj
│ └── Localizable.strings
├── ZYQPhoto.h
├── ZYQPhoto.m
├── ZYQPhotoBrowser.bundle
└── images
│ ├── IDMPhotoBrowser_arrowLeft.png
│ ├── IDMPhotoBrowser_arrowLeft@2x.png
│ ├── IDMPhotoBrowser_arrowRight.png
│ └── IDMPhotoBrowser_arrowRight@2x.png
├── ZYQPhotoBrowser.h
├── ZYQPhotoBrowser.m
├── ZYQPhotoProtocol.h
├── ZYQTapDetectingImageView.h
├── ZYQTapDetectingImageView.m
├── ZYQTapDetectingView.h
├── ZYQTapDetectingView.m
├── ZYQZoomingScrollView.h
└── ZYQZoomingScrollView.m
/PhotoBrowserDemo/.gitignore:
--------------------------------------------------------------------------------
1 | /Pods
2 | /Demo.xcworkspace
3 | /UserInterfaceState.xcuserstate
4 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo copy-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/project.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/PhotoBrowserCustomCoreDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/PhotoBrowserDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/admin.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | PhotoBrowserCustomCoreDemo.xcscheme
8 |
9 | orderHint
10 | 7
11 |
12 | PhotoBrowserDemo.xcscheme
13 |
14 | orderHint
15 | 0
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | 84549B181EFA48D600888AD8
21 |
22 | primary
23 |
24 |
25 | 84549B311EFA48D700888AD8
26 |
27 | primary
28 |
29 |
30 | 84549B3C1EFA48D700888AD8
31 |
32 | primary
33 |
34 |
35 | 84549B921EFBC1A600888AD8
36 |
37 | primary
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/heroims.xcuserdatad/xcschemes/PhotoBrowserCustomCoreDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/heroims.xcuserdatad/xcschemes/PhotoBrowserDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcodeproj/xcuserdata/heroims.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | PhotoBrowserCustomCoreDemo.xcscheme
8 |
9 | orderHint
10 | 1
11 |
12 | PhotoBrowserDemo.xcscheme
13 |
14 | orderHint
15 | 0
16 |
17 |
18 | SuppressBuildableAutocreation
19 |
20 | 84549B181EFA48D600888AD8
21 |
22 | primary
23 |
24 |
25 | 84549B311EFA48D700888AD8
26 |
27 | primary
28 |
29 |
30 | 84549B3C1EFA48D700888AD8
31 |
32 | primary
33 |
34 |
35 | 84549B921EFBC1A600888AD8
36 |
37 | primary
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
24 |
36 |
37 |
38 |
40 |
52 |
53 |
54 |
56 |
68 |
69 |
70 |
72 |
84 |
85 |
86 |
88 |
100 |
101 |
102 |
104 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/heroims.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/heroims.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo.xcworkspace/xcuserdata/heroims.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeft.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeft@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeft@2x.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeftSelected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeftSelected.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeftSelected@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowLeftSelected@2x.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRight.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRight@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRight@2x.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRightSelected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRightSelected.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRightSelected@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customArrowRightSelected@2x.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customDoneButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customDoneButton.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customDoneButton@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Custom Images/PhotoBrowser_customDoneButton@2x.png
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo1l.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo1l.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo1m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo1m.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo2l.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo2l.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo2m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo2m.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo3l.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo3l.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo3m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo3m.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo4l.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo4l.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo4m.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo4m.jpg
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo5l.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/PhotoBrowserDemo/PhotoBrowserDemo/Photos/photo5l.gif
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UITableViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | #import "ZYQPhotoBrowser.h"
12 |
13 | #ifdef CUSTOM
14 | #import "ZYQPhotoBrowser+Custom.h"
15 | #import "YYWebImage.h"
16 |
17 | @interface BackgroudBlurView : UIView
18 |
19 | @property(nonatomic,strong)UIImageView *imgView;
20 | @property(nonatomic,strong)UIVisualEffectView *maskView;
21 |
22 | @end
23 |
24 | @implementation BackgroudBlurView
25 |
26 | -(instancetype)init{
27 | if (self=[super init]) {
28 | [self addSubview:self.imgView];
29 | [self addSubview:self.maskView];
30 | }
31 | return self;
32 | }
33 |
34 | -(void)layoutSubviews{
35 | [super layoutSubviews];
36 |
37 | self.imgView.frame=self.bounds;
38 | self.maskView.frame=self.bounds;
39 | }
40 |
41 | -(UIImageView *)imgView{
42 | if (_imgView==nil) {
43 | _imgView=[[UIImageView alloc] init];
44 | }
45 | return _imgView;
46 | }
47 |
48 | -(UIVisualEffectView *)maskView{
49 | if (_maskView==nil) {
50 | UIBlurEffect *beffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight];
51 |
52 | _maskView = [[UIVisualEffectView alloc]initWithEffect:beffect];
53 | }
54 | return _maskView;
55 | }
56 |
57 | @end
58 | #else
59 | #endif
60 |
61 | @interface ViewController ()
62 |
63 | @end
64 |
65 | @implementation ViewController
66 |
67 | - (void)viewDidLoad {
68 | [super viewDidLoad];
69 | // Do any additional setup after loading the view, typically from a nib.
70 | #ifdef CUSTOM
71 | self.title=@"Custom";
72 | #else
73 | self.title=@"Default";
74 | #endif
75 |
76 | UIView *tableViewFooter=[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 426 * 0.9 + 40)];
77 |
78 | UIButton *buttonWithImageOnScreen1=[[UIButton alloc] initWithFrame:CGRectMake(15, 0, 640/3 * 0.9, 426/2 * 0.9)];
79 | buttonWithImageOnScreen1.tag = 101;
80 | buttonWithImageOnScreen1.adjustsImageWhenHighlighted = false;
81 | [buttonWithImageOnScreen1 setImage:[UIImage imageNamed:@"photo1m.jpg"] forState:UIControlStateNormal];
82 | buttonWithImageOnScreen1.imageView.contentMode=UIViewContentModeScaleToFill;
83 | buttonWithImageOnScreen1.backgroundColor = [UIColor blackColor];
84 | [buttonWithImageOnScreen1 addTarget:self action:@selector(buttonWithImageOnScreenPressed:) forControlEvents:UIControlEventTouchUpInside];
85 | [tableViewFooter addSubview:buttonWithImageOnScreen1];
86 |
87 |
88 | UIButton *buttonWithImageOnScreen2=[[UIButton alloc] initWithFrame:CGRectMake(15, 426/2 * 0.9 + 20, 640/3 * 0.9, 426/2 * 0.9)];
89 | buttonWithImageOnScreen2.tag = 102;
90 | buttonWithImageOnScreen2.adjustsImageWhenHighlighted = false;
91 | [buttonWithImageOnScreen2 setImage:[UIImage imageNamed:@"photo3m.jpg"] forState:UIControlStateNormal];
92 | buttonWithImageOnScreen2.imageView.contentMode=UIViewContentModeScaleToFill;
93 | buttonWithImageOnScreen2.backgroundColor = [UIColor blackColor];
94 | [buttonWithImageOnScreen2 addTarget:self action:@selector(buttonWithImageOnScreenPressed:) forControlEvents:UIControlEventTouchUpInside];
95 | [tableViewFooter addSubview:buttonWithImageOnScreen2];
96 |
97 | self.tableView.tableFooterView = tableViewFooter;
98 |
99 | }
100 |
101 |
102 | - (void)didReceiveMemoryWarning {
103 | [super didReceiveMemoryWarning];
104 | // Dispose of any resources that can be recreated.
105 | }
106 |
107 | -(void)buttonWithImageOnScreenPressed:(UIButton*)sender{
108 | NSMutableArray *photos=[[NSMutableArray alloc] init];
109 |
110 | ZYQPhoto *photo=nil;
111 |
112 | if (sender.tag==101) {
113 | NSString *path_photo1l=[[NSBundle mainBundle] pathForResource:@"photo1l" ofType:@"jpg"];
114 |
115 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo1l];
116 | photo.caption=@"Grotto of the Madonna";
117 | [photos addObject:photo];
118 | }
119 |
120 |
121 |
122 | NSString *path_photo3l=[[NSBundle mainBundle] pathForResource:@"photo3l" ofType:@"jpg"];
123 |
124 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo3l];
125 | photo.caption=@"York Floods";
126 | [photos addObject:photo];
127 |
128 | NSString *path_photo2l=[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"];
129 |
130 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo2l];
131 | photo.caption=@"The London Eye is a giant Ferris wheel situated on the banks of the River Thames, in London, England.";
132 | [photos addObject:photo];
133 |
134 | NSString *path_photo4l=[[NSBundle mainBundle] pathForResource:@"photo4l" ofType:@"jpg"];
135 |
136 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo4l];
137 | photo.caption=@"Campervan";
138 | [photos addObject:photo];
139 |
140 | NSString *path_photo5l=[[NSBundle mainBundle] pathForResource:@"photo5l" ofType:@"gif"];
141 |
142 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo5l];
143 | photo.caption=@"Gif";
144 | [photos addObject:photo];
145 |
146 | if (sender.tag==102) {
147 | NSString *path_photo1l=[[NSBundle mainBundle] pathForResource:@"photo1l" ofType:@"jpg"];
148 |
149 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo1l];
150 | photo.caption=@"Grotto of the Madonna";
151 | [photos addObject:photo];
152 |
153 | }
154 |
155 | ZYQPhotoBrowser *browser=[[ZYQPhotoBrowser alloc] initWithPhotos:photos animatedFromView:sender];
156 | browser.delegate = self;
157 | browser.displayActionButton = false;
158 | browser.displayArrowButton = NO;
159 | browser.displayDoneButton=NO;
160 | browser.autoHideInterface=NO;
161 | browser.displayCounterLabel = true;
162 | browser.useZoomAnimation = true;
163 | browser.scaleImage = sender.currentImage;
164 | browser.dismissOnTouch = true;
165 | browser.displayTitle= YES;
166 |
167 | [self presentViewController:browser animated:YES completion:nil];
168 |
169 | }
170 |
171 | -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
172 | return 3;
173 | }
174 |
175 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
176 | switch (section) {
177 | case 0:
178 | return 1;
179 | case 1:
180 | return 3;
181 | case 2:
182 | return 0;
183 | default:
184 | return 0;
185 | }
186 | }
187 |
188 | -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
189 | switch (section) {
190 | case 0:
191 | return @"Single photo";
192 | case 1:
193 | return @"Multiple photos";
194 | case 2:
195 | return @"Photos on screen";
196 | default:
197 | return @"";
198 | }
199 | }
200 |
201 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
202 | static NSString *cellIdentifier=@"cell";
203 | UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
204 | if (cell==nil) {
205 | cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
206 | [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
207 | }
208 |
209 | if (indexPath.section==0) {
210 | cell.textLabel.text=@"Local photo";
211 | }
212 | else if (indexPath.section==1){
213 | switch (indexPath.row) {
214 | case 0:
215 | cell.textLabel.text=@"Local photos";
216 | break;
217 | case 1:
218 | cell.textLabel.text=@"Photos from Baidu";
219 | break;
220 | case 2:
221 | cell.textLabel.text=@"Photos from Baidu - Custom";
222 | break;
223 |
224 | default:
225 | break;
226 | }
227 | }
228 | return cell;
229 | }
230 |
231 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
232 | NSMutableArray *photos=[[NSMutableArray alloc] init];
233 |
234 | ZYQPhoto *photo=nil;
235 |
236 | if (indexPath.section==0) {
237 | NSString *path_photo2l=[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"];
238 |
239 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo2l];
240 | photo.caption=@"The London Eye is a giant Ferris wheel situated on the banks of the River Thames, in London, England.";
241 | [photos addObject:photo];
242 | }
243 | else if(indexPath.section==1){
244 | if (indexPath.row==0) {
245 | NSString *path_photo1l=[[NSBundle mainBundle] pathForResource:@"photo1l" ofType:@"jpg"];
246 |
247 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo1l];
248 | photo.caption=@"Grotto of the Madonna";
249 | [photos addObject:photo];
250 |
251 | NSString *path_photo2l=[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"];
252 |
253 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo2l];
254 | photo.caption=@"The London Eye is a giant Ferris wheel situated on the banks of the River Thames, in London, England.";
255 | [photos addObject:photo];
256 |
257 | NSString *path_photo3l=[[NSBundle mainBundle] pathForResource:@"photo3l" ofType:@"jpg"];
258 |
259 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo3l];
260 | photo.caption=@"York Floods";
261 | [photos addObject:photo];
262 |
263 | NSString *path_photo4l=[[NSBundle mainBundle] pathForResource:@"photo4l" ofType:@"jpg"];
264 |
265 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo4l];
266 | photo.caption=@"Campervan";
267 | [photos addObject:photo];
268 |
269 | NSString *path_photo5l=[[NSBundle mainBundle] pathForResource:@"photo5l" ofType:@"gif"];
270 |
271 | photo=[[ZYQPhoto alloc] initWithFilePath:path_photo5l];
272 | photo.caption=@"Gif";
273 | [photos addObject:photo];
274 |
275 | }
276 | else if(indexPath.row==1||indexPath.row==2){
277 | NSArray *photosWithURLArray=@[
278 | [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498113175410&di=bd10bcd2b2a45906259f4e823bbf9df6&imgtype=0&src=http%3A%2F%2Fpic.58pic.com%2F58pic%2F14%2F27%2F45%2F71r58PICmDM_1024.jpg"]
279 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498113236506&di=004225eba24d74087feaeaa9484db047&imgtype=0&src=http%3A%2F%2Fic.topit.me%2Fc%2F3c%2Ff6%2F1110230348da8f63cco.jpg"]
280 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1498103182&di=eb660bada9ced24acbc9153464aaedcd&src=http://www.bz55.com/uploads/allimg/141120/139-141120151946.jpg"]
281 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498139264538&di=6e5cedbfecc6a0e4bce2fc0005f56785&imgtype=0&src=http%3A%2F%2Fimg3.a0bi.com%2Fupload%2Fttq%2F20140719%2F1405761279700.gif"]
282 | ];
283 |
284 | NSArray *photosWithURL=[ZYQPhoto photosWithURLs:photosWithURLArray];
285 | photos=[NSMutableArray arrayWithArray:photosWithURL];
286 |
287 | }
288 | }
289 |
290 | ZYQPhotoBrowser *browser=[[ZYQPhotoBrowser alloc] initWithPhotos:photos];
291 | browser.delegate=self;
292 | if (indexPath.section==1) {
293 | if (indexPath.row==1) {
294 | browser.displayCounterLabel = true;
295 | browser.displayActionButton = false;
296 | }
297 | else if (indexPath.row==2){
298 | browser.actionButtonTitles = @[@"Option 1", @"Option 2", @"Option 3", @"Option 4"];
299 | browser.displayCounterLabel = true;
300 | browser.useWhiteBackgroundColor = true;
301 | browser.leftArrowImage = [UIImage imageNamed:@"PhotoBrowser_customArrowLeft"];
302 | browser.rightArrowImage = [UIImage imageNamed:@"PhotoBrowser_customArrowRight"];
303 | browser.leftArrowSelectedImage = [UIImage imageNamed:@"PhotoBrowser_customArrowLeftSelected"];
304 | browser.rightArrowSelectedImage = [UIImage imageNamed:@"PhotoBrowser_customArrowRightSelected"];
305 | browser.doneButtonImage = [UIImage imageNamed:@"PhotoBrowser_customDoneButton"];
306 | browser.view.tintColor = [UIColor orangeColor];
307 | browser.progressTintColor = [UIColor orangeColor];
308 | browser.trackTintColor = [UIColor colorWithWhite:0.8 alpha:1];
309 | }
310 | else{
311 | browser.displayToolbar=NO;
312 | }
313 | }
314 | #ifdef CUSTOM
315 | browser.customBackgroud=[[BackgroudBlurView alloc] init];
316 | browser.gifSupportImageViewClass=[YYAnimatedImageView class];
317 | #else
318 | #endif
319 | [self presentViewController:browser animated:YES completion:nil];
320 |
321 | }
322 |
323 | -(void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didShowPhotoAtIndex:(NSUInteger)index{
324 | ZYQPhoto *photo=[photoBrowser photoAtIndex:index];
325 | #ifdef CUSTOM
326 | BackgroudBlurView *blurView=(BackgroudBlurView*)photoBrowser.customBackgroud;
327 | dispatch_async(dispatch_get_main_queue(), ^{
328 | if (photo.underlyingImage) {
329 | blurView.imgView.image=photo.underlyingImage;
330 | }
331 | else{
332 | [blurView.imgView yy_setImageWithURL:photo.photoURL options:YYWebImageOptionAllowBackgroundTask];
333 | }
334 | });
335 | #else
336 | #endif
337 |
338 | NSLog(@"Did show photoBrowser with photo index: %zi, photo caption: %@",index,photo.caption);
339 | }
340 |
341 | -(void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser willDismissAtPageIndex:(NSUInteger)index{
342 | ZYQPhoto *photo=[photoBrowser photoAtIndex:index];
343 | NSLog(@"Did show photoBrowser with photo index: %zi, photo caption: %@",index,photo.caption);
344 | }
345 |
346 | -(void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didDismissAtPageIndex:(NSUInteger)index{
347 | ZYQPhoto *photo=[photoBrowser photoAtIndex:index];
348 | NSLog(@"Did show photoBrowser with photo index: %zi, photo caption: %@",index,photo.caption);
349 | }
350 |
351 | -(void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didDismissActionSheetWithButtonIndex:(NSUInteger)buttonIndex photoIndex:(NSUInteger)photoIndex{
352 | ZYQPhoto *photo=[photoBrowser photoAtIndex:buttonIndex];
353 | NSLog(@"Did show photoBrowser with photo index: %zi, photo caption: %@",buttonIndex,photo.caption);
354 |
355 | }
356 |
357 | @end
358 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/ZYQPhotoBrowser+Custom.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhotoBrowser+Custom.h
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/22.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ZYQPhotoBrowser (Custom)
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/ZYQPhotoBrowser+Custom.m:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // ZYQPhotoBrowser+Custom.m
4 | // PhotoBrowserDemo
5 | //
6 | // Created by admin on 2017/6/22.
7 | // Copyright © 2017年 admin. All rights reserved.
8 | //
9 |
10 | #import "ZYQPhotoBrowser+Custom.h"
11 | #import "ZYQPhoto.h"
12 | #import "ZYQZoomingScrollView.h"
13 |
14 | #import "DACircularProgressView.h"
15 | #import "YYWebImage.h"
16 |
17 | @implementation ZYQPhotoBrowser (Custom)
18 |
19 | @end
20 |
21 | @interface ZYQPhoto (Custom)
22 |
23 | @end
24 |
25 | @implementation ZYQPhoto (Custom)
26 |
27 | -(void)loadImageWithURL:(NSURL*)url updateProgressBlock:(void (^)(CGFloat progress))updateProgressBlock completedBlock:(void (^)(UIImage *image,NSError *error))completedBlock{
28 | [[YYWebImageManager sharedManager] requestImageWithURL:url options:YYWebImageOptionAllowBackgroundTask progress:^(NSInteger receivedSize, NSInteger expectedSize) {
29 | if (updateProgressBlock) {
30 | updateProgressBlock(receivedSize/expectedSize*1.0);
31 | }
32 | } transform:nil completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
33 | if (completedBlock) {
34 | completedBlock(image,error);
35 | }
36 | }];
37 | }
38 |
39 | -(id)loadImageWithFile:(NSString *)path{
40 | return [YYImage imageWithContentsOfFile:path];
41 | }
42 |
43 | @end
44 |
45 | @interface ZYQZoomingScrollView (Custom)
46 |
47 | @end
48 |
49 | @implementation ZYQZoomingScrollView (Custom)
50 |
51 | -(id)getCustomProgressViewWithBrowser:(ZYQPhotoBrowser *)browser{
52 | CGRect screenBound = [[UIScreen mainScreen] bounds];
53 | CGFloat screenWidth = screenBound.size.width;
54 | CGFloat screenHeight = screenBound.size.height;
55 |
56 | DACircularProgressView *tmpProgressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake((screenWidth-35.)/2., (screenHeight-35.)/2, 35.0f, 35.0f)];
57 | [tmpProgressView setProgress:0.0f];
58 | tmpProgressView.tag = 101;
59 | tmpProgressView.thicknessRatio = 0.1;
60 | tmpProgressView.roundedCorners = NO;
61 | tmpProgressView.trackTintColor = browser.trackTintColor ? browser.trackTintColor : [UIColor colorWithWhite:0.2 alpha:1];
62 | tmpProgressView.progressTintColor = browser.progressTintColor ? browser.progressTintColor : [UIColor colorWithWhite:1.0 alpha:1];
63 |
64 | _progressView=tmpProgressView;
65 |
66 | return tmpProgressView;
67 | }
68 |
69 | @end
70 |
71 | @interface DACircularProgressView (Custom)
72 |
73 | @end
74 |
75 | @implementation DACircularProgressView (Custom)
76 |
77 | -(void)setZyq_progress:(CGFloat)zyq_progress{
78 | [self setProgress:zyq_progress animated:YES];
79 | }
80 |
81 | -(CGFloat)zyq_progress{
82 | return [self progress];
83 | }
84 |
85 | @end
86 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // PhotoBrowserDemo
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemoTests/PhotoBrowserDemoTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoBrowserDemoTests.m
3 | // PhotoBrowserDemoTests
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PhotoBrowserDemoTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation PhotoBrowserDemoTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemoUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/PhotoBrowserDemoUITests/PhotoBrowserDemoUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoBrowserDemoUITests.m
3 | // PhotoBrowserDemoUITests
4 | //
5 | // Created by admin on 2017/6/21.
6 | // Copyright © 2017年 admin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PhotoBrowserDemoUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation PhotoBrowserDemoUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | target 'PhotoBrowserDemo' do
5 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
6 | # use_frameworks!
7 |
8 | # Pods for PhotoBrowserDemo
9 | #Default core
10 | pod 'ZYQPhotoBrowser', :path => '../ZYQPhotoBrowser.podspec'
11 |
12 |
13 | target 'PhotoBrowserDemoTests' do
14 | inherit! :search_paths
15 | # Pods for testing
16 | end
17 |
18 | target 'PhotoBrowserDemoUITests' do
19 | inherit! :search_paths
20 | # Pods for testing
21 | end
22 |
23 | end
24 |
25 | target 'PhotoBrowserCustomCoreDemo' do
26 |
27 | #NoDependency custom core
28 | pod 'ZYQPhotoBrowser-NoDependency', :path => '../ZYQPhotoBrowser-NoDependency.podspec'
29 | pod 'YYWebImage'
30 | pod 'YYImage/WebP'
31 | pod 'DACircularProgress'
32 | end
33 |
--------------------------------------------------------------------------------
/PhotoBrowserDemo/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - DACircularProgress (2.3.1)
3 | - FLAnimatedImage (1.0.12)
4 | - SDWebImage (4.0.0):
5 | - SDWebImage/Core (= 4.0.0)
6 | - SDWebImage/Core (4.0.0)
7 | - SDWebImage/GIF (4.0.0):
8 | - FLAnimatedImage (~> 1.0)
9 | - SDWebImage/Core
10 | - YYCache (1.0.4)
11 | - YYImage (1.0.4):
12 | - YYImage/Core (= 1.0.4)
13 | - YYImage/Core (1.0.4)
14 | - YYImage/WebP (1.0.4):
15 | - YYImage/Core
16 | - YYWebImage (1.0.5):
17 | - YYCache
18 | - YYImage
19 | - ZYQPhotoBrowser (1.0):
20 | - DACircularProgress
21 | - SDWebImage (= 4.0.0)
22 | - SDWebImage/GIF (= 4.0.0)
23 | - ZYQPhotoBrowser-NoDependency (1.0)
24 |
25 | DEPENDENCIES:
26 | - DACircularProgress
27 | - YYImage/WebP
28 | - YYWebImage
29 | - ZYQPhotoBrowser (from `../ZYQPhotoBrowser.podspec`)
30 | - ZYQPhotoBrowser-NoDependency (from `../ZYQPhotoBrowser-NoDependency.podspec`)
31 |
32 | EXTERNAL SOURCES:
33 | ZYQPhotoBrowser:
34 | :path: "../ZYQPhotoBrowser.podspec"
35 | ZYQPhotoBrowser-NoDependency:
36 | :path: "../ZYQPhotoBrowser-NoDependency.podspec"
37 |
38 | SPEC CHECKSUMS:
39 | DACircularProgress: 4dd437c0fc3da5161cb289e07ac449493d41db71
40 | FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31
41 | SDWebImage: 76a6348bdc74eb5a55dd08a091ef298e56b55e41
42 | YYCache: 8105b6638f5e849296c71f331ff83891a4942952
43 | YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
44 | YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
45 | ZYQPhotoBrowser: 1b74c28ddc38991e4f1f7e6cabe95608246493f6
46 | ZYQPhotoBrowser-NoDependency: 94c7e339ec93077c3db2f3347ba82088951de3d7
47 |
48 | PODFILE CHECKSUM: 8d9697984782e14f8854bba8eb67f88493814932
49 |
50 | COCOAPODS: 1.2.1
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZYQPhotoBrowser  
2 |
3 | ZYQPhotoBrowser is a new implementation based on [MWPhotoBrowser](https://github.com/mwaterfall/MWPhotoBrowser).
4 |
5 | We've added both user experience and technical features inspired by Facebook's and Tweetbot's photo browsers.
6 |
7 | ## Screenshots
8 |
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | ## Usage
16 |
17 | See the code snippet below for an example of how to implement the photo browser.
18 |
19 | First create a photos array containing ZYQPhoto objects:
20 |
21 | ``` objective-c
22 | // URLs array
23 | NSArray *photosURL = @[
24 | [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498113175410&di=bd10bcd2b2a45906259f4e823bbf9df6&imgtype=0&src=http%3A%2F%2Fpic.58pic.com%2F58pic%2F14%2F27%2F45%2F71r58PICmDM_1024.jpg"]
25 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498113236506&di=004225eba24d74087feaeaa9484db047&imgtype=0&src=http%3A%2F%2Fic.topit.me%2Fc%2F3c%2Ff6%2F1110230348da8f63cco.jpg"]
26 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b10000_10000&sec=1498103182&di=eb660bada9ced24acbc9153464aaedcd&src=http://www.bz55.com/uploads/allimg/141120/139-141120151946.jpg"]
27 | ,[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1498139264538&di=6e5cedbfecc6a0e4bce2fc0005f56785&imgtype=0&src=http%3A%2F%2Fimg3.a0bi.com%2Fupload%2Fttq%2F20140719%2F1405761279700.gif"]
28 | ];
29 |
30 | // Create an array to store ZYQPhoto objects
31 | NSMutableArray *photos = [NSMutableArray new];
32 |
33 | for (NSURL *url in photosURL) {
34 | ZYQPhoto *photo = [ZYQPhoto photoWithURL:url];
35 | [photos addObject:photo];
36 | }
37 |
38 | // Or use this constructor to receive an NSArray of ZYQPhoto objects from your NSURL objects
39 | NSArray *photos = [ZYQPhoto photosWithURLs:photosURL];
40 | ````
41 |
42 | There are two main ways to presente the photoBrowser, with a fade on screen or with a zooming effect from an existing view.
43 |
44 | Using a simple fade transition:
45 |
46 | ``` objective-c
47 | ZYQPhotoBrowser *browser = [[ZYQPhotoBrowser alloc] initWithPhotos:photos];
48 | ```
49 |
50 | Zooming effect from a view:
51 |
52 | ``` objective-c
53 | ZYQPhotoBrowser *browser = [[ZYQPhotoBrowser alloc] initWithPhotos:photos animatedFromView:sender];
54 | ```
55 |
56 | When using this animation you can set the `scaleImage` property, in case the image from the view is not the same as the one that will be shown on the browser, so it will dynamically scale it:
57 |
58 | ``` objective-c
59 | browser.scaleImage = buttonSender.currentImage;
60 | ```
61 |
62 | Presenting using a modal view controller:
63 |
64 | ``` objective-c
65 | [self presentViewController:browser animated:YES completion:nil];
66 | ```
67 |
68 | ### Customization
69 |
70 | ##### Toolbar
71 |
72 | You can customize the toolbar. There are three boolean properties you can set: displayActionButton (default is YES), displayArrowButton (default is YES) and displayCounterLabel (default is NO). If you dont want the toolbar at all, you can set displayToolbar = NO.
73 |
74 | Toolbar setup example:
75 | ``` objective-c
76 | browser.displayActionButton = NO;
77 | browser.displayArrowButton = YES;
78 | browser.displayCounterLabel = YES;
79 | ```
80 |
81 | It is possible to use your own image on the toolbar arrows:
82 | ``` objective-c
83 | browser.leftArrowImage = [UIImage imageNamed:@"PhotoBrowser_customArrowLeft.png"];
84 | browser.rightArrowImage = [UIImage imageNamed:@"PhotoBrowser_customArrowRight.png"];
85 | browser.leftArrowSelectedImage = [UIImage imageNamed:@"PhotoBrowser_customArrowLeftSelected.png"];
86 | browser.rightArrowSelectedImage = [UIImage imageNamed:@"PhotoBrowser_customArrowRightSelected.png"];
87 | ```
88 |
89 | If you want to use custom actions, set the actionButtonTitles array with the titles for the actionSheet. Then, implement the photoBrowser:didDismissActionSheetWithButtonIndex:photoIndex: method, from the ZYQPhotoBrowser delegate
90 |
91 | ``` objective-c
92 | browser.actionButtonTitles = @[@"Option 1", @"Option 2", @"Option 3", @"Option 4"];
93 | ```
94 |
95 | #### Others
96 |
97 | Others customizations you can make are: use white background color, don't display the done button and change the done button background image:
98 | ``` objective-c
99 | browser.useWhiteBackgroundColor = YES;
100 | browser.displayDoneButton = NO;
101 | browser.doneButtonImage = [UIImage imageNamed:@"PhotoBrowser_customDoneButton.png"];
102 | ```
103 |
104 | If you want to keep the interface shown when the user is scrolling :
105 | ``` objective-c
106 | browser.autoHideInterface = NO;
107 | ```
108 |
109 | You can use a smooth zoom animation when presenting and dismissing a photo:
110 | ``` objective-c
111 | browser.useZoomAnimation = YES;
112 | ```
113 |
114 | If the presenting view controller doesn't have a status bar, in some cases you can force it to be hidden:
115 | ``` objective-c
116 | browser.forceHideStatusBar = YES;
117 | ```
118 |
119 | It's possible to disable the vertical dismiss swipe gesture:
120 | ``` objective-c
121 | browser.disableVerticalSwipe = YES;
122 | ```
123 |
124 | Dismiss the photo browser with a touch (instead of showing/hiding controls):
125 | ``` objective-c
126 | browser.dismissOnTouch = YES;
127 | ```
128 | You can use custom backgroud
129 | ``` objective-c
130 | browser.customBackgroud=[[BackgroudBlurView alloc] init];
131 | ```
132 |
133 | ### Photo Captions
134 |
135 | Photo captions can be displayed simply by setting the `caption` property on specific photos:
136 | ``` objective-c
137 | ZYQPhoto *photo = [ZYQPhoto photoWithFilePath:[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"]];
138 | photo.caption = @"Campervan";
139 | ```
140 |
141 | No caption will be displayed if the caption property is not set.
142 |
143 | #### Custom Captions
144 |
145 | By default, the caption is a simple black transparent view with a label displaying the photo's caption in white. If you want to implement your own caption view, follow these steps:
146 |
147 | 1. Optionally use a subclass of `ZYQPhoto` for your photos so you can store more data than a simple caption string.
148 | 2. Subclass `ZYQCaptionView` and override `-setupCaption` and `-sizeThatFits:` (and any other UIView methods you see fit) to layout your own view and set it's size. More information on this can be found in `ZYQCaptionView.h`
149 | 3. Implement the `-photoBrowser:captionViewForPhotoAtIndex:` ZYQPhotoBrowser delegate method (shown below).
150 |
151 | Example delegate method for custom caption view:
152 | ``` objective-c
153 | - (ZYQCaptionView *)photoBrowser:(ZYQPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index {
154 | ZYQPhoto *photo = [self.photos objectAtIndex:index];
155 | MyCaptionViewSubclass *captionView = [[MyCaptionViewSubclass alloc] initWithPhoto:photo];
156 | return captionView;
157 | }
158 | ```
159 | #### Custom Opensource libraries
160 | You need to implement protocol
161 | ``` objective-c
162 | //ZYQPhotoBrowser's
163 | @protocol ZYQPhotoBrowserCustomProtocol
164 |
165 | @required
166 | - (void)custom_animateView:(UIView *)view toFrame:(CGRect)frame completion:(void (^)(void))completion;
167 |
168 | @end
169 |
170 | //ZYQPhoto's
171 | @protocol ZYQPhotoCustomLoadProtocol
172 |
173 | @required
174 | -(void)loadImageWithURL:(NSURL*)url updateProgressBlock:(void (^)(CGFloat progress))updateProgressBlock completedBlock:(void (^)(UIImage *image,NSError *error))completedBlock;
175 | -(id)loadImageWithFile:(NSString *)path;
176 |
177 | @end
178 |
179 | //ZYQZoomingScrollView's
180 | @class ZYQPhotoBrowser;
181 | @protocol ZYQZoomingScrollViewCustomProtocol
182 |
183 | @required
184 | -(id)getCustomProgressViewWithBrowser:(ZYQPhotoBrowser*)browser;
185 |
186 | @end
187 |
188 | //Your ProgressView's
189 | @protocol ZYQProgressViewProtocol
190 |
191 | @required
192 | @property(nonatomic,assign)CGFloat zyq_progress;
193 |
194 | @end
195 |
196 | ```
197 | Example
198 |
199 | add `pod 'YYWebImage'` to your Podfile.
200 |
201 | ``` objective-c
202 |
203 | @interface ZYQPhoto (Custom)
204 |
205 | @end
206 |
207 | @implementation ZYQPhoto (Custom)
208 |
209 | -(void)loadImageWithURL:(NSURL*)url updateProgressBlock:(void (^)(CGFloat progress))updateProgressBlock completedBlock:(void (^)(UIImage *image,NSError *error))completedBlock{
210 | [[YYWebImageManager sharedManager] requestImageWithURL:url options:YYWebImageOptionAllowBackgroundTask progress:^(NSInteger receivedSize, NSInteger expectedSize) {
211 | if (updateProgressBlock) {
212 | updateProgressBlock(receivedSize/expectedSize*1.0);
213 | }
214 | } transform:nil completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
215 | if (completedBlock) {
216 | completedBlock(image,error);
217 | }
218 | }];
219 | }
220 |
221 | -(id)loadImageWithFile:(NSString *)path{
222 | return [YYImage imageWithContentsOfFile:path];
223 | }
224 |
225 | @end
226 |
227 | @interface ZYQZoomingScrollView (Custom)
228 |
229 | @end
230 |
231 | @implementation ZYQZoomingScrollView (Custom)
232 |
233 | -(id)getCustomProgressViewWithBrowser:(ZYQPhotoBrowser *)browser{
234 | CGRect screenBound = [[UIScreen mainScreen] bounds];
235 | CGFloat screenWidth = screenBound.size.width;
236 | CGFloat screenHeight = screenBound.size.height;
237 |
238 | DACircularProgressView *tmpProgressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake((screenWidth-35.)/2., (screenHeight-35.)/2, 35.0f, 35.0f)];
239 | [tmpProgressView setProgress:0.0f];
240 | tmpProgressView.tag = 101;
241 | tmpProgressView.thicknessRatio = 0.1;
242 | tmpProgressView.roundedCorners = NO;
243 | tmpProgressView.trackTintColor = browser.trackTintColor ? browser.trackTintColor : [UIColor colorWithWhite:0.2 alpha:1];
244 | tmpProgressView.progressTintColor = browser.progressTintColor ? browser.progressTintColor : [UIColor colorWithWhite:1.0 alpha:1];
245 |
246 | _progressView=tmpProgressView;
247 |
248 | return tmpProgressView;
249 | }
250 |
251 | @end
252 |
253 | @interface DACircularProgressView (Custom)
254 |
255 | @end
256 |
257 | @implementation DACircularProgressView (Custom)
258 |
259 | -(void)setZyq_progress:(CGFloat)zyq_progress{
260 | [self setProgress:zyq_progress animated:YES];
261 | }
262 |
263 | -(CGFloat)zyq_progress{
264 | return [self progress];
265 | }
266 |
267 | @end
268 | ```
269 |
270 |
271 | ## Adding to your project
272 |
273 | ### Using CocoaPods
274 |
275 | Just add `pod 'ZYQPhotoBrowser'` to your Podfile.
276 |
277 | If you want to use custom libraries
278 |
279 | Just add `pod 'ZYQPhotoBrowser-NoDependency'` to your Podfile.
280 |
281 | ### Including Source Directly Into Your Project
282 |
283 | #### ZYQPhotoBrowser Opensource libraries used
284 |
285 | - [SDWebImage](https://github.com/rs/SDWebImage)
286 | - [DACircularProgress](https://github.com/danielamitay/DACircularProgress)
287 |
288 | ## Licence
289 |
290 | This project uses MIT License.
291 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser-NoDependency.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "ZYQPhotoBrowser-NoDependency"
3 | s.summary = "Photo Browser / Viewer inspired by Facebook's and Tweetbot's with ARC support, swipe-to-dismiss, image progress and more."
4 | s.version = "1.2"
5 | s.homepage = "https://github.com/heroims/ZYQPhotoBrowser"
6 | s.license = { :type => 'MIT', :file => 'README.md' }
7 | s.author = { "Eduardo Callado" => "eduardo_tasker@hotmail.com" }
8 | s.source = { :git => "https://github.com/heroims/ZYQPhotoBrowser.git", :tag => "#{s.version}" }
9 | s.platform = :ios, '6.0'
10 | s.requires_arc = true
11 | s.framework = 'MessageUI', 'QuartzCore', 'SystemConfiguration', 'MobileCoreServices', 'Security'
12 | s.source_files = 'ZYQPhotoBrowser/*.{h,m}'
13 | s.resources = 'ZYQPhotoBrowser/ZYQPhotoBrowser.bundle', 'ZYQPhotoBrowser/ZYQPBLocalizations.bundle'
14 | s.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'NODEPENDENCY=11111'}
15 | end
16 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "ZYQPhotoBrowser"
3 | s.summary = "Photo Browser / Viewer inspired by Facebook's and Tweetbot's with ARC support, swipe-to-dismiss, image progress and more."
4 | s.version = "1.2"
5 | s.homepage = "https://github.com/heroims/ZYQPhotoBrowser"
6 | s.license = { :type => 'MIT', :file => 'README.md' }
7 | s.author = { "Eduardo Callado" => "eduardo_tasker@hotmail.com" }
8 | s.source = { :git => "https://github.com/heroims/ZYQPhotoBrowser.git", :tag => "#{s.version}" }
9 | s.platform = :ios, '6.0'
10 | s.requires_arc = true
11 | s.framework = 'MessageUI', 'QuartzCore', 'SystemConfiguration', 'MobileCoreServices', 'Security'
12 | s.source_files = 'ZYQPhotoBrowser/*.{h,m}'
13 | s.resources = 'ZYQPhotoBrowser/ZYQPhotoBrowser.bundle', 'ZYQPhotoBrowser/ZYQPBLocalizations.bundle'
14 | s.dependency 'SDWebImage', '4.0.0'
15 | s.dependency 'SDWebImage/GIF', '4.0.0'
16 |
17 | s.dependency 'DACircularProgress'
18 | end
19 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQCaptionView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQCaptionView.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 30/12/2011.
6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ZYQPhotoProtocol.h"
11 |
12 | @interface ZYQCaptionView : UIView
13 |
14 | @property (nonatomic, strong) UILabel *label;
15 | @property (nonatomic, strong, readonly) id photo;
16 |
17 | // Init
18 | - (id)initWithPhoto:(id)photo;
19 |
20 | // To create your own custom caption view, subclass this view
21 | // and override the following two methods (as well as any other
22 | // UIView methods that you see fit):
23 |
24 | // Override -setupCaption so setup your subviews and customise the appearance
25 | // of your custom caption
26 | // You can access the photo's data by accessing the _photo ivar
27 | // If you need more data per photo then simply subclass ZYQPhoto and return your
28 | // subclass to the photo browsers -photoBrowser:photoAtIndex: delegate method
29 | - (void)setupCaption;
30 |
31 | // Override -sizeThatFits: and return a CGSize specifying the height of your
32 | // custom caption view. With width property is ignored and the caption is displayed
33 | // the full width of the screen
34 | - (CGSize)sizeThatFits:(CGSize)size;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQCaptionView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQCaptionView.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 30/12/2011.
6 | // Copyright (c) 2011 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import "ZYQCaptionView.h"
10 | #import "ZYQPhoto.h"
11 | #import
12 |
13 | static const CGFloat labelPadding = 10;
14 |
15 | // Private
16 | @interface ZYQCaptionView ()
17 |
18 | @property (nonatomic, strong, readwrite) id photo;
19 |
20 | @end
21 |
22 | @implementation ZYQCaptionView
23 |
24 | @synthesize label = _label;
25 | @synthesize photo = _photo;
26 |
27 | - (id)initWithPhoto:(id)photo {
28 | CGRect screenBound = [[UIScreen mainScreen] bounds];
29 | CGFloat screenWidth = screenBound.size.width;
30 |
31 | if ([[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeLeft ||
32 | [[UIDevice currentDevice] orientation] == UIDeviceOrientationLandscapeRight) {
33 | screenWidth = screenBound.size.height;
34 | }
35 |
36 | self = [super initWithFrame:CGRectMake(0, 0, screenWidth, 44)]; // Random initial frame
37 | if (self) {
38 | _photo = photo;
39 | self.opaque = NO;
40 |
41 | [self setBackground];
42 |
43 | [self setupCaption];
44 | }
45 |
46 | return self;
47 | }
48 |
49 | - (CGSize)sizeThatFits:(CGSize)size {
50 | if (_label.text.length == 0) return CGSizeZero;
51 |
52 | CGFloat maxHeight = 9999;
53 | if (_label.numberOfLines > 0) maxHeight = _label.font.leading*_label.numberOfLines;
54 |
55 | /*CGSize textSizeOLD = [_label.text sizeWithFont:_label.font
56 | constrainedToSize:CGSizeMake(size.width - labelPadding*2, maxHeight)
57 | lineBreakMode:_label.lineBreakMode];*/
58 |
59 | CGFloat width = size.width - labelPadding*2;
60 |
61 | CGFloat height = [_label sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)].height;
62 | return CGSizeMake(size.width, height + labelPadding * 2);
63 | }
64 |
65 | - (void)setupCaption {
66 | _label = [[UILabel alloc] initWithFrame:CGRectMake(labelPadding, 0,
67 | self.bounds.size.width-labelPadding*2,
68 | self.bounds.size.height)];
69 | _label.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
70 | _label.opaque = NO;
71 | _label.backgroundColor = [UIColor clearColor];
72 | _label.textAlignment = NSTextAlignmentCenter;
73 | _label.lineBreakMode = NSLineBreakByWordWrapping;
74 | _label.numberOfLines = 3;
75 | _label.textColor = [UIColor whiteColor];
76 | _label.shadowColor = [UIColor colorWithWhite:0 alpha:0.5];
77 | _label.shadowOffset = CGSizeMake(0, 1);
78 | _label.font = [UIFont systemFontOfSize:17];
79 | if ([_photo respondsToSelector:@selector(caption)]) {
80 | _label.text = [_photo caption] ? [_photo caption] : @" ";
81 | }
82 |
83 | [self addSubview:_label];
84 | }
85 |
86 | - (void)setBackground {
87 | UIView *fadeView = [[UIView alloc] initWithFrame:CGRectMake(0, -100, 10000, 130+100)]; // Static width, autoresizingMask is not working
88 | CAGradientLayer *gradient = [CAGradientLayer layer];
89 | gradient.frame = fadeView.bounds;
90 | gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0 alpha:0.0] CGColor], (id)[[UIColor colorWithWhite:0 alpha:0.8] CGColor], nil];
91 | [fadeView.layer insertSublayer:gradient atIndex:0];
92 | fadeView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; //UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin;
93 | [self addSubview:fadeView];
94 | }
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBConstants.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhotoBrowserConstants.h
3 | // PhotoBrowserDemo
4 | //
5 | // Created by Eduardo Callado on 10/7/13.
6 | //
7 | //
8 |
9 | #define PADDING 10
10 | #define PAGE_INDEX_TAG_OFFSET 1000
11 | #define PAGE_INDEX(page) ([(page) tag] - PAGE_INDEX_TAG_OFFSET)
12 |
13 | // Debug Logging
14 | #if 0 // Set to 1 to enable debug logging
15 | #define ZYQLog(x, ...) NSLog(x, ## __VA_ARGS__);
16 | #else
17 | #define ZYQLog(x, ...)
18 | #endif
19 |
20 | //
21 | // System Versioning Preprocessor Macros
22 | //
23 |
24 | //#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
25 | //#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
26 | //#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
27 | //#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
28 | //#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
29 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/de.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* No comment provided by engineer. */
2 | "Cancel" = "Abbrechen";
3 |
4 | /* Informing the user an item has finished copying */
5 | "Copied" = "Kopiert";
6 |
7 | /* No comment provided by engineer. */
8 | "Copy" = "Kopieren";
9 |
10 | /* Displayed with ellipsis as 'Copying...' when an item is in the process of being copied */
11 | "Copying" = "Kopiere";
12 |
13 | /* No comment provided by engineer. */
14 | "Dismiss" = "Schließen";
15 |
16 | /* No comment provided by engineer. */
17 | "Done" = "Fertig";
18 |
19 | /* No comment provided by engineer. */
20 | "Email" = "Email";
21 |
22 | /* No comment provided by engineer. */
23 | "Email failed to send. Please try again." = "Senden fehlgeschlagen. Bitte erneut versuchen.";
24 |
25 | /* Informing the user a process has failed */
26 | "Failed" = "Fehlgeschlagen";
27 |
28 | /* Used in the context: 'Showing 1 of 3 items' */
29 | "of" = "von";
30 |
31 | /* No comment provided by engineer. */
32 | "Photo" = "Foto";
33 |
34 | /* Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared */
35 | "Preparing" = "Vorbereiten";
36 |
37 | /* No comment provided by engineer. */
38 | "Save" = "Sichern";
39 |
40 | /* Informing the user an item has been saved */
41 | "Saved" = "Gesichert";
42 |
43 | /* Displayed with ellipsis as 'Saving...' when an item is in the process of being saved */
44 | "Saving" = "Sichere";
45 |
46 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/en.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/es.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/es.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/fr.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/fr.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/ja.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* No comment provided by engineer. */
2 | "Cancel" = "キャンセル";
3 |
4 | /* Informing the user an item has finished copying */
5 | "Copied" = "コピーされました";
6 |
7 | /* No comment provided by engineer. */
8 | "Copy" = "コピー";
9 |
10 | /* Displayed with ellipsis as 'Copying...' when an item is in the process of being copied */
11 | "Copying" = "コピー中";
12 |
13 | /* No comment provided by engineer. */
14 | "Dismiss" = "閉じる";
15 |
16 | /* No comment provided by engineer. */
17 | "Done" = "完了";
18 |
19 | /* No comment provided by engineer. */
20 | "Email" = "メール";
21 |
22 | /* No comment provided by engineer. */
23 | "Email failed to send. Please try again." = "メールの送信に失敗しました。もう一度送信してください。";
24 |
25 | /* Informing the user a process has failed */
26 | "Failed" = "失敗しました";
27 |
28 | /* Used in the context: 'Showing 1 of 3 items' */
29 | "of" = "/";
30 |
31 | /* No comment provided by engineer. */
32 | "Photo" = "写真";
33 |
34 | /* Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared */
35 | "Preparing" = "読み込み中";
36 |
37 | /* No comment provided by engineer. */
38 | "Save" = "保存";
39 |
40 | /* Informing the user an item has been saved */
41 | "Saved" = "保存されました";
42 |
43 | /* Displayed with ellipsis as 'Saving...' when an item is in the process of being saved */
44 | "Saving" = "保存中";
45 |
46 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/nl.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* No comment provided by engineer. */
2 | "Cancel" = "Annuleer";
3 |
4 | /* Informing the user an item has finished copying */
5 | "Copied" = "Gekopieerd";
6 |
7 | /* No comment provided by engineer. */
8 | "Copy" = "Kopieer";
9 |
10 | /* Displayed with ellipsis as 'Copying...' when an item is in the process of being copied */
11 | "Copying" = "Kopieer";
12 |
13 | /* No comment provided by engineer. */
14 | "Dismiss" = "Annuleer";
15 |
16 | /* No comment provided by engineer. */
17 | "Done" = "Klaae";
18 |
19 | /* No comment provided by engineer. */
20 | "Email" = "E-mail";
21 |
22 | /* No comment provided by engineer. */
23 | "Email failed to send. Please try again." = "Het versturen van de e-mail is mislukt. Probeer opnieuw.";
24 |
25 | /* Informing the user a process has failed */
26 | "Failed" = "Mislukt";
27 |
28 | /* Used in the context: 'Showing 1 of 3 items' */
29 | "of" = "of";
30 |
31 | /* No comment provided by engineer. */
32 | "Photo" = "Foto";
33 |
34 | /* Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared */
35 | "Preparing" = "Voorbereiden";
36 |
37 | /* No comment provided by engineer. */
38 | "Save" = "Opslaan";
39 |
40 | /* Informing the user an item has been saved */
41 | "Saved" = "Opgeslagen";
42 |
43 | /* Displayed with ellipsis as 'Saving...' when an item is in the process of being saved */
44 | "Saving" = "Opslaan";
45 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/pt.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /* No comment provided by engineer. */
2 | "Cancel" = "Cancelar";
3 |
4 | /* Informing the user an item has finished copying */
5 | "Copied" = "Copiado";
6 |
7 | /* No comment provided by engineer. */
8 | "Copy" = "Copiar";
9 |
10 | /* Displayed with ellipsis as 'Copying...' when an item is in the process of being copied */
11 | "Copying" = "Copiando";
12 |
13 | /* No comment provided by engineer. */
14 | "Dismiss" = "Fechar";
15 |
16 | /* No comment provided by engineer. */
17 | "Done" = "Fechar";
18 |
19 | /* No comment provided by engineer. */
20 | "Email" = "Email";
21 |
22 | /* No comment provided by engineer. */
23 | "Email failed to send. Please try again." = "Email falhou ao enviar. Por favor, tente novamente.";
24 |
25 | /* Informing the user a process has failed */
26 | "Failed" = "Falhou";
27 |
28 | /* Used in the context: 'Showing 1 of 3 items' */
29 | "of" = "de";
30 |
31 | /* No comment provided by engineer. */
32 | "Photo" = "Foto";
33 |
34 | /* Displayed with ellipsis as 'Preparing...' when an item is in the process of being prepared */
35 | "Preparing" = "Preparando";
36 |
37 | /* No comment provided by engineer. */
38 | "Save" = "Salvar";
39 |
40 | /* Informing the user an item has been saved */
41 | "Saved" = "Salvada";
42 |
43 | /* Displayed with ellipsis as 'Saving...' when an item is in the process of being saved */
44 | "Saving" = "Salvando";
45 |
46 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/ru.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/ru.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPBLocalizations.bundle/zh-Hans.lproj/Localizable.strings
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhoto.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhoto.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 17/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ZYQPhotoProtocol.h"
11 | #ifdef NODEPENDENCY
12 | #else
13 | #import
14 | #endif
15 |
16 | // This class models a photo/image and it's caption
17 | // If you want to handle photos, caching, decompression
18 | // yourself then you can simply ensure your custom data model
19 | // conforms to ZYQPhotoProtocol
20 | // Progress download block, used to update the progressView
21 | typedef void (^ZYQProgressUpdateBlock)(CGFloat progress);
22 |
23 | @interface ZYQPhoto : NSObject
24 |
25 | // Properties
26 | @property (nonatomic, strong) NSString *caption;
27 | @property (nonatomic, strong) NSURL *photoURL;
28 | @property (nonatomic, strong) ZYQProgressUpdateBlock progressUpdateBlock;
29 | @property (nonatomic, strong) UIImage *placeholderImage;
30 |
31 | // Class
32 | + (ZYQPhoto *)photoWithImage:(UIImage *)image;
33 | + (ZYQPhoto *)photoWithFilePath:(NSString *)path;
34 | + (ZYQPhoto *)photoWithURL:(NSURL *)url;
35 |
36 | + (NSArray *)photosWithImages:(NSArray *)imagesArray;
37 | + (NSArray *)photosWithFilePaths:(NSArray *)pathsArray;
38 | + (NSArray *)photosWithURLs:(NSArray *)urlsArray;
39 |
40 | // Init
41 | - (id)initWithImage:(UIImage *)image;
42 | - (id)initWithFilePath:(NSString *)path;
43 | - (id)initWithURL:(NSURL *)url;
44 |
45 | @end
46 |
47 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhoto.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhoto.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 17/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import "ZYQPhoto.h"
10 | #import "ZYQPhotoBrowser.h"
11 | #import
12 |
13 | #ifdef NODEPENDENCY
14 | #else
15 | #import "UIImage+GIF.h"
16 | #import "FLAnimatedImage.h"
17 | #import "NSData+ImageContentType.h"
18 | #endif
19 |
20 | // Private
21 | @interface ZYQPhoto () {
22 | // Image Sources
23 | NSString *_photoPath;
24 |
25 | // Image
26 | UIImage *_underlyingImage;
27 |
28 | // Other
29 | NSString *_caption;
30 | BOOL _loadingInProgress;
31 | }
32 |
33 | // Properties
34 | @property (nonatomic, strong) UIImage *underlyingImage;
35 |
36 | // Methods
37 | - (void)imageLoadingComplete;
38 |
39 | @end
40 |
41 | // ZYQPhoto
42 | @implementation ZYQPhoto
43 |
44 | // Properties
45 | @synthesize underlyingImage = _underlyingImage,
46 | photoURL = _photoURL,
47 | caption = _caption;
48 |
49 | #pragma mark Class Methods
50 |
51 | + (ZYQPhoto *)photoWithImage:(UIImage *)image {
52 | return [[ZYQPhoto alloc] initWithImage:image];
53 | }
54 |
55 | + (ZYQPhoto *)photoWithFilePath:(NSString *)path {
56 | return [[ZYQPhoto alloc] initWithFilePath:path];
57 | }
58 |
59 | + (ZYQPhoto *)photoWithURL:(NSURL *)url {
60 | return [[ZYQPhoto alloc] initWithURL:url];
61 | }
62 |
63 | + (NSArray *)photosWithImages:(NSArray *)imagesArray {
64 | NSMutableArray *photos = [NSMutableArray arrayWithCapacity:imagesArray.count];
65 |
66 | for (UIImage *image in imagesArray) {
67 | if ([image isKindOfClass:[UIImage class]]) {
68 | ZYQPhoto *photo = [ZYQPhoto photoWithImage:image];
69 | [photos addObject:photo];
70 | }
71 | }
72 |
73 | return photos;
74 | }
75 |
76 | + (NSArray *)photosWithFilePaths:(NSArray *)pathsArray {
77 | NSMutableArray *photos = [NSMutableArray arrayWithCapacity:pathsArray.count];
78 |
79 | for (NSString *path in pathsArray) {
80 | if ([path isKindOfClass:[NSString class]]) {
81 | ZYQPhoto *photo = [ZYQPhoto photoWithFilePath:path];
82 | [photos addObject:photo];
83 | }
84 | }
85 |
86 | return photos;
87 | }
88 |
89 | + (NSArray *)photosWithURLs:(NSArray *)urlsArray {
90 | NSMutableArray *photos = [NSMutableArray arrayWithCapacity:urlsArray.count];
91 |
92 | for (id url in urlsArray) {
93 | if ([url isKindOfClass:[NSURL class]]) {
94 | ZYQPhoto *photo = [ZYQPhoto photoWithURL:url];
95 | [photos addObject:photo];
96 | }
97 | else if ([url isKindOfClass:[NSString class]]) {
98 | ZYQPhoto *photo = [ZYQPhoto photoWithURL:[NSURL URLWithString:url]];
99 | [photos addObject:photo];
100 | }
101 | }
102 |
103 | return photos;
104 | }
105 |
106 | #pragma mark NSObject
107 |
108 | - (id)initWithImage:(UIImage *)image {
109 | if ((self = [super init])) {
110 | self.underlyingImage = image;
111 | }
112 | return self;
113 | }
114 |
115 | - (id)initWithFilePath:(NSString *)path {
116 | if ((self = [super init])) {
117 | _photoPath = [path copy];
118 | }
119 | return self;
120 | }
121 |
122 | - (id)initWithURL:(NSURL *)url {
123 | if ((self = [super init])) {
124 | _photoURL = [url copy];
125 | }
126 | return self;
127 | }
128 |
129 | #pragma mark ZYQPhoto Protocol Methods
130 |
131 | - (UIImage *)underlyingImage {
132 | return _underlyingImage;
133 | }
134 |
135 | - (void)loadUnderlyingImageAndNotify {
136 | NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
137 | _loadingInProgress = YES;
138 | if (self.underlyingImage) {
139 | // Image already loaded
140 | [self imageLoadingComplete];
141 | } else {
142 | if (_photoPath) {
143 | // Load async from file
144 | [self performSelectorInBackground:@selector(loadImageFromFileAsync) withObject:nil];
145 | } else if (_photoURL) {
146 | // Load async from web (using SDWebImageManager)
147 | if ([self conformsToProtocol:objc_getProtocol("ZYQPhotoCustomLoadProtocol")]) {
148 | [(ZYQPhoto*)self loadImageWithURL:_photoURL updateProgressBlock:^(CGFloat progress) {
149 | if (self.progressUpdateBlock) {
150 | self.progressUpdateBlock(progress);
151 | }
152 | } completedBlock:^(UIImage *image, NSError *error) {
153 | if (image) {
154 | self.underlyingImage = image;
155 | }
156 |
157 | [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
158 | }];
159 | }
160 | else{
161 | #ifdef NODEPENDENCY
162 | #else
163 | [[SDWebImageManager sharedManager] loadImageWithURL:_photoURL options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
164 | CGFloat progress = ((CGFloat)receivedSize)/((CGFloat)expectedSize);
165 |
166 | if (self.progressUpdateBlock) {
167 | self.progressUpdateBlock(progress);
168 | }
169 | } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
170 | if (image) {
171 | if (image.isGIF) {
172 | self.underlyingImage=(UIImage*)[FLAnimatedImage animatedImageWithGIFData:data];
173 | }
174 | else{
175 | self.underlyingImage = image;
176 | }
177 | }
178 |
179 | [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
180 | }];
181 | #endif
182 | }
183 |
184 |
185 | } else {
186 | // Failed - no source
187 | self.underlyingImage = nil;
188 | [self imageLoadingComplete];
189 | }
190 | }
191 | }
192 |
193 | // Release if we can get it again from path or url
194 | - (void)unloadUnderlyingImage {
195 | _loadingInProgress = NO;
196 |
197 | if (self.underlyingImage && (_photoPath || _photoURL)) {
198 | self.underlyingImage = nil;
199 | }
200 | }
201 |
202 | #pragma mark - Async Loading
203 |
204 | /*- (UIImage *)decodedImageWithImage:(UIImage *)image {
205 | CGImageRef imageRef = image.CGImage;
206 | // System only supports RGB, set explicitly and prevent context error
207 | // if the downloaded image is not the supported format
208 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
209 |
210 | CGContextRef context = CGBitmapContextCreate(NULL,
211 | CGImageGetWidth(imageRef),
212 | CGImageGetHeight(imageRef),
213 | 8,
214 | // width * 4 will be enough because are in ARGB format, don't read from the image
215 | CGImageGetWidth(imageRef) * 4,
216 | colorSpace,
217 | // kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
218 | // makes system don't need to do extra conversion when displayed.
219 | kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
220 | CGColorSpaceRelease(colorSpace);
221 |
222 | if ( ! context) {
223 | return nil;
224 | }
225 |
226 | CGRect rect = (CGRect){CGPointZero, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)};
227 | CGContextDrawImage(context, rect, imageRef);
228 | CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
229 | CGContextRelease(context);
230 |
231 | UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef];
232 | CGImageRelease(decompressedImageRef);
233 | return decompressedImage;
234 | }*/
235 |
236 | - (UIImage *)decodedImageWithImage:(UIImage *)image {
237 | if (![image isKindOfClass:[UIImage class]]||image.images) {
238 | // Do not decode animated images
239 | return image;
240 | }
241 |
242 | CGImageRef imageRef = image.CGImage;
243 | CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
244 | CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize};
245 |
246 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
247 | CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
248 |
249 | int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask);
250 | BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone ||
251 | infoMask == kCGImageAlphaNoneSkipFirst ||
252 | infoMask == kCGImageAlphaNoneSkipLast);
253 |
254 | // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB.
255 | // https://developer.apple.com/library/mac/#qa/qa1037/_index.html
256 | if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1)
257 | {
258 | // Unset the old alpha info.
259 | bitmapInfo &= ~kCGBitmapAlphaInfoMask;
260 |
261 | // Set noneSkipFirst.
262 | bitmapInfo |= kCGImageAlphaNoneSkipFirst;
263 | }
264 | // Some PNGs tell us they have alpha but only 3 components. Odd.
265 | else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3)
266 | {
267 | // Unset the old alpha info.
268 | bitmapInfo &= ~kCGBitmapAlphaInfoMask;
269 | bitmapInfo |= kCGImageAlphaPremultipliedFirst;
270 | }
271 |
272 | // It calculates the bytes-per-row based on the bitsPerComponent and width arguments.
273 | CGContextRef context = CGBitmapContextCreate(NULL,
274 | imageSize.width,
275 | imageSize.height,
276 | CGImageGetBitsPerComponent(imageRef),
277 | 0,
278 | colorSpace,
279 | bitmapInfo);
280 | CGColorSpaceRelease(colorSpace);
281 |
282 | // If failed, return undecompressed image
283 | if (!context) return image;
284 |
285 | CGContextDrawImage(context, imageRect, imageRef);
286 | CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
287 |
288 | CGContextRelease(context);
289 |
290 | UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation];
291 | CGImageRelease(decompressedImageRef);
292 | return decompressedImage;
293 | }
294 |
295 | // Called in background
296 | // Load image in background from local file
297 | - (void)loadImageFromFileAsync {
298 | @autoreleasepool {
299 | @try {
300 | if ([self conformsToProtocol:objc_getProtocol("ZYQPhotoCustomLoadProtocol")]) {
301 | self.underlyingImage=[(ZYQPhoto*)self loadImageWithFile:_photoPath];
302 | }
303 | else{
304 | self.underlyingImage = [UIImage imageWithContentsOfFile:_photoPath];
305 | #ifdef NODEPENDENCY
306 | #else
307 | NSData *imageData=[NSData dataWithContentsOfFile:_photoPath];
308 | if (self.underlyingImage.isGIF||SDImageFormatGIF== [NSData sd_imageFormatForImageData:imageData]) {
309 | self.underlyingImage=(UIImage*)[FLAnimatedImage animatedImageWithGIFData:imageData];
310 | }
311 |
312 | #endif
313 | }
314 |
315 | if (!_underlyingImage) {
316 | ZYQLog(@"Error loading photo from path: %@", _photoPath);
317 | }
318 | } @finally {
319 | if (![self conformsToProtocol:objc_getProtocol("ZYQPhotoCustomLoadProtocol")]) {
320 | self.underlyingImage = [self decodedImageWithImage: self.underlyingImage];
321 | }
322 | [self performSelectorOnMainThread:@selector(imageLoadingComplete) withObject:nil waitUntilDone:NO];
323 | }
324 | }
325 | }
326 |
327 | // Called on main
328 | - (void)imageLoadingComplete {
329 | NSAssert([[NSThread currentThread] isMainThread], @"This method must be called on the main thread.");
330 | // Complete so notify
331 | _loadingInProgress = NO;
332 | [[NSNotificationCenter defaultCenter] postNotificationName:ZYQPhoto_LOADING_DID_END_NOTIFICATION
333 | object:self];
334 | }
335 |
336 | @end
337 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowLeft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowLeft.png
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowLeft@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowLeft@2x.png
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowRight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowRight.png
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowRight@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heroims/ZYQPhotoBrowser/0bfa3ce3a5cf5ce4a452bf1c5d5335a066a4a367/ZYQPhotoBrowser/ZYQPhotoBrowser.bundle/images/IDMPhotoBrowser_arrowRight@2x.png
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhotoBrowser.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 14/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #import "ZYQPhoto.h"
13 | #import "ZYQPhotoProtocol.h"
14 | #import "ZYQCaptionView.h"
15 | #import "ZYQTapDetectingImageView.h"
16 |
17 | // Delgate
18 | @class ZYQPhotoBrowser;
19 | @protocol ZYQPhotoBrowserDelegate
20 | @optional
21 | - (void)willAppearPhotoBrowser:(ZYQPhotoBrowser *)photoBrowser;
22 | - (void)willDisappearPhotoBrowser:(ZYQPhotoBrowser *)photoBrowser;
23 | - (void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didShowPhotoAtIndex:(NSUInteger)index;
24 | - (void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didDismissAtPageIndex:(NSUInteger)index;
25 | - (void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser willDismissAtPageIndex:(NSUInteger)index;
26 | - (void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser didDismissActionSheetWithButtonIndex:(NSUInteger)buttonIndex photoIndex:(NSUInteger)photoIndex;
27 | - (NSString *)photoBrowser:(ZYQPhotoBrowser *)photoBrowser counterForPhotoAtIndex:(NSUInteger)index;
28 | - (NSString *)photoBrowser:(ZYQPhotoBrowser *)photoBrowser titleForPhotoAtIndex:(NSUInteger)index;
29 |
30 | - (ZYQCaptionView *)photoBrowser:(ZYQPhotoBrowser *)photoBrowser captionViewForPhotoAtIndex:(NSUInteger)index;
31 | - (void)photoBrowser:(ZYQPhotoBrowser *)photoBrowser imageFailed:(NSUInteger)index imageView:(ZYQTapDetectingImageView *)imageView;
32 | @end
33 |
34 | // ZYQPhotoBrowser
35 | @interface ZYQPhotoBrowser : UIViewController
36 |
37 | // Properties
38 | @property (nonatomic, strong) id delegate;
39 |
40 | // Toolbar customization
41 | @property (nonatomic) BOOL displayToolbar;
42 | @property (nonatomic) BOOL displayCounterLabel;
43 | @property (nonatomic) BOOL displayArrowButton;
44 | @property (nonatomic) BOOL displayActionButton;
45 | @property (nonatomic, strong) NSArray *actionButtonTitles;
46 | @property (nonatomic, weak) UIImage *leftArrowImage, *leftArrowSelectedImage;
47 | @property (nonatomic, weak) UIImage *rightArrowImage, *rightArrowSelectedImage;
48 | @property (nonatomic, weak) UIImage *actionButtonImage, *actionButtonSelectedImage;
49 |
50 | // View customization
51 | @property (nonatomic) BOOL displayDoneButton;
52 | @property (nonatomic) BOOL displayTitle;
53 | @property (nonatomic) BOOL useWhiteBackgroundColor;
54 | @property (nonatomic, weak) UIImage *doneButtonImage;
55 | @property (nonatomic, weak) UIColor *trackTintColor, *progressTintColor;
56 | @property (nonatomic, assign) CGFloat doneButtonRightInset, doneButtonTopInset;
57 | @property (nonatomic, assign) CGSize doneButtonSize;
58 | @property (nonatomic, strong) UIView *customBackgroud;
59 |
60 | @property (nonatomic, assign) Class gifSupportImageViewClass;
61 |
62 | @property (nonatomic, weak) UIImage *scaleImage;
63 |
64 | @property (nonatomic) BOOL arrowButtonsChangePhotosAnimated;
65 |
66 | @property (nonatomic) BOOL forceHideStatusBar;
67 | @property (nonatomic) BOOL useZoomAnimation;
68 | @property (nonatomic) BOOL disableVerticalSwipe;
69 |
70 | @property (nonatomic) BOOL dismissOnTouch;
71 |
72 | // Default value: true
73 | // Set to false to tell the photo viewer not to hide the interface when scrolling
74 | @property (nonatomic) BOOL autoHideInterface;
75 |
76 | // Defines zooming of the background (default 1.0)
77 | @property (nonatomic) float backgroundScaleFactor;
78 |
79 | // Animation time (default .28)
80 | @property (nonatomic) float animationDuration;
81 |
82 | // Init
83 | - (id)initWithPhotos:(NSArray *)photosArray;
84 |
85 | // Init (animated from view)
86 | - (id)initWithPhotos:(NSArray *)photosArray animatedFromView:(UIView*)view;
87 |
88 | // Init with NSURL objects
89 | - (id)initWithPhotoURLs:(NSArray *)photoURLsArray;
90 |
91 | // Init with NSURL objects (animated from view)
92 | - (id)initWithPhotoURLs:(NSArray *)photoURLsArray animatedFromView:(UIView*)view;
93 |
94 | // Reloads the photo browser and refetches data
95 | - (void)reloadData;
96 |
97 | // Set page that photo browser starts on
98 | - (void)setInitialPageIndex:(NSUInteger)index;
99 |
100 | // Get ZYQPhoto at index
101 | - (id)photoAtIndex:(NSUInteger)index;
102 |
103 | // Get Photos
104 | @property(nonatomic,readonly)NSArray *photos;
105 |
106 | @end
107 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoBrowser.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhotoBrowser.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 14/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ZYQPhotoBrowser.h"
11 | #import "ZYQZoomingScrollView.h"
12 | #import
13 | #import
14 |
15 | #ifndef ZYQPhotoBrowserLocalizedStrings
16 | #define ZYQPhotoBrowserLocalizedStrings(key) \
17 | NSLocalizedStringFromTableInBundle((key), nil, [NSBundle bundleWithPath:[[NSBundle bundleForClass: [ZYQPhotoBrowser class]] pathForResource:@"ZYQPBLocalizations" ofType:@"bundle"]], nil)
18 | #endif
19 |
20 | // Private
21 | @interface ZYQPhotoBrowser () {
22 | // Data
23 | NSMutableArray *_photos;
24 |
25 | // Views
26 | UIScrollView *_pagingScrollView;
27 |
28 | // Gesture
29 | UIPanGestureRecognizer *_panGesture;
30 |
31 | // Paging
32 | NSMutableSet *_visiblePages, *_recycledPages;
33 | NSUInteger _pageIndexBeforeRotation;
34 | NSUInteger _currentPageIndex;
35 |
36 | // Buttons
37 | UIButton *_doneButton;
38 |
39 | // Title
40 | UILabel *_titleLabel;
41 |
42 | // Toolbar
43 | UIToolbar *_toolbar;
44 | UIBarButtonItem *_previousButton, *_nextButton, *_actionButton;
45 | UIBarButtonItem *_counterButton;
46 | UILabel *_counterLabel;
47 |
48 | // Actions
49 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
50 | UIAlertController *_actionsSheet;
51 | #else
52 | UIActionSheet *_actionsSheet;
53 | #endif
54 | UIActivityViewController *activityViewController;
55 |
56 | // Control
57 | NSTimer *_controlVisibilityTimer;
58 |
59 | // Appearance
60 | //UIStatusBarStyle _previousStatusBarStyle;
61 | BOOL _statusBarOriginallyHidden;
62 |
63 | // Present
64 | UIView *_senderViewForAnimation;
65 |
66 | // Misc
67 | BOOL _performingLayout;
68 | BOOL _rotating;
69 | BOOL _viewIsActive; // active as in it's in the view heirarchy
70 | BOOL _autoHide;
71 | NSInteger _initalPageIndex;
72 |
73 | BOOL _isdraggingPhoto;
74 |
75 | CGRect _senderViewOriginalFrame;
76 | //UIImage *_backgroundScreenshot;
77 |
78 | UIWindow *_applicationWindow;
79 |
80 | // iOS 7
81 | UIViewController *_applicationTopViewController;
82 | int _previousModalPresentationStyle;
83 | }
84 |
85 | // Private Properties
86 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
87 | @property (nonatomic, strong) UIAlertController *actionsSheet;
88 | #else
89 | @property (nonatomic, strong) UIActionSheet *actionsSheet;
90 | #endif
91 | @property (nonatomic, strong) UIActivityViewController *activityViewController;
92 |
93 | // Private Methods
94 |
95 | // Layout
96 | - (void)performLayout;
97 |
98 | // Paging
99 | - (void)tilePages;
100 | - (BOOL)isDisplayingPageForIndex:(NSUInteger)index;
101 | - (ZYQZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index;
102 | - (ZYQZoomingScrollView *)pageDisplayingPhoto:(id)photo;
103 | - (ZYQZoomingScrollView *)dequeueRecycledPage;
104 | - (void)configurePage:(ZYQZoomingScrollView *)page forIndex:(NSUInteger)index;
105 | - (void)didStartViewingPageAtIndex:(NSUInteger)index;
106 |
107 | // Frames
108 | - (CGRect)frameForPagingScrollView;
109 | - (CGRect)frameForPageAtIndex:(NSUInteger)index;
110 | - (CGSize)contentSizeForPagingScrollView;
111 | - (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index;
112 | - (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation;
113 | - (CGRect)frameForDoneButtonAtOrientation:(UIInterfaceOrientation)orientation;
114 | - (CGRect)frameForCaptionView:(ZYQCaptionView *)captionView atIndex:(NSUInteger)index;
115 |
116 | // Toolbar
117 | - (void)updateToolbar;
118 |
119 | // Navigation
120 | - (void)jumpToPageAtIndex:(NSUInteger)index;
121 | - (void)gotoPreviousPage;
122 | - (void)gotoNextPage;
123 |
124 | // Controls
125 | - (void)cancelControlHiding;
126 | - (void)hideControlsAfterDelay;
127 | - (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent;
128 | //- (void)toggleControls;
129 | - (BOOL)areControlsHidden;
130 |
131 | // Interactions
132 | - (void)handleSingleTap;
133 |
134 | // Data
135 | - (NSUInteger)numberOfPhotos;
136 | - (id)photoAtIndex:(NSUInteger)index;
137 | - (UIImage *)imageForPhoto:(id)photo;
138 | - (void)loadAdjacentPhotosIfNecessary:(id)photo;
139 | - (void)releaseAllUnderlyingPhotos;
140 |
141 | @end
142 |
143 | // ZYQPhotoBrowser
144 | @implementation ZYQPhotoBrowser
145 |
146 | // Properties
147 | @synthesize displayDoneButton = _displayDoneButton, displayTitle=_displayTitle, displayToolbar = _displayToolbar, displayActionButton = _displayActionButton, displayCounterLabel = _displayCounterLabel, useWhiteBackgroundColor = _useWhiteBackgroundColor, doneButtonImage = _doneButtonImage;
148 | @synthesize leftArrowImage = _leftArrowImage, rightArrowImage = _rightArrowImage, leftArrowSelectedImage = _leftArrowSelectedImage, rightArrowSelectedImage = _rightArrowSelectedImage, actionButtonImage = _actionButtonImage, actionButtonSelectedImage = _actionButtonSelectedImage;
149 | @synthesize displayArrowButton = _displayArrowButton, actionButtonTitles = _actionButtonTitles;
150 | @synthesize arrowButtonsChangePhotosAnimated = _arrowButtonsChangePhotosAnimated;
151 | @synthesize forceHideStatusBar = _forceHideStatusBar;
152 | @synthesize useZoomAnimation = _useZoomAnimation;
153 | @synthesize disableVerticalSwipe = _disableVerticalSwipe;
154 | @synthesize dismissOnTouch = _dismissOnTouch;
155 | @synthesize actionsSheet = _actionsSheet, activityViewController = _activityViewController;
156 | @synthesize customBackgroud = _customBackgroud;
157 | @synthesize gifSupportImageViewClass = _gifSupportImageViewClass;
158 | @synthesize trackTintColor = _trackTintColor, progressTintColor = _progressTintColor;
159 | @synthesize delegate = _delegate;
160 |
161 | #pragma mark - NSObject
162 |
163 | - (id)init {
164 | if ((self = [super init])) {
165 | // Defaults
166 | self.hidesBottomBarWhenPushed = YES;
167 |
168 | _currentPageIndex = 0;
169 | _performingLayout = NO; // Reset on view did appear
170 | _rotating = NO;
171 | _viewIsActive = NO;
172 | _visiblePages = [NSMutableSet new];
173 | _recycledPages = [NSMutableSet new];
174 | _photos = [NSMutableArray new];
175 |
176 | _initalPageIndex = 0;
177 | _autoHide = YES;
178 | _autoHideInterface = YES;
179 |
180 | _displayDoneButton = YES;
181 | _displayTitle=NO;
182 | _doneButtonImage = nil;
183 |
184 | _displayToolbar = YES;
185 | _displayActionButton = YES;
186 | _displayArrowButton = YES;
187 | _displayCounterLabel = NO;
188 |
189 | _forceHideStatusBar = NO;
190 | _useZoomAnimation = NO;
191 | _disableVerticalSwipe = NO;
192 |
193 | _dismissOnTouch = NO;
194 |
195 | _useWhiteBackgroundColor = NO;
196 | _leftArrowImage = _rightArrowImage = _leftArrowSelectedImage = _rightArrowSelectedImage = nil;
197 |
198 | _arrowButtonsChangePhotosAnimated = YES;
199 |
200 | _backgroundScaleFactor = 1.0;
201 | _animationDuration = 0.28;
202 | _senderViewForAnimation = nil;
203 | _scaleImage = nil;
204 |
205 | _isdraggingPhoto = NO;
206 |
207 | _doneButtonRightInset = 20.f;
208 | _doneButtonTopInset = 30.f;
209 | _doneButtonSize = CGSizeMake(55.f, 26.f);
210 |
211 | if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
212 | self.automaticallyAdjustsScrollViewInsets = NO;
213 | }
214 |
215 | _applicationWindow = [[[UIApplication sharedApplication] delegate] window];
216 | self.modalPresentationStyle = UIModalPresentationCustom;
217 | self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
218 | self.modalPresentationCapturesStatusBarAppearance = YES;
219 | self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
220 |
221 | // Listen for ZYQPhoto notifications
222 | [[NSNotificationCenter defaultCenter] addObserver:self
223 | selector:@selector(handleZYQPhotoLoadingDidEndNotification:)
224 | name:ZYQPhoto_LOADING_DID_END_NOTIFICATION
225 | object:nil];
226 | }
227 |
228 | return self;
229 | }
230 |
231 | - (id)initWithPhotos:(NSArray *)photosArray {
232 | if ((self = [self init])) {
233 | _photos = [[NSMutableArray alloc] initWithArray:photosArray];
234 | }
235 | return self;
236 | }
237 |
238 | - (id)initWithPhotos:(NSArray *)photosArray animatedFromView:(UIView*)view {
239 | if ((self = [self init])) {
240 | _photos = [[NSMutableArray alloc] initWithArray:photosArray];
241 | _senderViewForAnimation = view;
242 | }
243 | return self;
244 | }
245 |
246 | - (id)initWithPhotoURLs:(NSArray *)photoURLsArray {
247 | if ((self = [self init])) {
248 | NSArray *photosArray = [ZYQPhoto photosWithURLs:photoURLsArray];
249 | _photos = [[NSMutableArray alloc] initWithArray:photosArray];
250 | }
251 | return self;
252 | }
253 |
254 | - (id)initWithPhotoURLs:(NSArray *)photoURLsArray animatedFromView:(UIView*)view {
255 | if ((self = [self init])) {
256 | NSArray *photosArray = [ZYQPhoto photosWithURLs:photoURLsArray];
257 | _photos = [[NSMutableArray alloc] initWithArray:photosArray];
258 | _senderViewForAnimation = view;
259 | }
260 | return self;
261 | }
262 |
263 | - (void)dealloc {
264 | _pagingScrollView.delegate = nil;
265 | [[NSNotificationCenter defaultCenter] removeObserver:self];
266 | [self releaseAllUnderlyingPhotos];
267 | }
268 |
269 | - (void)releaseAllUnderlyingPhotos {
270 | for (id p in _photos) { if (p != [NSNull null]) [p unloadUnderlyingImage]; } // Release photos
271 | }
272 |
273 | - (void)didReceiveMemoryWarning {
274 | // Release any cached data, images, etc that aren't in use.
275 | [self releaseAllUnderlyingPhotos];
276 | [_recycledPages removeAllObjects];
277 |
278 | // Releases the view if it doesn't have a superview.
279 | [super didReceiveMemoryWarning];
280 | }
281 |
282 | #pragma mark - Pan Gesture
283 |
284 | - (void)panGestureRecognized:(id)sender {
285 | // Initial Setup
286 | ZYQZoomingScrollView *scrollView = [self pageDisplayedAtIndex:_currentPageIndex];
287 | //ZYQTapDetectingImageView *scrollView.photoImageView = scrollView.photoImageView;
288 |
289 | static float firstX, firstY;
290 |
291 | float viewHeight = scrollView.frame.size.height;
292 | float viewHalfHeight = viewHeight/2;
293 |
294 | CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];
295 |
296 | // Gesture Began
297 | if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {
298 | [self setControlsHidden:YES animated:YES permanent:YES];
299 |
300 | firstX = [scrollView center].x;
301 | firstY = [scrollView center].y;
302 |
303 | _senderViewForAnimation.hidden = (_currentPageIndex == _initalPageIndex);
304 |
305 | _isdraggingPhoto = YES;
306 | [self setNeedsStatusBarAppearanceUpdate];
307 | }
308 |
309 | translatedPoint = CGPointMake(firstX, firstY+translatedPoint.y);
310 | [scrollView setCenter:translatedPoint];
311 |
312 | float newY = scrollView.center.y - viewHalfHeight;
313 | float newAlpha = 1 - fabsf(newY)/viewHeight; //abs(newY)/viewHeight * 1.8;
314 |
315 | self.view.opaque = YES;
316 |
317 | self.view.backgroundColor = [UIColor colorWithWhite:(_useWhiteBackgroundColor ? 1 : 0) alpha:newAlpha];
318 |
319 | // Gesture Ended
320 | if ([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {
321 | if(scrollView.center.y > viewHalfHeight+40 || scrollView.center.y < viewHalfHeight-40) // Automatic Dismiss View
322 | {
323 | if (_senderViewForAnimation && _currentPageIndex == _initalPageIndex) {
324 | [self performCloseAnimationWithScrollView:scrollView];
325 | return;
326 | }
327 |
328 | CGFloat finalX = firstX, finalY;
329 |
330 | CGFloat windowsHeigt = [_applicationWindow frame].size.height;
331 |
332 | if(scrollView.center.y > viewHalfHeight+30) // swipe down
333 | finalY = windowsHeigt*2;
334 | else // swipe up
335 | finalY = -viewHalfHeight;
336 |
337 | CGFloat animationDuration = 0.35;
338 |
339 | [UIView beginAnimations:nil context:NULL];
340 | [UIView setAnimationDuration:animationDuration];
341 | [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
342 | [UIView setAnimationDelegate:self];
343 | [scrollView setCenter:CGPointMake(finalX, finalY)];
344 | self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
345 | [UIView commitAnimations];
346 |
347 | [self performSelector:@selector(doneButtonPressed:) withObject:self afterDelay:animationDuration];
348 | }
349 | else // Continue Showing View
350 | {
351 | _isdraggingPhoto = NO;
352 | [self setNeedsStatusBarAppearanceUpdate];
353 | [self setControlsHidden:NO animated:YES permanent:YES];
354 |
355 | self.view.backgroundColor = [UIColor colorWithWhite:(_useWhiteBackgroundColor ? 1 : 0) alpha:1];
356 |
357 | CGFloat velocityY = (.35*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);
358 |
359 | CGFloat finalX = firstX;
360 | CGFloat finalY = viewHalfHeight;
361 |
362 | CGFloat animationDuration = (ABS(velocityY)*.0002)+.2;
363 |
364 | [UIView beginAnimations:nil context:NULL];
365 | [UIView setAnimationDuration:animationDuration];
366 | [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
367 | [UIView setAnimationDelegate:self];
368 | [scrollView setCenter:CGPointMake(finalX, finalY)];
369 | [UIView commitAnimations];
370 | }
371 | }
372 | }
373 |
374 | #pragma mark - Animation
375 |
376 | - (void)performPresentAnimation {
377 | self.view.alpha = 0.0f;
378 | _pagingScrollView.alpha = 0.0f;
379 |
380 | UIImage *imageFromView = _scaleImage ? _scaleImage : [self getImageFromView:_senderViewForAnimation];
381 |
382 | _senderViewOriginalFrame = [_senderViewForAnimation.superview convertRect:_senderViewForAnimation.frame toView:nil];
383 |
384 | UIView *fadeView = [[UIView alloc] initWithFrame:_applicationWindow.bounds];
385 | fadeView.backgroundColor = [UIColor clearColor];
386 | [_applicationWindow addSubview:fadeView];
387 |
388 | UIImageView *resizableImageView = [[UIImageView alloc] initWithImage:imageFromView];
389 | resizableImageView.frame = _senderViewOriginalFrame;
390 | resizableImageView.clipsToBounds = YES;
391 | resizableImageView.contentMode = _senderViewForAnimation ? _senderViewForAnimation.contentMode : UIViewContentModeScaleAspectFill;
392 | resizableImageView.backgroundColor = [UIColor clearColor];
393 | [_applicationWindow addSubview:resizableImageView];
394 | _senderViewForAnimation.hidden = YES;
395 |
396 | void (^completion)() = ^() {
397 | self.view.alpha = 1.0f;
398 | _pagingScrollView.alpha = 1.0f;
399 | resizableImageView.backgroundColor = [UIColor colorWithWhite:(_useWhiteBackgroundColor) ? 1 : 0 alpha:1];
400 | [fadeView removeFromSuperview];
401 | [resizableImageView removeFromSuperview];
402 | };
403 |
404 | [UIView animateWithDuration:_animationDuration animations:^{
405 | fadeView.backgroundColor = self.useWhiteBackgroundColor ? [UIColor whiteColor] : [UIColor blackColor];
406 | } completion:nil];
407 |
408 | CGRect finalImageViewFrame = [self animationFrameForImage:imageFromView presenting:YES scrollView:nil];
409 |
410 | if(_useZoomAnimation)
411 | {
412 | [self animateView:resizableImageView
413 | toFrame:finalImageViewFrame
414 | completion:completion];
415 | }
416 | else
417 | {
418 | [UIView animateWithDuration:_animationDuration animations:^{
419 | resizableImageView.layer.frame = finalImageViewFrame;
420 | } completion:^(BOOL finished) {
421 | completion();
422 | }];
423 | }
424 | }
425 |
426 | - (void)performCloseAnimationWithScrollView:(ZYQZoomingScrollView*)scrollView {
427 | if ([_delegate respondsToSelector:@selector(willDisappearPhotoBrowser:)]) {
428 | [_delegate willDisappearPhotoBrowser:self];
429 | }
430 |
431 | float fadeAlpha = 1 - fabs(scrollView.frame.origin.y)/scrollView.frame.size.height;
432 |
433 | UIImage *imageFromView = [scrollView.photo underlyingImage];
434 | if (!imageFromView && [scrollView.photo respondsToSelector:@selector(placeholderImage)]) {
435 | imageFromView = [scrollView.photo placeholderImage];
436 | }
437 |
438 | UIView *fadeView = [[UIView alloc] initWithFrame:_applicationWindow.bounds];
439 | fadeView.backgroundColor = self.useWhiteBackgroundColor ? [UIColor whiteColor] : [UIColor blackColor];
440 | fadeView.alpha = fadeAlpha;
441 | [_applicationWindow addSubview:fadeView];
442 |
443 | CGRect imageViewFrame = [self animationFrameForImage:imageFromView presenting:NO scrollView:scrollView];
444 |
445 | UIImageView *resizableImageView = [[UIImageView alloc] initWithImage:imageFromView];
446 | resizableImageView.frame = imageViewFrame;
447 | resizableImageView.contentMode = _senderViewForAnimation ? _senderViewForAnimation.contentMode : UIViewContentModeScaleAspectFill;
448 | resizableImageView.backgroundColor = [UIColor clearColor];
449 | resizableImageView.clipsToBounds = YES;
450 | [_applicationWindow addSubview:resizableImageView];
451 | self.view.hidden = YES;
452 |
453 | void (^completion)() = ^() {
454 | _senderViewForAnimation.hidden = NO;
455 | _senderViewForAnimation = nil;
456 | _scaleImage = nil;
457 |
458 | [fadeView removeFromSuperview];
459 | [resizableImageView removeFromSuperview];
460 |
461 | [self prepareForClosePhotoBrowser];
462 | [self dismissPhotoBrowserAnimated:NO];
463 | };
464 |
465 | [UIView animateWithDuration:_animationDuration animations:^{
466 | fadeView.alpha = 0;
467 | self.view.backgroundColor = [UIColor clearColor];
468 | } completion:nil];
469 |
470 | CGRect senderViewOriginalFrame = _senderViewForAnimation.superview ? [_senderViewForAnimation.superview convertRect:_senderViewForAnimation.frame toView:nil] : _senderViewOriginalFrame;
471 |
472 | if(_useZoomAnimation)
473 | {
474 | [self animateView:resizableImageView
475 | toFrame:senderViewOriginalFrame
476 | completion:completion];
477 | }
478 | else
479 | {
480 | [UIView animateWithDuration:_animationDuration animations:^{
481 | resizableImageView.layer.frame = senderViewOriginalFrame;
482 | } completion:^(BOOL finished) {
483 | completion();
484 | }];
485 | }
486 | }
487 |
488 | - (CGRect)animationFrameForImage:(UIImage *)image presenting:(BOOL)presenting scrollView:(UIScrollView *)scrollView
489 | {
490 | if (!image) {
491 | return CGRectZero;
492 | }
493 |
494 | CGSize imageSize = image.size;
495 |
496 | CGFloat maxWidth = CGRectGetWidth(_applicationWindow.bounds);
497 | CGFloat maxHeight = CGRectGetHeight(_applicationWindow.bounds);
498 |
499 | CGRect animationFrame = CGRectZero;
500 |
501 | CGFloat aspect = imageSize.width / imageSize.height;
502 | if (maxWidth / aspect <= maxHeight) {
503 | animationFrame.size = CGSizeMake(maxWidth, maxWidth / aspect);
504 | }
505 | else {
506 | animationFrame.size = CGSizeMake(maxHeight * aspect, maxHeight);
507 | }
508 |
509 | animationFrame.origin.x = roundf((maxWidth - animationFrame.size.width) / 2.0f);
510 | animationFrame.origin.y = roundf((maxHeight - animationFrame.size.height) / 2.0f);
511 |
512 | if (!presenting) {
513 | animationFrame.origin.y += scrollView.frame.origin.y;
514 | }
515 |
516 | return animationFrame;
517 | }
518 |
519 | #pragma mark - Genaral
520 |
521 | - (void)prepareForClosePhotoBrowser {
522 | // Gesture
523 | [_applicationWindow removeGestureRecognizer:_panGesture];
524 |
525 | _autoHide = NO;
526 |
527 | // Controls
528 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; // Cancel any pending toggles from taps
529 | }
530 |
531 | - (void)dismissPhotoBrowserAnimated:(BOOL)animated {
532 | self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
533 |
534 | if ([_delegate respondsToSelector:@selector(photoBrowser:willDismissAtPageIndex:)])
535 | [_delegate photoBrowser:self willDismissAtPageIndex:_currentPageIndex];
536 |
537 | [self dismissViewControllerAnimated:animated completion:^{
538 | if ([_delegate respondsToSelector:@selector(photoBrowser:didDismissAtPageIndex:)])
539 | [_delegate photoBrowser:self didDismissAtPageIndex:_currentPageIndex];
540 |
541 | // if (SYSTEM_VERSION_LESS_THAN(@"8.0"))
542 | // {
543 | // _applicationTopViewController.modalPresentationStyle = _previousModalPresentationStyle;
544 | // }
545 | }];
546 | }
547 |
548 | - (UIButton*)customToolbarButtonImage:(UIImage*)image imageSelected:(UIImage*)selectedImage action:(SEL)action {
549 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
550 | [button setImage:image forState:UIControlStateNormal];
551 | [button setImage:selectedImage forState:UIControlStateDisabled];
552 | [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
553 | [button setContentMode:UIViewContentModeCenter];
554 | [button setFrame:[self getToolbarButtonFrame:image]];
555 | return button;
556 | }
557 |
558 | - (CGRect)getToolbarButtonFrame:(UIImage *)image{
559 | BOOL const isRetinaHd = ((float)[[UIScreen mainScreen] scale] > 2.0f);
560 | float const defaultButtonSize = isRetinaHd ? 66.0f : 44.0f;
561 | CGFloat buttonWidth = (image.size.width > defaultButtonSize) ? image.size.width : defaultButtonSize;
562 | CGFloat buttonHeight = (image.size.height > defaultButtonSize) ? image.size.width : defaultButtonSize;
563 | return CGRectMake(0,0, buttonWidth, buttonHeight);
564 | }
565 |
566 | - (UIImage*)getImageFromView:(UIView *)view {
567 | UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 2);
568 | [view.layer renderInContext:UIGraphicsGetCurrentContext()];
569 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
570 | UIGraphicsEndImageContext();
571 | return image;
572 | }
573 |
574 | - (UIViewController *)topviewController
575 | {
576 | UIViewController *topviewController = [UIApplication sharedApplication].keyWindow.rootViewController;
577 |
578 | while (topviewController.presentedViewController) {
579 | topviewController = topviewController.presentedViewController;
580 | }
581 |
582 | return topviewController;
583 | }
584 |
585 | #pragma mark - View Lifecycle
586 |
587 | - (void)viewDidLoad {
588 | // View
589 | self.view.backgroundColor = [UIColor colorWithWhite:(_useWhiteBackgroundColor ? 1 : 0) alpha:1];
590 |
591 | self.view.clipsToBounds = YES;
592 |
593 | // Setup paging scrolling view
594 | CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
595 | _pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
596 | //_pagingScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
597 | _pagingScrollView.pagingEnabled = YES;
598 | _pagingScrollView.delegate = self;
599 | _pagingScrollView.showsHorizontalScrollIndicator = NO;
600 | _pagingScrollView.showsVerticalScrollIndicator = NO;
601 | _pagingScrollView.backgroundColor = [UIColor clearColor];
602 | _pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
603 | [self.view addSubview:_pagingScrollView];
604 |
605 | // Transition animation
606 | [self performPresentAnimation];
607 |
608 | UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
609 |
610 | // Toolbar
611 | _toolbar = [[UIToolbar alloc] initWithFrame:[self frameForToolbarAtOrientation:currentOrientation]];
612 | _toolbar.backgroundColor = [UIColor clearColor];
613 | _toolbar.clipsToBounds = YES;
614 | _toolbar.translucent = YES;
615 | [_toolbar setBackgroundImage:[UIImage new]
616 | forToolbarPosition:UIToolbarPositionAny
617 | barMetrics:UIBarMetricsDefault];
618 |
619 | // Close Button
620 | _doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
621 | [_doneButton setFrame:[self frameForDoneButtonAtOrientation:currentOrientation]];
622 | [_doneButton setAlpha:1.0f];
623 | [_doneButton addTarget:self action:@selector(doneButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
624 |
625 | if(!_doneButtonImage) {
626 | [_doneButton setTitleColor:[UIColor colorWithWhite:0.9 alpha:0.9] forState:UIControlStateNormal|UIControlStateHighlighted];
627 | [_doneButton setTitle:ZYQPhotoBrowserLocalizedStrings(@"Done") forState:UIControlStateNormal];
628 | [_doneButton.titleLabel setFont:[UIFont boldSystemFontOfSize:11.0f]];
629 | [_doneButton setBackgroundColor:[UIColor colorWithWhite:0.1 alpha:0.5]];
630 | _doneButton.layer.cornerRadius = 3.0f;
631 | _doneButton.layer.borderColor = [UIColor colorWithWhite:0.9 alpha:0.9].CGColor;
632 | _doneButton.layer.borderWidth = 1.0f;
633 | _doneButtonSize = _doneButton.frame.size;
634 | }
635 | else {
636 | [_doneButton setImage:_doneButtonImage forState:UIControlStateNormal];
637 | _doneButton.contentMode = UIViewContentModeScaleAspectFit;
638 | }
639 |
640 | _titleLabel = [[UILabel alloc] init];
641 | [_titleLabel setFrame:[self frameForTitleLabelAtOrientation:currentOrientation]];
642 | [_titleLabel setAlpha:1.0f];
643 | _titleLabel.textAlignment = NSTextAlignmentCenter;
644 | _titleLabel.backgroundColor = [UIColor clearColor];
645 | [_titleLabel setFont:[UIFont boldSystemFontOfSize:17]];
646 | if(_useWhiteBackgroundColor == NO) {
647 | _titleLabel.textColor = [UIColor whiteColor];
648 | _titleLabel.shadowColor = [UIColor darkTextColor];
649 | _titleLabel.shadowOffset = CGSizeMake(0, 1);
650 | }
651 | else {
652 | _titleLabel.textColor = [UIColor blackColor];
653 | }
654 |
655 | UIImage *leftButtonImage = (_leftArrowImage == nil) ?
656 | [UIImage imageNamed:@"ZYQPhotoBrowser.bundle/images/ZYQPhotoBrowser_arrowLeft.png"] : _leftArrowImage;
657 |
658 | UIImage *rightButtonImage = (_rightArrowImage == nil) ?
659 | [UIImage imageNamed:@"ZYQPhotoBrowser.bundle/images/ZYQPhotoBrowser_arrowRight.png"] : _rightArrowImage;
660 |
661 | UIImage *leftButtonSelectedImage = (_leftArrowSelectedImage == nil) ?
662 | [UIImage imageNamed:@"ZYQPhotoBrowser.bundle/images/ZYQPhotoBrowser_arrowLeftSelected.png"] : _leftArrowSelectedImage;
663 |
664 | UIImage *rightButtonSelectedImage = (_rightArrowSelectedImage == nil) ?
665 | [UIImage imageNamed:@"ZYQPhotoBrowser.bundle/images/ZYQPhotoBrowser_arrowRightSelected.png"] : _rightArrowSelectedImage;
666 |
667 | // Arrows
668 | _previousButton = [[UIBarButtonItem alloc] initWithCustomView:[self customToolbarButtonImage:leftButtonImage
669 | imageSelected:leftButtonSelectedImage
670 | action:@selector(gotoPreviousPage)]];
671 |
672 | _nextButton = [[UIBarButtonItem alloc] initWithCustomView:[self customToolbarButtonImage:rightButtonImage
673 | imageSelected:rightButtonSelectedImage
674 | action:@selector(gotoNextPage)]];
675 |
676 | // Counter Label
677 | _counterLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 95, 40)];
678 | _counterLabel.textAlignment = NSTextAlignmentCenter;
679 | _counterLabel.backgroundColor = [UIColor clearColor];
680 | _counterLabel.font = [UIFont fontWithName:@"Helvetica" size:17];
681 |
682 | if(_useWhiteBackgroundColor == NO) {
683 | _counterLabel.textColor = [UIColor whiteColor];
684 | _counterLabel.shadowColor = [UIColor darkTextColor];
685 | _counterLabel.shadowOffset = CGSizeMake(0, 1);
686 | }
687 | else {
688 | _counterLabel.textColor = [UIColor blackColor];
689 | }
690 |
691 | // Counter Button
692 | _counterButton = [[UIBarButtonItem alloc] initWithCustomView:_counterLabel];
693 |
694 | // Action Button
695 | if(_actionButtonImage != nil && _actionButtonSelectedImage != nil) {
696 | _actionButton = [[UIBarButtonItem alloc] initWithCustomView:[self customToolbarButtonImage:_actionButtonImage
697 | imageSelected:_actionButtonSelectedImage
698 | action:@selector(actionButtonPressed:)]];
699 | }
700 | else {
701 | _actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction
702 | target:self
703 | action:@selector(actionButtonPressed:)];
704 | }
705 |
706 | // Gesture
707 | _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)];
708 | [_panGesture setMinimumNumberOfTouches:1];
709 | [_panGesture setMaximumNumberOfTouches:1];
710 |
711 | // Update
712 | //[self reloadData];
713 |
714 | // Super
715 | [super viewDidLoad];
716 | }
717 |
718 | - (void)viewWillAppear:(BOOL)animated {
719 | // Update
720 | [self reloadData];
721 |
722 |
723 | if ([_delegate respondsToSelector:@selector(willAppearPhotoBrowser:)]) {
724 | [_delegate willAppearPhotoBrowser:self];
725 | }
726 |
727 | // Super
728 | [super viewWillAppear:animated];
729 |
730 | // Status Bar
731 | _statusBarOriginallyHidden = [UIApplication sharedApplication].statusBarHidden;
732 |
733 | // Update UI
734 | [self hideControlsAfterDelay];
735 | }
736 |
737 | - (void)viewDidAppear:(BOOL)animated {
738 | [super viewDidAppear:animated];
739 | _viewIsActive = YES;
740 | }
741 |
742 | // Release any retained subviews of the main view.
743 | - (void)viewDidUnload {
744 | _currentPageIndex = 0;
745 | _pagingScrollView = nil;
746 | _visiblePages = nil;
747 | _recycledPages = nil;
748 | _toolbar = nil;
749 | _doneButton = nil;
750 | _titleLabel = nil;
751 | _previousButton = nil;
752 | _nextButton = nil;
753 |
754 | [super viewDidUnload];
755 | }
756 |
757 | #pragma mark - Status Bar
758 |
759 | - (UIStatusBarStyle)preferredStatusBarStyle {
760 | return _useWhiteBackgroundColor ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
761 | }
762 |
763 | - (BOOL)prefersStatusBarHidden {
764 | if(_forceHideStatusBar) {
765 | return YES;
766 | }
767 |
768 | if(_isdraggingPhoto) {
769 | if(_statusBarOriginallyHidden) {
770 | return YES;
771 | }
772 | else {
773 | return NO;
774 | }
775 | }
776 | else {
777 | return [self areControlsHidden];
778 | }
779 | }
780 |
781 | - (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
782 | return UIStatusBarAnimationFade;
783 | }
784 |
785 | #pragma mark - Layout
786 |
787 | - (void)viewWillLayoutSubviews {
788 | // Flag
789 | _performingLayout = YES;
790 |
791 | UIInterfaceOrientation currentOrientation = [UIApplication sharedApplication].statusBarOrientation;
792 |
793 | // Toolbar
794 | _toolbar.frame = [self frameForToolbarAtOrientation:currentOrientation];
795 |
796 | // Done button
797 | _doneButton.frame = [self frameForDoneButtonAtOrientation:currentOrientation];
798 |
799 | // Title label
800 | _titleLabel.frame = [self frameForTitleLabelAtOrientation:currentOrientation];
801 |
802 | //Custom Backgroud
803 | _customBackgroud.frame=self.view.bounds;
804 |
805 | // Remember index
806 | NSUInteger indexPriorToLayout = _currentPageIndex;
807 |
808 | // Get paging scroll view frame to determine if anything needs changing
809 | CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
810 |
811 | // Frame needs changing
812 | _pagingScrollView.frame = pagingScrollViewFrame;
813 |
814 | // Recalculate contentSize based on current orientation
815 | _pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
816 |
817 | // Adjust frames and configuration of each visible page
818 | for (ZYQZoomingScrollView *page in _visiblePages) {
819 | NSUInteger index = PAGE_INDEX(page);
820 | page.frame = [self frameForPageAtIndex:index];
821 | page.captionView.frame = [self frameForCaptionView:page.captionView atIndex:index];
822 | [page setMaxMinZoomScalesForCurrentBounds];
823 | }
824 |
825 | // Adjust contentOffset to preserve page location based on values collected prior to location
826 | _pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:indexPriorToLayout];
827 | [self didStartViewingPageAtIndex:_currentPageIndex]; // initial
828 |
829 | // Reset
830 | _currentPageIndex = indexPriorToLayout;
831 | _performingLayout = NO;
832 |
833 | // Super
834 | [super viewWillLayoutSubviews];
835 | }
836 |
837 | - (void)performLayout {
838 | // Setup
839 | _performingLayout = YES;
840 | NSUInteger numberOfPhotos = [self numberOfPhotos];
841 |
842 | // Setup pages
843 | [_visiblePages removeAllObjects];
844 | [_recycledPages removeAllObjects];
845 |
846 | // Toolbar
847 | if (_displayToolbar) {
848 | [self.view addSubview:_toolbar];
849 | } else {
850 | [_toolbar removeFromSuperview];
851 | }
852 |
853 | // Title
854 | if(_displayTitle && !self.navigationController.navigationBar)
855 | [self.view addSubview:_titleLabel];
856 |
857 | // Close button
858 | if(_displayDoneButton && !self.navigationController.navigationBar)
859 | [self.view addSubview:_doneButton];
860 |
861 | // Toolbar items & navigation
862 | UIBarButtonItem *fixedLeftSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
863 | target:self action:nil];
864 | fixedLeftSpace.width = 32; // To balance action button
865 | UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
866 | target:self action:nil];
867 | NSMutableArray *items = [NSMutableArray new];
868 |
869 | if (_displayActionButton)
870 | [items addObject:fixedLeftSpace];
871 | [items addObject:flexSpace];
872 |
873 | if (numberOfPhotos > 1 && _displayArrowButton)
874 | [items addObject:_previousButton];
875 |
876 | if(_displayCounterLabel) {
877 | [items addObject:flexSpace];
878 | [items addObject:_counterButton];
879 | }
880 |
881 | [items addObject:flexSpace];
882 | if (numberOfPhotos > 1 && _displayArrowButton)
883 | [items addObject:_nextButton];
884 | [items addObject:flexSpace];
885 |
886 | if(_displayActionButton)
887 | [items addObject:_actionButton];
888 |
889 | [_toolbar setItems:items];
890 | [self updateToolbar];
891 |
892 | // Content offset
893 | _pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:_currentPageIndex];
894 | [self tilePages];
895 | _performingLayout = NO;
896 |
897 | if(! _disableVerticalSwipe) {
898 | [self.view addGestureRecognizer:_panGesture];
899 | }
900 |
901 | //Custom Backgroud
902 | if (_customBackgroud) {
903 | [self.view insertSubview:_customBackgroud atIndex:0];
904 | }
905 | }
906 |
907 | #pragma mark - Data
908 |
909 | - (void)reloadData {
910 | // Get data
911 | [self releaseAllUnderlyingPhotos];
912 |
913 | // Update
914 | [self performLayout];
915 |
916 | // Layout
917 | [self.view setNeedsLayout];
918 | }
919 |
920 | - (NSUInteger)numberOfPhotos {
921 | return _photos.count;
922 | }
923 |
924 | - (id)photoAtIndex:(NSUInteger)index {
925 | return _photos[index];
926 | }
927 |
928 | - (ZYQCaptionView *)captionViewForPhotoAtIndex:(NSUInteger)index {
929 | ZYQCaptionView *captionView = nil;
930 | if ([_delegate respondsToSelector:@selector(photoBrowser:captionViewForPhotoAtIndex:)]) {
931 | captionView = [_delegate photoBrowser:self captionViewForPhotoAtIndex:index];
932 | } else {
933 | id photo = [self photoAtIndex:index];
934 | if ([photo respondsToSelector:@selector(caption)]) {
935 | if ([photo caption]) captionView = [[ZYQCaptionView alloc] initWithPhoto:photo];
936 | }
937 | }
938 | captionView.alpha = [self areControlsHidden] ? 0 : 1; // Initial alpha
939 |
940 | return captionView;
941 | }
942 |
943 | - (UIImage *)imageForPhoto:(id)photo {
944 | if (photo) {
945 | // Get image or obtain in background
946 | if ([photo underlyingImage]) {
947 | return [photo underlyingImage];
948 | } else {
949 | [photo loadUnderlyingImageAndNotify];
950 | if ([photo respondsToSelector:@selector(placeholderImage)]) {
951 | return [photo placeholderImage];
952 | }
953 | }
954 | }
955 |
956 | return nil;
957 | }
958 |
959 | - (void)loadAdjacentPhotosIfNecessary:(id)photo {
960 | ZYQZoomingScrollView *page = [self pageDisplayingPhoto:photo];
961 | if (page) {
962 | // If page is current page then initiate loading of previous and next pages
963 | NSUInteger pageIndex = PAGE_INDEX(page);
964 | if (_currentPageIndex == pageIndex) {
965 | if (pageIndex > 0) {
966 | // Preload index - 1
967 | id photo = [self photoAtIndex:pageIndex-1];
968 | if (![photo underlyingImage]) {
969 | [photo loadUnderlyingImageAndNotify];
970 | ZYQLog(@"Pre-loading image at index %i", pageIndex-1);
971 | }
972 | }
973 | if (pageIndex < [self numberOfPhotos] - 1) {
974 | // Preload index + 1
975 | id photo = [self photoAtIndex:pageIndex+1];
976 | if (![photo underlyingImage]) {
977 | [photo loadUnderlyingImageAndNotify];
978 | ZYQLog(@"Pre-loading image at index %i", pageIndex+1);
979 | }
980 | }
981 | }
982 | }
983 | }
984 |
985 | #pragma mark - ZYQPhoto Loading Notification
986 |
987 | - (void)handleZYQPhotoLoadingDidEndNotification:(NSNotification *)notification {
988 | id photo = [notification object];
989 | ZYQZoomingScrollView *page = [self pageDisplayingPhoto:photo];
990 | if (page) {
991 | if ([photo underlyingImage]) {
992 | // Successful load
993 | [page displayImage];
994 | [self loadAdjacentPhotosIfNecessary:photo];
995 | } else {
996 | // Failed to load
997 | [page displayImageFailure];
998 | if ([_delegate respondsToSelector:@selector(photoBrowser:imageFailed:imageView:)]) {
999 | NSUInteger pageIndex = PAGE_INDEX(page);
1000 | [_delegate photoBrowser:self imageFailed:pageIndex imageView:page.photoImageView];
1001 | }
1002 | // make sure the page is completely updated
1003 | [page setNeedsLayout];
1004 | }
1005 | }
1006 | }
1007 |
1008 | #pragma mark - Paging
1009 |
1010 | - (void)tilePages {
1011 | // Calculate which pages should be visible
1012 | // Ignore padding as paging bounces encroach on that
1013 | // and lead to false page loads
1014 | CGRect visibleBounds = _pagingScrollView.bounds;
1015 | NSInteger iFirstIndex = (NSInteger) floorf((CGRectGetMinX(visibleBounds)+PADDING*2) / CGRectGetWidth(visibleBounds));
1016 | NSInteger iLastIndex = (NSInteger) floorf((CGRectGetMaxX(visibleBounds)-PADDING*2-1) / CGRectGetWidth(visibleBounds));
1017 | if (iFirstIndex < 0) iFirstIndex = 0;
1018 | if (iFirstIndex > [self numberOfPhotos] - 1) iFirstIndex = [self numberOfPhotos] - 1;
1019 | if (iLastIndex < 0) iLastIndex = 0;
1020 | if (iLastIndex > [self numberOfPhotos] - 1) iLastIndex = [self numberOfPhotos] - 1;
1021 |
1022 | // Recycle no longer needed pages
1023 | NSInteger pageIndex;
1024 | for (ZYQZoomingScrollView *page in _visiblePages) {
1025 | pageIndex = PAGE_INDEX(page);
1026 | if (pageIndex < (NSUInteger)iFirstIndex || pageIndex > (NSUInteger)iLastIndex) {
1027 | [_recycledPages addObject:page];
1028 | [page prepareForReuse];
1029 | [page removeFromSuperview];
1030 | ZYQLog(@"Removed page at index %i", PAGE_INDEX(page));
1031 | }
1032 | }
1033 | [_visiblePages minusSet:_recycledPages];
1034 | while (_recycledPages.count > 2) // Only keep 2 recycled pages
1035 | [_recycledPages removeObject:[_recycledPages anyObject]];
1036 |
1037 | // Add missing pages
1038 | for (NSUInteger index = (NSUInteger)iFirstIndex; index <= (NSUInteger)iLastIndex; index++) {
1039 | if (![self isDisplayingPageForIndex:index]) {
1040 | // Add new page
1041 | ZYQZoomingScrollView *page;
1042 | page = [[ZYQZoomingScrollView alloc] initWithPhotoBrowser:self];
1043 | page.backgroundColor = [UIColor clearColor];
1044 | page.opaque = YES;
1045 |
1046 | [self configurePage:page forIndex:index];
1047 | [_visiblePages addObject:page];
1048 | [_pagingScrollView addSubview:page];
1049 | ZYQLog(@"Added page at index %i", index);
1050 |
1051 | // Add caption
1052 | ZYQCaptionView *captionView = [self captionViewForPhotoAtIndex:index];
1053 | captionView.frame = [self frameForCaptionView:captionView atIndex:index];
1054 | [_pagingScrollView addSubview:captionView];
1055 | page.captionView = captionView;
1056 | }
1057 | }
1058 | }
1059 |
1060 | - (BOOL)isDisplayingPageForIndex:(NSUInteger)index {
1061 | for (ZYQZoomingScrollView *page in _visiblePages)
1062 | if (PAGE_INDEX(page) == index) return YES;
1063 | return NO;
1064 | }
1065 |
1066 | - (ZYQZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index {
1067 | ZYQZoomingScrollView *thePage = nil;
1068 | for (ZYQZoomingScrollView *page in _visiblePages) {
1069 | if (PAGE_INDEX(page) == index) {
1070 | thePage = page; break;
1071 | }
1072 | }
1073 | return thePage;
1074 | }
1075 |
1076 | - (ZYQZoomingScrollView *)pageDisplayingPhoto:(id)photo {
1077 | ZYQZoomingScrollView *thePage = nil;
1078 | for (ZYQZoomingScrollView *page in _visiblePages) {
1079 | if (page.photo == photo) {
1080 | thePage = page; break;
1081 | }
1082 | }
1083 | return thePage;
1084 | }
1085 |
1086 | - (void)configurePage:(ZYQZoomingScrollView *)page forIndex:(NSUInteger)index {
1087 | page.frame = [self frameForPageAtIndex:index];
1088 | page.tag = PAGE_INDEX_TAG_OFFSET + index;
1089 | page.photo = [self photoAtIndex:index];
1090 |
1091 | __block __weak ZYQPhoto *photo = (ZYQPhoto*)page.photo;
1092 | __weak ZYQZoomingScrollView* weakPage = page;
1093 | photo.progressUpdateBlock = ^(CGFloat progress){
1094 | [weakPage setProgress:progress forPhoto:photo];
1095 | };
1096 | }
1097 |
1098 | - (ZYQZoomingScrollView *)dequeueRecycledPage {
1099 | ZYQZoomingScrollView *page = [_recycledPages anyObject];
1100 | if (page) {
1101 | [_recycledPages removeObject:page];
1102 | }
1103 | return page;
1104 | }
1105 |
1106 | // Handle page changes
1107 | - (void)didStartViewingPageAtIndex:(NSUInteger)index {
1108 | // Load adjacent images if needed and the photo is already
1109 | // loaded. Also called after photo has been loaded in background
1110 | id currentPhoto = [self photoAtIndex:index];
1111 | if ([currentPhoto underlyingImage]) {
1112 | // photo loaded so load ajacent now
1113 | [self loadAdjacentPhotosIfNecessary:currentPhoto];
1114 | }
1115 | if ([_delegate respondsToSelector:@selector(photoBrowser:didShowPhotoAtIndex:)]) {
1116 | [_delegate photoBrowser:self didShowPhotoAtIndex:index];
1117 | }
1118 | }
1119 |
1120 | #pragma mark - Frame Calculations
1121 |
1122 | - (CGRect)frameForPagingScrollView {
1123 | CGRect frame = self.view.bounds;
1124 | frame.origin.x -= PADDING;
1125 | frame.size.width += (2 * PADDING);
1126 | return frame;
1127 | }
1128 |
1129 | - (CGRect)frameForPageAtIndex:(NSUInteger)index {
1130 | // We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in
1131 | // landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's
1132 | // view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape
1133 | // because it has a rotation transform applied.
1134 | CGRect bounds = _pagingScrollView.bounds;
1135 | CGRect pageFrame = bounds;
1136 | pageFrame.size.width -= (2 * PADDING);
1137 | pageFrame.origin.x = (bounds.size.width * index) + PADDING;
1138 | return pageFrame;
1139 | }
1140 |
1141 | - (CGSize)contentSizeForPagingScrollView {
1142 | // We have to use the paging scroll view's bounds to calculate the contentSize, for the same reason outlined above.
1143 | CGRect bounds = _pagingScrollView.bounds;
1144 | return CGSizeMake(bounds.size.width * [self numberOfPhotos], bounds.size.height);
1145 | }
1146 |
1147 | - (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index {
1148 | CGFloat pageWidth = _pagingScrollView.bounds.size.width;
1149 | CGFloat newOffset = index * pageWidth;
1150 | return CGPointMake(newOffset, 0);
1151 | }
1152 |
1153 | - (BOOL)isLandscape:(UIInterfaceOrientation)orientation
1154 | {
1155 | return UIInterfaceOrientationIsLandscape(orientation);
1156 | }
1157 |
1158 | - (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation {
1159 | CGFloat height = 44;
1160 |
1161 | if ([self isLandscape:orientation])
1162 | height = 32;
1163 |
1164 | return CGRectMake(0, self.view.bounds.size.height - height, self.view.bounds.size.width, height);
1165 | }
1166 |
1167 | - (CGRect)frameForDoneButtonAtOrientation:(UIInterfaceOrientation)orientation {
1168 | CGRect screenBound = self.view.bounds;
1169 | CGFloat screenWidth = screenBound.size.width;
1170 |
1171 | // if ([self isLandscape:orientation]) screenWidth = screenBound.size.height;
1172 |
1173 | return CGRectMake(screenWidth - self.doneButtonRightInset - self.doneButtonSize.width, self.doneButtonTopInset, self.doneButtonSize.width, self.doneButtonSize.height);
1174 | }
1175 |
1176 | - (CGRect)frameForTitleLabelAtOrientation:(UIInterfaceOrientation)orientation {
1177 | CGRect screenBound = self.view.bounds;
1178 | CGFloat screenWidth = screenBound.size.width;
1179 | CGFloat statusBarBottom=[UIApplication sharedApplication].statusBarFrame.origin.y+[UIApplication sharedApplication].statusBarFrame.size.height;
1180 |
1181 | // if ([self isLandscape:orientation]) screenWidth = screenBound.size.height;
1182 |
1183 | return CGRectMake(0, statusBarBottom, screenWidth, 44);
1184 | }
1185 |
1186 | - (CGRect)frameForCaptionView:(ZYQCaptionView *)captionView atIndex:(NSUInteger)index {
1187 | CGRect pageFrame = [self frameForPageAtIndex:index];
1188 |
1189 | CGSize captionSize = [captionView sizeThatFits:CGSizeMake(pageFrame.size.width, 0)];
1190 | CGRect captionFrame = CGRectMake(pageFrame.origin.x, pageFrame.size.height - captionSize.height - (_toolbar.superview?_toolbar.frame.size.height:0), pageFrame.size.width, captionSize.height);
1191 |
1192 | return captionFrame;
1193 | }
1194 |
1195 | #pragma mark - UIScrollView Delegate
1196 |
1197 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
1198 | // Checks
1199 | if (!_viewIsActive || _performingLayout || _rotating) return;
1200 |
1201 | // Tile pages
1202 | [self tilePages];
1203 |
1204 | // Calculate current page
1205 | CGRect visibleBounds = _pagingScrollView.bounds;
1206 | NSInteger index = (NSInteger) (floorf(CGRectGetMidX(visibleBounds) / CGRectGetWidth(visibleBounds)));
1207 | if (index < 0) index = 0;
1208 | if (index > [self numberOfPhotos] - 1) index = [self numberOfPhotos] - 1;
1209 | NSUInteger previousCurrentPage = _currentPageIndex;
1210 | _currentPageIndex = index;
1211 | if (_currentPageIndex != previousCurrentPage) {
1212 | [self didStartViewingPageAtIndex:index];
1213 |
1214 | if(_arrowButtonsChangePhotosAnimated) [self updateToolbar];
1215 | }
1216 | }
1217 |
1218 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
1219 | // Hide controls when dragging begins
1220 | if(_autoHideInterface){
1221 | [self setControlsHidden:YES animated:YES permanent:NO];
1222 | }
1223 | }
1224 |
1225 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
1226 | // Update toolbar when page changes
1227 | if(! _arrowButtonsChangePhotosAnimated) [self updateToolbar];
1228 | }
1229 |
1230 | #pragma mark - Toolbar
1231 |
1232 | - (void)updateToolbar {
1233 | //Title
1234 | if ([_delegate respondsToSelector:@selector(photoBrowser:titleForPhotoAtIndex:)]) {
1235 | _titleLabel.text=[_delegate photoBrowser:self titleForPhotoAtIndex:_currentPageIndex];
1236 | }
1237 | else{
1238 | _titleLabel.text=ZYQPhotoBrowserLocalizedStrings(@"PhotoBrowser");
1239 | }
1240 |
1241 | // Counter
1242 | if ([_delegate respondsToSelector:@selector(photoBrowser:counterForPhotoAtIndex:)]) {
1243 | _counterLabel.text=[_delegate photoBrowser:self counterForPhotoAtIndex:_currentPageIndex];
1244 | }
1245 | else{
1246 | if ([self numberOfPhotos] > 1) {
1247 | _counterLabel.text = [NSString stringWithFormat:@"%lu %@ %lu", (unsigned long)(_currentPageIndex+1), ZYQPhotoBrowserLocalizedStrings(@"of"), (unsigned long)[self numberOfPhotos]];
1248 | } else {
1249 | _counterLabel.text = nil;
1250 | }
1251 | }
1252 |
1253 | // Buttons
1254 | _previousButton.enabled = (_currentPageIndex > 0);
1255 | _nextButton.enabled = (_currentPageIndex < [self numberOfPhotos]-1);
1256 | }
1257 |
1258 | - (void)jumpToPageAtIndex:(NSUInteger)index {
1259 | // Change page
1260 | if (index < [self numberOfPhotos]) {
1261 | CGRect pageFrame = [self frameForPageAtIndex:index];
1262 |
1263 | if(_arrowButtonsChangePhotosAnimated)
1264 | {
1265 | [_pagingScrollView setContentOffset:CGPointMake(pageFrame.origin.x - PADDING, 0) animated:YES];
1266 | }
1267 | else
1268 | {
1269 | _pagingScrollView.contentOffset = CGPointMake(pageFrame.origin.x - PADDING, 0);
1270 | [self updateToolbar];
1271 | }
1272 | }
1273 |
1274 | // Update timer to give more time
1275 | [self hideControlsAfterDelay];
1276 | }
1277 |
1278 | - (void)gotoPreviousPage { [self jumpToPageAtIndex:_currentPageIndex-1]; }
1279 | - (void)gotoNextPage { [self jumpToPageAtIndex:_currentPageIndex+1]; }
1280 |
1281 | #pragma mark - Control Hiding / Showing
1282 |
1283 | // If permanent then we don't set timers to hide again
1284 | - (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent {
1285 | // Cancel any timers
1286 | [self cancelControlHiding];
1287 |
1288 | // Captions
1289 | NSMutableSet *captionViews = [[NSMutableSet alloc] initWithCapacity:_visiblePages.count];
1290 | for (ZYQZoomingScrollView *page in _visiblePages) {
1291 | if (page.captionView) [captionViews addObject:page.captionView];
1292 | }
1293 |
1294 | // Hide/show bars
1295 | [UIView animateWithDuration:(animated ? 0.1 : 0) animations:^(void) {
1296 | CGFloat alpha = hidden ? 0 : 1;
1297 | [self.navigationController.navigationBar setAlpha:alpha];
1298 | [_toolbar setAlpha:alpha];
1299 | [_doneButton setAlpha:alpha];
1300 | [_titleLabel setAlpha:alpha];
1301 | for (UIView *v in captionViews) v.alpha = alpha;
1302 | } completion:^(BOOL finished) {}];
1303 |
1304 | // Control hiding timer
1305 | // Will cancel existing timer but only begin hiding if they are visible
1306 | if (!permanent) {
1307 | [self hideControlsAfterDelay];
1308 | }
1309 |
1310 | [self setNeedsStatusBarAppearanceUpdate];
1311 | }
1312 |
1313 | - (void)cancelControlHiding {
1314 | // If a timer exists then cancel and release
1315 | if (_controlVisibilityTimer) {
1316 | [_controlVisibilityTimer invalidate];
1317 | _controlVisibilityTimer = nil;
1318 | }
1319 | }
1320 |
1321 | // Enable/disable control visiblity timer
1322 | - (void)hideControlsAfterDelay {
1323 | if (![self autoHideInterface]) {
1324 | return;
1325 | }
1326 |
1327 | if (![self areControlsHidden]) {
1328 | [self cancelControlHiding];
1329 | _controlVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(hideControls) userInfo:nil repeats:NO];
1330 | }
1331 | }
1332 |
1333 | - (BOOL)areControlsHidden {
1334 | return (_toolbar.alpha == 0);
1335 | }
1336 |
1337 | - (void)hideControls {
1338 | if(_autoHide && _autoHideInterface) {
1339 | [self setControlsHidden:YES animated:YES permanent:NO];
1340 | }
1341 | }
1342 | - (void)handleSingleTap {
1343 | if (_dismissOnTouch) {
1344 | [self doneButtonPressed:nil];
1345 | } else {
1346 | [self setControlsHidden:![self areControlsHidden] animated:YES permanent:NO];
1347 | }
1348 | }
1349 |
1350 |
1351 | #pragma mark - Properties
1352 |
1353 | -(NSArray *)photos{
1354 | return _photos;
1355 | }
1356 |
1357 | - (void)setInitialPageIndex:(NSUInteger)index {
1358 | // Validate
1359 | if (index >= [self numberOfPhotos]) index = [self numberOfPhotos]-1;
1360 | _initalPageIndex = index;
1361 | _currentPageIndex = index;
1362 | if ([self isViewLoaded]) {
1363 | [self jumpToPageAtIndex:index];
1364 | if (!_viewIsActive) [self tilePages]; // Force tiling if view is not visible
1365 | }
1366 | }
1367 |
1368 | - (void)setGifSupportImageViewClass:(Class)gifSupportImageViewClass{
1369 | if (_gifSupportImageViewClass==gifSupportImageViewClass) {
1370 | return;
1371 | }
1372 | _gifSupportImageViewClass=gifSupportImageViewClass;
1373 |
1374 | [self zyq_hookOrAddWithOriginSeletor:@selector(touchesEnded:withEvent:) originClass:_gifSupportImageViewClass swizzledSelector:@selector(touchesEnded:withEvent:) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1375 | [self zyq_hookOrAddWithOriginSeletor:@selector(handleSingleTap:) originClass:_gifSupportImageViewClass swizzledSelector:@selector(handleSingleTap:) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1376 | [self zyq_hookOrAddWithOriginSeletor:@selector(handleDoubleTap:) originClass:_gifSupportImageViewClass swizzledSelector:@selector(handleDoubleTap:) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1377 | [self zyq_hookOrAddWithOriginSeletor:@selector(handleTripleTap:) originClass:_gifSupportImageViewClass swizzledSelector:@selector(handleTripleTap:) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1378 | [self zyq_hookOrAddWithOriginSeletor:@selector(setTapDelegate:) originClass:_gifSupportImageViewClass swizzledSelector:@selector(setTapDelegate:) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1379 | [self zyq_hookOrAddWithOriginSeletor:@selector(tapDelegate) originClass:_gifSupportImageViewClass swizzledSelector:@selector(tapDelegate) swizzledClass:NSClassFromString(@"ZYQTapDetectingImageView")];
1380 |
1381 |
1382 | }
1383 |
1384 | -(void)zyq_hookOrAddWithOriginSeletor:(SEL)originalSelector originClass:(Class)originClass swizzledSelector:(SEL)swizzledSelector swizzledClass:(Class)swizzledClass{
1385 |
1386 | Method originalMethod = class_getInstanceMethod(originClass, originalSelector);
1387 | Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector);
1388 |
1389 | static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
1390 | OSSpinLockLock(&aspect_lock);
1391 |
1392 | BOOL success = class_addMethod(originClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
1393 | if (success) {
1394 | class_replaceMethod(originClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
1395 | } else {
1396 | method_exchangeImplementations(originalMethod, swizzledMethod);
1397 | }
1398 | OSSpinLockUnlock(&aspect_lock);
1399 |
1400 | }
1401 |
1402 |
1403 | #pragma mark - Buttons
1404 |
1405 | - (void)doneButtonPressed:(id)sender {
1406 | if ([_delegate respondsToSelector:@selector(willDisappearPhotoBrowser:)]) {
1407 | [_delegate willDisappearPhotoBrowser:self];
1408 | }
1409 |
1410 | if (_senderViewForAnimation && _currentPageIndex == _initalPageIndex) {
1411 | ZYQZoomingScrollView *scrollView = [self pageDisplayedAtIndex:_currentPageIndex];
1412 | [self performCloseAnimationWithScrollView:scrollView];
1413 | }
1414 | else {
1415 | _senderViewForAnimation.hidden = NO;
1416 | [self prepareForClosePhotoBrowser];
1417 | [self dismissPhotoBrowserAnimated:YES];
1418 | }
1419 | }
1420 |
1421 | - (void)actionButtonPressed:(id)sender {
1422 | id photo = [self photoAtIndex:_currentPageIndex];
1423 |
1424 | if ([self numberOfPhotos] > 0 && [photo underlyingImage]) {
1425 | if(!_actionButtonTitles)
1426 | {
1427 | // Activity view
1428 | NSMutableArray *activityItems = [NSMutableArray arrayWithObject:[photo underlyingImage]];
1429 | if (photo.caption) [activityItems addObject:photo.caption];
1430 |
1431 | self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
1432 |
1433 | __typeof__(self) __weak selfBlock = self;
1434 |
1435 | [self.activityViewController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
1436 | [selfBlock hideControlsAfterDelay];
1437 | selfBlock.activityViewController = nil;
1438 | }];
1439 |
1440 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
1441 | [self presentViewController:self.activityViewController animated:YES completion:nil];
1442 | }
1443 | else { // iPad
1444 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
1445 |
1446 | self.activityViewController.modalPresentationStyle = UIModalPresentationPopover;
1447 | self.activityViewController.preferredContentSize = CGSizeMake(300, 400);
1448 |
1449 | UIPopoverPresentationController *popover = self.activityViewController.popoverPresentationController;
1450 | [popover setSourceRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)];
1451 | [popover setSourceView:self.view];
1452 | [self presentViewController:self.activityViewController animated:YES completion:nil];
1453 | #else
1454 | UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:self.activityViewController];
1455 | [popover presentPopoverFromRect:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/4, 0, 0)
1456 | inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny
1457 | animated:YES];
1458 | #endif
1459 | }
1460 | }
1461 | else
1462 | {
1463 | // Action sheet
1464 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
1465 | self.actionsSheet=[UIAlertController alertControllerWithTitle:@"" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];
1466 | for (NSInteger i=0; i<_actionButtonTitles.count; i++) {
1467 | UIAlertAction *alertAction=[UIAlertAction actionWithTitle:_actionButtonTitles[i] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
1468 | if ([_delegate respondsToSelector:@selector(photoBrowser:didDismissActionSheetWithButtonIndex:photoIndex:)]) {
1469 | [_delegate photoBrowser:self didDismissActionSheetWithButtonIndex:i photoIndex:_currentPageIndex];
1470 | return;
1471 | }
1472 | }];
1473 | [self.actionsSheet addAction:alertAction];
1474 | }
1475 |
1476 | UIAlertAction *cancelAlertAction=[UIAlertAction actionWithTitle:ZYQPhotoBrowserLocalizedStrings(@"Cancel") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
1477 | [self hideControlsAfterDelay];
1478 | }];
1479 |
1480 | [self.actionsSheet addAction:cancelAlertAction];
1481 |
1482 |
1483 | [self presentViewController:self.actionsSheet animated:YES completion:nil];
1484 |
1485 | #else
1486 | self.actionsSheet = [UIActionSheet new];
1487 | self.actionsSheet.delegate = self;
1488 | for(NSString *action in _actionButtonTitles) {
1489 | [self.actionsSheet addButtonWithTitle:action];
1490 | }
1491 |
1492 | self.actionsSheet.cancelButtonIndex = [self.actionsSheet addButtonWithTitle:ZYQPhotoBrowserLocalizedStrings(@"Cancel")];
1493 | self.actionsSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
1494 |
1495 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
1496 | [_actionsSheet showInView:self.view];
1497 | } else {
1498 | [_actionsSheet showFromBarButtonItem:sender animated:YES];
1499 | }
1500 | #endif
1501 |
1502 |
1503 | }
1504 |
1505 | // Keep controls hidden
1506 | [self setControlsHidden:NO animated:YES permanent:YES];
1507 | }
1508 | }
1509 |
1510 | #pragma mark - Action Sheet Delegate
1511 |
1512 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
1513 | #else
1514 | - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
1515 | if (actionSheet == _actionsSheet) {
1516 | self.actionsSheet = nil;
1517 |
1518 | if (buttonIndex != actionSheet.cancelButtonIndex) {
1519 | if ([_delegate respondsToSelector:@selector(photoBrowser:didDismissActionSheetWithButtonIndex:photoIndex:)]) {
1520 | [_delegate photoBrowser:self didDismissActionSheetWithButtonIndex:buttonIndex photoIndex:_currentPageIndex];
1521 | return;
1522 | }
1523 | }
1524 | }
1525 |
1526 | [self hideControlsAfterDelay]; // Continue as normal...
1527 | }
1528 | #endif
1529 |
1530 |
1531 | #pragma mark - Zoom Animation
1532 |
1533 | - (void)animateView:(UIView *)view toFrame:(CGRect)frame completion:(void (^)(void))completion
1534 | {
1535 | if ([self conformsToProtocol:objc_getProtocol("ZYQPhotoBrowserCustomProtocol")]) {
1536 | [(ZYQPhotoBrowser*)self animateView:view toFrame:frame completion:completion];
1537 | }
1538 | else{
1539 | [UIView animateWithDuration:0.5 animations:^{
1540 | view.frame=frame;
1541 | } completion:^(BOOL finished) {
1542 | if (completion) {
1543 | completion();
1544 | }
1545 | }];
1546 | }
1547 | }
1548 | @end
1549 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQPhotoProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQPhotoProtocol.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 02/01/2012.
6 | // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ZYQPBConstants.h"
11 |
12 | // Name of notification used when a photo has completed loading process
13 | // Used to notify browser display the image
14 | #define ZYQPhoto_LOADING_DID_END_NOTIFICATION @"ZYQPhoto_LOADING_DID_END_NOTIFICATION"
15 |
16 | //If you wish to use custom libraies for download image ...
17 | //you can use this protocol
18 |
19 | //ZYQPhotoBrowser's
20 | @protocol ZYQPhotoBrowserCustomProtocol
21 |
22 | @required
23 | - (void)custom_animateView:(UIView *)view toFrame:(CGRect)frame completion:(void (^)(void))completion;
24 |
25 | @end
26 |
27 | //ZYQPhoto's
28 | @protocol ZYQPhotoCustomLoadProtocol
29 |
30 | @required
31 | -(void)loadImageWithURL:(NSURL*)url updateProgressBlock:(void (^)(CGFloat progress))updateProgressBlock completedBlock:(void (^)(UIImage *image,NSError *error))completedBlock;
32 | -(id)loadImageWithFile:(NSString *)path;
33 |
34 | @end
35 |
36 | //ZYQZoomingScrollView's
37 | @class ZYQPhotoBrowser;
38 | @protocol ZYQZoomingScrollViewCustomProtocol
39 |
40 | @required
41 | -(id)getCustomProgressViewWithBrowser:(ZYQPhotoBrowser*)browser;
42 |
43 | @end
44 |
45 | //Your ProgressView's
46 | @protocol ZYQProgressViewProtocol
47 |
48 | @required
49 | @property(nonatomic,assign)CGFloat zyq_progress;
50 |
51 | @end
52 |
53 |
54 | // If you wish to use your own data models for photo then they must conform
55 | // to this protocol. See instructions for details on each method.
56 | // Otherwise you can use the ZYQPhoto object or subclass it yourself to
57 | // store more information per photo.
58 | //
59 | // You can see the ZYQPhoto class for an example implementation of this protocol
60 | //
61 | @protocol ZYQPhoto
62 |
63 | @required
64 |
65 | // Return underlying UIImage to be displayed
66 | // Return nil if the image is not immediately available (loaded into memory, preferably
67 | // already decompressed) and needs to be loaded from a source (cache, file, web, etc)
68 | // IMPORTANT: You should *NOT* use this method to initiate
69 | // fetching of images from any external of source. That should be handled
70 | // in -loadUnderlyingImageAndNotify: which may be called by the photo browser if this
71 | // methods returns nil.
72 | - (UIImage *)underlyingImage;
73 |
74 | // Called when the browser has determined the underlying images is not
75 | // already loaded into memory but needs it.
76 | // You must load the image asyncronously (and decompress it for better performance).
77 | // See ZYQPhoto object for an example implementation.
78 | // When the underlying UIImage is loaded (or failed to load) you should post the following
79 | // notification:
80 | //
81 | // [[NSNotificationCenter defaultCenter] postNotificationName:ZYQPhoto_LOADING_DID_END_NOTIFICATION
82 | // object:self];
83 | //
84 | - (void)loadUnderlyingImageAndNotify;
85 |
86 | // This is called when the photo browser has determined the photo data
87 | // is no longer needed or there are low memory conditions
88 | // You should release any underlying (possibly large and decompressed) image data
89 | // as long as the image can be re-loaded (from cache, file, or URL)
90 | - (void)unloadUnderlyingImage;
91 |
92 | @optional
93 |
94 | // Return a caption string to be displayed over the image
95 | // Return nil to display no caption
96 | - (NSString *)caption;
97 |
98 | // Return placeholder UIImage to be displayed while loading underlyingImage
99 | // Return nil if there is no placeholder
100 | - (UIImage *)placeholderImage;
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQTapDetectingImageView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQTapDetectingImageView.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 04/11/2009.
6 | // Copyright 2009 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol ZYQTapDetectingImageViewDelegate;
12 | #ifdef NODEPENDENCY
13 | @interface ZYQTapDetectingImageView : UIImageView
14 | #else
15 | #import "FLAnimatedImageView.h"
16 | @interface ZYQTapDetectingImageView : FLAnimatedImageView
17 | #endif
18 |
19 | @property (nonatomic, weak) id tapDelegate;
20 | - (void)handleSingleTap:(UITouch *)touch;
21 | - (void)handleDoubleTap:(UITouch *)touch;
22 | - (void)handleTripleTap:(UITouch *)touch;
23 | @end
24 |
25 | @protocol ZYQTapDetectingImageViewDelegate
26 | @optional
27 | - (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch;
28 | - (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch;
29 | - (void)imageView:(UIImageView *)imageView tripleTapDetected:(UITouch *)touch;
30 | @end
31 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQTapDetectingImageView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQTapDetectingImageView.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 04/11/2009.
6 | // Copyright 2009 d3i. All rights reserved.
7 | //
8 |
9 | #import "ZYQTapDetectingImageView.h"
10 | #import
11 |
12 | #ifdef NODEPENDENCY
13 | #else
14 | #import "UIImage+GIF.h"
15 | #import "FLAnimatedImage.h"
16 | #endif
17 |
18 | @implementation ZYQTapDetectingImageView
19 |
20 | #ifdef NODEPENDENCY
21 | #else
22 | -(void)setImage:(UIImage *)image{
23 | if (![image isKindOfClass:[UIImage class]]||image.isGIF) {
24 | NSLog(@"Gif");
25 | self.animatedImage=(FLAnimatedImage*)image;
26 | }
27 | else{
28 | [super setImage:image];
29 | }
30 | }
31 | #endif
32 |
33 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
34 | UITouch *touch = [touches anyObject];
35 | NSUInteger tapCount = touch.tapCount;
36 | switch (tapCount) {
37 | case 1:
38 | [self handleSingleTap:touch];
39 | break;
40 | case 2:
41 | [self handleDoubleTap:touch];
42 | break;
43 | case 3:
44 | [self handleTripleTap:touch];
45 | break;
46 | default:
47 | break;
48 | }
49 | [[self nextResponder] touchesEnded:touches withEvent:event];
50 | }
51 |
52 | - (void)handleSingleTap:(UITouch *)touch {
53 | if ([self.tapDelegate respondsToSelector:@selector(imageView:singleTapDetected:)])
54 | [self.tapDelegate imageView:self singleTapDetected:touch];
55 | }
56 |
57 | - (void)handleDoubleTap:(UITouch *)touch {
58 | if ([self.tapDelegate respondsToSelector:@selector(imageView:doubleTapDetected:)])
59 | [self.tapDelegate imageView:self doubleTapDetected:touch];
60 | }
61 |
62 | - (void)handleTripleTap:(UITouch *)touch {
63 | if ([self.tapDelegate respondsToSelector:@selector(imageView:tripleTapDetected:)])
64 | [self.tapDelegate imageView:self tripleTapDetected:touch];
65 | }
66 |
67 | -(void)setTapDelegate:(id)tapDelegate{
68 | objc_setAssociatedObject(self, @selector(tapDelegate), tapDelegate, OBJC_ASSOCIATION_ASSIGN);
69 |
70 | self.userInteractionEnabled=YES;
71 | }
72 |
73 | -(id)tapDelegate{
74 | return objc_getAssociatedObject(self, @selector(tapDelegate));
75 | }
76 |
77 | @end
78 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQTapDetectingView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQTapDetectingView.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 04/11/2009.
6 | // Copyright 2009 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol ZYQTapDetectingViewDelegate;
12 |
13 | @interface ZYQTapDetectingView : UIView {
14 | id __weak tapDelegate;
15 | }
16 | @property (nonatomic, weak) id tapDelegate;
17 | - (void)handleSingleTap:(UITouch *)touch;
18 | - (void)handleDoubleTap:(UITouch *)touch;
19 | - (void)handleTripleTap:(UITouch *)touch;
20 | @end
21 |
22 | @protocol ZYQTapDetectingViewDelegate
23 | @optional
24 | - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch;
25 | - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch;
26 | - (void)view:(UIView *)view tripleTapDetected:(UITouch *)touch;
27 | @end
28 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQTapDetectingView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQTapDetectingView.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 04/11/2009.
6 | // Copyright 2009 d3i. All rights reserved.
7 | //
8 |
9 | #import "ZYQTapDetectingView.h"
10 |
11 | @implementation ZYQTapDetectingView
12 |
13 | @synthesize tapDelegate;
14 |
15 | - (id)init {
16 | if ((self = [super init])) {
17 | self.userInteractionEnabled = YES;
18 | }
19 | return self;
20 | }
21 |
22 | - (id)initWithFrame:(CGRect)frame {
23 | if ((self = [super initWithFrame:frame])) {
24 | self.userInteractionEnabled = YES;
25 | }
26 | return self;
27 | }
28 |
29 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
30 | UITouch *touch = [touches anyObject];
31 | NSUInteger tapCount = touch.tapCount;
32 | switch (tapCount) {
33 | case 1:
34 | [self handleSingleTap:touch];
35 | break;
36 | case 2:
37 | [self handleDoubleTap:touch];
38 | break;
39 | case 3:
40 | [self handleTripleTap:touch];
41 | break;
42 | default:
43 | break;
44 | }
45 | [[self nextResponder] touchesEnded:touches withEvent:event];
46 | }
47 |
48 | - (void)handleSingleTap:(UITouch *)touch {
49 | if ([tapDelegate respondsToSelector:@selector(view:singleTapDetected:)])
50 | [tapDelegate view:self singleTapDetected:touch];
51 | }
52 |
53 | - (void)handleDoubleTap:(UITouch *)touch {
54 | if ([tapDelegate respondsToSelector:@selector(view:doubleTapDetected:)])
55 | [tapDelegate view:self doubleTapDetected:touch];
56 | }
57 |
58 | - (void)handleTripleTap:(UITouch *)touch {
59 | if ([tapDelegate respondsToSelector:@selector(view:tripleTapDetected:)])
60 | [tapDelegate view:self tripleTapDetected:touch];
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQZoomingScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQZoomingScrollView.h
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 14/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "ZYQPhotoProtocol.h"
11 | #import "ZYQTapDetectingImageView.h"
12 | #import "ZYQTapDetectingView.h"
13 |
14 | #ifdef NODEPENDENCY
15 | #else
16 | #import
17 | #endif
18 |
19 | @class ZYQPhotoBrowser, ZYQPhoto, ZYQCaptionView;
20 |
21 | @interface ZYQZoomingScrollView : UIScrollView {
22 |
23 | ZYQPhotoBrowser *__weak _photoBrowser;
24 | id _photo;
25 |
26 | // This view references the related caption view for simplified handling in photo browser
27 | ZYQCaptionView *_captionView;
28 |
29 | ZYQTapDetectingView *_tapView; // for background taps
30 |
31 | UIView *_progressView;
32 | }
33 |
34 | @property (nonatomic, strong) ZYQTapDetectingImageView *photoImageView;
35 | @property (nonatomic, strong) ZYQCaptionView *captionView;
36 | @property (nonatomic, strong) id photo;
37 | @property (nonatomic) CGFloat maximumDoubleTapZoomScale;
38 |
39 | - (id)initWithPhotoBrowser:(ZYQPhotoBrowser *)browser;
40 | - (void)displayImage;
41 | - (void)displayImageFailure;
42 | - (void)setProgress:(CGFloat)progress forPhoto:(ZYQPhoto*)photo;
43 | - (void)setMaxMinZoomScalesForCurrentBounds;
44 | - (void)prepareForReuse;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/ZYQPhotoBrowser/ZYQZoomingScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // ZYQZoomingScrollView.m
3 | // ZYQPhotoBrowser
4 | //
5 | // Created by Michael Waterfall on 14/10/2010.
6 | // Copyright 2010 d3i. All rights reserved.
7 | //
8 |
9 | #import "ZYQZoomingScrollView.h"
10 | #import "ZYQPhotoBrowser.h"
11 | #import "ZYQPhoto.h"
12 |
13 | #import
14 |
15 | // Declare private methods of browser
16 | @interface ZYQPhotoBrowser ()
17 | - (UIImage *)imageForPhoto:(id)photo;
18 | - (void)cancelControlHiding;
19 | - (void)hideControlsAfterDelay;
20 | //- (void)toggleControls;
21 | - (void)handleSingleTap;
22 | @end
23 |
24 | // Private methods and properties
25 | @interface ZYQZoomingScrollView ()
26 | @property (nonatomic, weak) ZYQPhotoBrowser *photoBrowser;
27 | - (void)handleSingleTap:(CGPoint)touchPoint;
28 | - (void)handleDoubleTap:(CGPoint)touchPoint;
29 | @end
30 |
31 | @implementation ZYQZoomingScrollView
32 |
33 | @synthesize photoImageView = _photoImageView, photoBrowser = _photoBrowser, photo = _photo, captionView = _captionView;
34 |
35 | - (id)initWithPhotoBrowser:(ZYQPhotoBrowser *)browser {
36 | if ((self = [super init])) {
37 | // Delegate
38 | self.photoBrowser = browser;
39 |
40 | // Tap view for background
41 | _tapView = [[ZYQTapDetectingView alloc] initWithFrame:self.bounds];
42 | _tapView.tapDelegate = self;
43 | _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
44 | _tapView.backgroundColor = [UIColor clearColor];
45 | [self addSubview:_tapView];
46 |
47 | // Image view
48 | Class myTapDetectingImageViewClass=[ZYQTapDetectingImageView class];
49 | if (browser.gifSupportImageViewClass) {
50 | myTapDetectingImageViewClass=browser.gifSupportImageViewClass;
51 | }
52 |
53 | _photoImageView = [[myTapDetectingImageViewClass alloc] initWithFrame:CGRectZero];
54 | _photoImageView.tapDelegate = self;
55 | _photoImageView.backgroundColor = [UIColor clearColor];
56 | [self addSubview:_photoImageView];
57 |
58 | CGRect screenBound = [[UIScreen mainScreen] bounds];
59 | CGFloat screenWidth = screenBound.size.width;
60 | CGFloat screenHeight = screenBound.size.height;
61 |
62 | if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
63 | screenWidth = screenBound.size.height;
64 | screenHeight = screenBound.size.width;
65 | }
66 |
67 | // Progress view
68 | if ([self conformsToProtocol:objc_getProtocol("ZYQZoomingScrollViewCustomProtocol")]) {
69 | _progressView=[(ZYQZoomingScrollView*)self getCustomProgressViewWithBrowser:browser];
70 | }
71 | else{
72 | #ifdef NODEPENDENCY
73 | #else
74 | DACircularProgressView *tmpProgressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake((screenWidth-35.)/2., (screenHeight-35.)/2, 35.0f, 35.0f)];
75 | [tmpProgressView setProgress:0.0f];
76 | tmpProgressView.tag = 101;
77 | tmpProgressView.thicknessRatio = 0.1;
78 | tmpProgressView.roundedCorners = NO;
79 | tmpProgressView.trackTintColor = browser.trackTintColor ? self.photoBrowser.trackTintColor : [UIColor colorWithWhite:0.2 alpha:1];
80 | tmpProgressView.progressTintColor = browser.progressTintColor ? self.photoBrowser.progressTintColor : [UIColor colorWithWhite:1.0 alpha:1];
81 |
82 | _progressView=tmpProgressView;
83 | #endif
84 |
85 | }
86 | [self addSubview:_progressView];
87 |
88 | // Setup
89 | self.backgroundColor = [UIColor clearColor];
90 | self.delegate = self;
91 | self.showsHorizontalScrollIndicator = NO;
92 | self.showsVerticalScrollIndicator = NO;
93 | self.decelerationRate = UIScrollViewDecelerationRateFast;
94 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
95 | }
96 |
97 | return self;
98 | }
99 |
100 | - (void)setPhoto:(id)photo {
101 | _photoImageView.image = nil; // Release image
102 | if (_photo != photo) {
103 | _photo = photo;
104 | }
105 | [self displayImage];
106 | }
107 |
108 | - (void)prepareForReuse {
109 | self.photo = nil;
110 | [_captionView removeFromSuperview];
111 | self.captionView = nil;
112 | }
113 |
114 | #pragma mark - Image
115 |
116 | // Get and display image
117 | - (void)displayImage {
118 | if (_photo) {
119 | // Reset
120 | self.maximumZoomScale = 1;
121 | self.minimumZoomScale = 1;
122 | self.zoomScale = 1;
123 |
124 | self.contentSize = CGSizeMake(0, 0);
125 |
126 | // Get image from browser as it handles ordering of fetching
127 | UIImage *img = [self.photoBrowser imageForPhoto:_photo];
128 | if (img) {
129 | // Hide ProgressView
130 | //_progressView.alpha = 0.0f;
131 | [_progressView removeFromSuperview];
132 |
133 | // Set image
134 | _photoImageView.image = img;
135 | _photoImageView.hidden = NO;
136 |
137 | // Setup photo frame
138 | CGRect photoImageViewFrame;
139 | photoImageViewFrame.origin = CGPointZero;
140 | photoImageViewFrame.size = img.size;
141 |
142 | _photoImageView.frame = photoImageViewFrame;
143 | self.contentSize = photoImageViewFrame.size;
144 |
145 | // Set zoom to minimum zoom
146 | [self setMaxMinZoomScalesForCurrentBounds];
147 | } else {
148 | // Hide image view
149 | _photoImageView.hidden = YES;
150 |
151 | _progressView.alpha = 1.0f;
152 | }
153 |
154 | [self setNeedsLayout];
155 | }
156 | }
157 |
158 | - (void)setProgress:(CGFloat)progress forPhoto:(ZYQPhoto*)photo {
159 | ZYQPhoto *p = (ZYQPhoto*)self.photo;
160 |
161 | if ([photo.photoURL.absoluteString isEqualToString:p.photoURL.absoluteString]) {
162 | if ([_progressView conformsToProtocol:objc_getProtocol("ZYQProgressViewProtocol")]) {
163 | if ([(UIView*)_progressView zyq_progress] < progress) {
164 | [(UIView*)_progressView setZyq_progress:progress];
165 | }
166 | }
167 | else{
168 | #ifdef NODEPENDENCY
169 | #else
170 | if ([(DACircularProgressView*)_progressView progress] < progress) {
171 | [(DACircularProgressView*)_progressView setProgress:progress animated:YES];
172 | }
173 | #endif
174 | }
175 | }
176 | }
177 |
178 | // Image failed so just show black!
179 | - (void)displayImageFailure {
180 | [_progressView removeFromSuperview];
181 | }
182 |
183 | #pragma mark - Setup
184 |
185 | - (void)setMaxMinZoomScalesForCurrentBounds {
186 | // Reset
187 | self.maximumZoomScale = 1;
188 | self.minimumZoomScale = 1;
189 | self.zoomScale = 1;
190 |
191 | // Bail
192 | if (_photoImageView.image == nil) return;
193 |
194 | // Sizes
195 | CGSize boundsSize = self.bounds.size;
196 | boundsSize.width -= 0.1;
197 | boundsSize.height -= 0.1;
198 |
199 | CGSize imageSize = _photoImageView.frame.size;
200 |
201 | // Calculate Min
202 | CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
203 | CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
204 | CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
205 |
206 | // If image is smaller than the screen then ensure we show it at
207 | // min scale of 1
208 | if (xScale > 1 && yScale > 1) {
209 | //minScale = 1.0;
210 | }
211 |
212 | // Calculate Max
213 | CGFloat maxScale = 4.0; // Allow double scale
214 | // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
215 | // maximum zoom scale to 0.5.
216 | if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
217 | maxScale = maxScale / [[UIScreen mainScreen] scale];
218 |
219 | if (maxScale < minScale) {
220 | maxScale = minScale * 2;
221 | }
222 | }
223 |
224 | // Calculate Max Scale Of Double Tap
225 | CGFloat maxDoubleTapZoomScale = 4.0 * minScale; // Allow double scale
226 | // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
227 | // maximum zoom scale to 0.5.
228 | if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
229 | maxDoubleTapZoomScale = maxDoubleTapZoomScale / [[UIScreen mainScreen] scale];
230 |
231 | if (maxDoubleTapZoomScale < minScale) {
232 | maxDoubleTapZoomScale = minScale * 2;
233 | }
234 | }
235 |
236 | // Make sure maxDoubleTapZoomScale isn't larger than maxScale
237 | maxDoubleTapZoomScale = MIN(maxDoubleTapZoomScale, maxScale);
238 |
239 | // Set
240 | self.maximumZoomScale = maxScale;
241 | self.minimumZoomScale = minScale;
242 | self.zoomScale = minScale;
243 | self.maximumDoubleTapZoomScale = maxDoubleTapZoomScale;
244 |
245 | // Reset position
246 | _photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
247 | [self setNeedsLayout];
248 | }
249 |
250 | #pragma mark - Layout
251 |
252 | - (void)layoutSubviews {
253 | // Update tap view frame
254 | _tapView.frame = self.bounds;
255 |
256 | // Super
257 | [super layoutSubviews];
258 |
259 | // Center the image as it becomes smaller than the size of the screen
260 | CGSize boundsSize = self.bounds.size;
261 | CGRect frameToCenter = _photoImageView.frame;
262 |
263 | // Horizontally
264 | if (frameToCenter.size.width < boundsSize.width) {
265 | frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
266 | } else {
267 | frameToCenter.origin.x = 0;
268 | }
269 |
270 | // Vertically
271 | if (frameToCenter.size.height < boundsSize.height) {
272 | frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
273 | } else {
274 | frameToCenter.origin.y = 0;
275 | }
276 |
277 | // Center
278 | if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
279 | _photoImageView.frame = frameToCenter;
280 | }
281 |
282 | #pragma mark - UIScrollViewDelegate
283 |
284 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
285 | return _photoImageView;
286 | }
287 |
288 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
289 | [_photoBrowser cancelControlHiding];
290 | }
291 |
292 | - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
293 | [_photoBrowser cancelControlHiding];
294 | }
295 |
296 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
297 | [_photoBrowser hideControlsAfterDelay];
298 | }
299 |
300 | - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
301 | [self setNeedsLayout];
302 | [self layoutIfNeeded];
303 | }
304 |
305 | #pragma mark - Tap Detection
306 |
307 | - (void)handleSingleTap:(CGPoint)touchPoint {
308 | // [_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
309 | [_photoBrowser performSelector:@selector(handleSingleTap) withObject:nil afterDelay:0.2];
310 | }
311 |
312 | - (void)handleDoubleTap:(CGPoint)touchPoint {
313 |
314 | // Cancel any single tap handling
315 | [NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
316 |
317 | // Zoom
318 | if (round(self.zoomScale*100000) == round(self.maximumDoubleTapZoomScale*100000)) {
319 |
320 | // Zoom out
321 | [self setZoomScale:self.minimumZoomScale animated:YES];
322 |
323 | } else {
324 |
325 | // Zoom in
326 | CGSize targetSize = CGSizeMake(self.frame.size.width / self.maximumDoubleTapZoomScale, self.frame.size.height / self.maximumDoubleTapZoomScale);
327 | CGPoint targetPoint = CGPointMake(touchPoint.x - targetSize.width / 2.0, touchPoint.y - targetSize.height / 2.0);
328 |
329 | [self zoomToRect:CGRectMake(targetPoint.x, targetPoint.y, targetSize.width, targetSize.height) animated:YES];
330 |
331 | }
332 |
333 | // Delay controls
334 | [_photoBrowser hideControlsAfterDelay];
335 | }
336 |
337 | // Image View
338 | - (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch {
339 | [self handleSingleTap:[touch locationInView:imageView]];
340 | }
341 | - (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
342 | [self handleDoubleTap:[touch locationInView:imageView]];
343 | }
344 |
345 | // Background View
346 | - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
347 | [self handleSingleTap:[touch locationInView:view]];
348 | }
349 | - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
350 | [self handleDoubleTap:[touch locationInView:view]];
351 | }
352 |
353 | @end
354 |
--------------------------------------------------------------------------------