├── FSPhotoViewDemo
├── FSPhotoViewDemo
│ ├── en.lproj
│ │ └── InfoPlist.strings
│ ├── Default.png
│ ├── Default@2x.png
│ ├── demo_pic1.png
│ ├── demo_pic2.png
│ ├── demo_pic3.png
│ ├── Default-568h@2x.png
│ ├── FSViewController.h
│ ├── FSPhotoViewDemo-Prefix.pch
│ ├── main.m
│ ├── FSPhotoView
│ │ ├── FSPhotoView.h
│ │ └── FSPhotoView.m
│ ├── AppDelegate.h
│ ├── FSPhotoViewDemo-Info.plist
│ ├── AppDelegate.m
│ └── FSViewController.m
└── FSPhotoViewDemo.xcodeproj
│ ├── xcuserdata
│ └── jinxiaojun.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── FSPhotoViewDemo.xcscheme
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── jinxiaojun.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ └── project.pbxproj
├── FSPhotoView
├── FSPhotoView.h
└── FSPhotoView.m
└── README.md
/FSPhotoViewDemo/FSPhotoViewDemo/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/Default.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/Default@2x.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic1.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic2.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/demo_pic3.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo/Default-568h@2x.png
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/xcuserdata/jinxiaojun.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/project.xcworkspace/xcuserdata/jinxiaojun.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjinxx/FSPhotoView/HEAD/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/project.xcworkspace/xcuserdata/jinxiaojun.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSViewController.h
3 | // FSPhotoViewDemo
4 | //
5 | // Created by Stephen Jin on 8/30/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface FSViewController : UITableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSPhotoViewDemo-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'FSPhotoViewDemo' target in the 'FSPhotoViewDemo' project
3 | //
4 |
5 | #import
6 |
7 | #ifndef __IPHONE_3_0
8 | #warning "This project uses features only available in iOS SDK 3.0 and later."
9 | #endif
10 |
11 | #ifdef __OBJC__
12 | #import
13 | #import
14 | #endif
15 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // FSPhotoViewDemo
4 | //
5 | // Created by Stephen Jin on 8/30/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/FSPhotoView/FSPhotoView.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSPhotoView.h
3 | // FSPhotoView
4 | //
5 | // Created by Stephen Jin on 7/26/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef void (^DisappearBlcok)();
12 |
13 | @interface FSPhotoView : UIScrollView
14 |
15 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView;
16 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView completion:(DisappearBlcok)completed;
17 |
18 | @end
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSPhotoView/FSPhotoView.h:
--------------------------------------------------------------------------------
1 | //
2 | // FSPhotoView.h
3 | // FSPhotoView
4 | //
5 | // Created by Stephen Jin on 7/26/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef void (^DisappearBlcok)();
12 |
13 | @interface FSPhotoView : UIScrollView
14 |
15 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView;
16 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView completion:(DisappearBlcok)completed;
17 |
18 | @end
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // FSPhotoViewDemo
4 | //
5 | // Created by Stephen Jin on 8/30/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "FSViewController.h"
11 |
12 | @interface AppDelegate : UIResponder
13 |
14 | @property (strong, nonatomic) UIWindow *window;
15 | @property (nonatomic, retain) UINavigationController *navigationController;
16 | @property (nonatomic, retain) FSViewController *viewController;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/xcuserdata/jinxiaojun.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | FSPhotoViewDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 49AC2F3917D083CD00606BAE
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | FSPhotoView
2 | ===========
3 |
4 | FSPhotoView gives the ability to display image in fullscreen by a simple tap on any thumbnail image. The thumbnail image will animated to focused fullscreen image view from its initial position. Another tap on the fullscreen view zooms out the image back to the thumbnail status.
5 |
6 | This project is an attempt to re-implement the experience of inaction from Sina Weibo/WangYi News timeline to photo browser and to figure out how the animations are done.
7 |
8 |
9 |
10 |
11 | Please feel free to improve it, send comments or suggestions. Please let me know if you have great idea on it.
12 | You can contact me through Email: xiaojun.jin@outlook.com or Skype: xiaojun.jin
13 |
14 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSPhotoViewDemo-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | ArcSoft.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0
25 | LSRequiresIPhoneOS
26 |
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // FSPhotoViewDemo
4 | //
5 | // Created by Stephen Jin on 8/30/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @implementation AppDelegate
12 |
13 | - (void)dealloc
14 | {
15 | [_window release];
16 | self.viewController = nil;
17 | self.navigationController = nil;
18 | [super dealloc];
19 | }
20 |
21 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
22 | {
23 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
24 | // Override point for customization after application launch.
25 | self.window.backgroundColor = [UIColor whiteColor];
26 |
27 | self.viewController = [[[FSViewController alloc] initWithStyle:UITableViewStylePlain] autorelease];
28 | self.navigationController = [[[UINavigationController alloc]initWithRootViewController:self.viewController] autorelease];
29 | [self.window addSubview:self.navigationController.view];
30 |
31 | [self.window makeKeyAndVisible];
32 | return YES;
33 | }
34 |
35 | - (void)applicationWillResignActive:(UIApplication *)application
36 | {
37 | // 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.
38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
39 | }
40 |
41 | - (void)applicationDidEnterBackground:(UIApplication *)application
42 | {
43 | // 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.
44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
45 | }
46 |
47 | - (void)applicationWillEnterForeground:(UIApplication *)application
48 | {
49 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
50 | }
51 |
52 | - (void)applicationDidBecomeActive:(UIApplication *)application
53 | {
54 | // 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.
55 | }
56 |
57 | - (void)applicationWillTerminate:(UIApplication *)application
58 | {
59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/xcuserdata/jinxiaojun.xcuserdatad/xcschemes/FSPhotoViewDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSViewController.m
3 | // FSPhotoViewDemo
4 | //
5 | // Created by Stephen Jin on 8/30/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import "FSViewController.h"
10 | #import "FSPhotoView.h"
11 |
12 | @interface FSViewController ()
13 |
14 | @end
15 |
16 | @implementation FSViewController
17 |
18 | - (id)initWithStyle:(UITableViewStyle)style
19 | {
20 | self = [super initWithStyle:style];
21 | if (self) {
22 | // Custom initialization
23 | [self setTitle:@"FSPhotoView Demo"];
24 | [self.tableView setBackgroundColor:[UIColor clearColor]];
25 | [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
26 |
27 | }
28 | return self;
29 | }
30 |
31 | - (void)viewDidLoad
32 | {
33 | [super viewDidLoad];
34 |
35 | // Uncomment the following line to preserve selection between presentations.
36 | // self.clearsSelectionOnViewWillAppear = NO;
37 |
38 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
39 | // self.navigationItem.rightBarButtonItem = self.editButtonItem;
40 | }
41 |
42 | - (void)didReceiveMemoryWarning
43 | {
44 | [super didReceiveMemoryWarning];
45 | // Dispose of any resources that can be recreated.
46 | }
47 |
48 | #pragma mark - Table view data source
49 |
50 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
51 | {
52 | // Return the number of sections.
53 | return 1;
54 | }
55 |
56 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
57 | {
58 | // Return the number of rows in the section.
59 | return 2;
60 | }
61 |
62 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
63 | {
64 | return indexPath.row?205:305;
65 | }
66 |
67 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
68 | {
69 | static NSString *CellIdentifier = @"Cell";
70 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
71 |
72 | // Configure the cell...
73 | if (cell == nil)
74 | {
75 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
76 | [cell setAccessoryType:UITableViewCellAccessoryNone];
77 | [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
78 | }
79 | else
80 | {
81 | while ([cell.contentView.subviews lastObject] != nil)
82 | {
83 | [(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];
84 | }
85 | }
86 |
87 | if (indexPath.row == 0)
88 | {
89 | for (int i = 0; i < 2; i++)
90 | {
91 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5+i*(155+2.5), 0, 152.5, 300)];
92 | [imageView setContentMode:UIViewContentModeScaleAspectFill];
93 | [imageView setClipsToBounds:YES];
94 | [imageView setUserInteractionEnabled:YES];
95 | [imageView setImage:[UIImage imageNamed:[NSString stringWithFormat:@"demo_pic%d",i+1]]];
96 | [cell.contentView addSubview:imageView];
97 |
98 | UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self
99 | action:@selector(tapClickAction:)];
100 | [imageView addGestureRecognizer:tapGes];
101 | [tapGes release];
102 |
103 | [imageView release];
104 | }
105 | }
106 | else
107 | {
108 | UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 0, 310, 200)];
109 | [imageView setContentMode:UIViewContentModeScaleAspectFill];
110 | [imageView setClipsToBounds:YES];
111 | [imageView setUserInteractionEnabled:YES];
112 | [imageView setImage:[UIImage imageNamed:@"demo_pic3"]];
113 | [cell.contentView addSubview:imageView];
114 |
115 | UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self
116 | action:@selector(tapClickAction:)];
117 | [imageView addGestureRecognizer:tapGes];
118 | [tapGes release];
119 |
120 | [imageView release];
121 | }
122 | return cell;
123 | }
124 |
125 | - (void)tapClickAction:(UITapGestureRecognizer *)sender
126 | {
127 | [FSPhotoView showImageWithSenderView:(UIImageView*)sender.view];
128 | }
129 |
130 | @end
131 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 496B9C9217D0848500712F95 /* FSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 496B9C9117D0848500712F95 /* FSViewController.m */; };
11 | 496B9C9417D0896E00712F95 /* demo_pic1.png in Resources */ = {isa = PBXBuildFile; fileRef = 496B9C9317D0896E00712F95 /* demo_pic1.png */; };
12 | 496B9C9817D08E6200712F95 /* demo_pic2.png in Resources */ = {isa = PBXBuildFile; fileRef = 496B9C9717D08E6200712F95 /* demo_pic2.png */; };
13 | 496B9C9A17D08F6000712F95 /* demo_pic3.png in Resources */ = {isa = PBXBuildFile; fileRef = 496B9C9917D08F6000712F95 /* demo_pic3.png */; };
14 | 496B9C9E17D0936100712F95 /* FSPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 496B9C9D17D092D900712F95 /* FSPhotoView.m */; };
15 | 49AC2F3E17D083CD00606BAE /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49AC2F3D17D083CD00606BAE /* UIKit.framework */; };
16 | 49AC2F4017D083CD00606BAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49AC2F3F17D083CD00606BAE /* Foundation.framework */; };
17 | 49AC2F4217D083CD00606BAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49AC2F4117D083CD00606BAE /* CoreGraphics.framework */; };
18 | 49AC2F4817D083CD00606BAE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 49AC2F4617D083CD00606BAE /* InfoPlist.strings */; };
19 | 49AC2F4A17D083CD00606BAE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 49AC2F4917D083CD00606BAE /* main.m */; };
20 | 49AC2F4E17D083CD00606BAE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 49AC2F4D17D083CD00606BAE /* AppDelegate.m */; };
21 | 49AC2F5017D083CD00606BAE /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 49AC2F4F17D083CD00606BAE /* Default.png */; };
22 | 49AC2F5217D083CD00606BAE /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 49AC2F5117D083CD00606BAE /* Default@2x.png */; };
23 | 49AC2F5417D083CD00606BAE /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 49AC2F5317D083CD00606BAE /* Default-568h@2x.png */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXFileReference section */
27 | 496B9C9017D0848500712F95 /* FSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSViewController.h; sourceTree = ""; };
28 | 496B9C9117D0848500712F95 /* FSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSViewController.m; sourceTree = ""; };
29 | 496B9C9317D0896E00712F95 /* demo_pic1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = demo_pic1.png; sourceTree = ""; };
30 | 496B9C9717D08E6200712F95 /* demo_pic2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = demo_pic2.png; sourceTree = ""; };
31 | 496B9C9917D08F6000712F95 /* demo_pic3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = demo_pic3.png; sourceTree = ""; };
32 | 496B9C9C17D092D900712F95 /* FSPhotoView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FSPhotoView.h; path = FSPhotoView/FSPhotoView.h; sourceTree = ""; };
33 | 496B9C9D17D092D900712F95 /* FSPhotoView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FSPhotoView.m; path = FSPhotoView/FSPhotoView.m; sourceTree = ""; };
34 | 49AC2F3A17D083CD00606BAE /* FSPhotoViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSPhotoViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 49AC2F3D17D083CD00606BAE /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
36 | 49AC2F3F17D083CD00606BAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
37 | 49AC2F4117D083CD00606BAE /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
38 | 49AC2F4517D083CD00606BAE /* FSPhotoViewDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FSPhotoViewDemo-Info.plist"; sourceTree = ""; };
39 | 49AC2F4717D083CD00606BAE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
40 | 49AC2F4917D083CD00606BAE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
41 | 49AC2F4B17D083CD00606BAE /* FSPhotoViewDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FSPhotoViewDemo-Prefix.pch"; sourceTree = ""; };
42 | 49AC2F4C17D083CD00606BAE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
43 | 49AC2F4D17D083CD00606BAE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
44 | 49AC2F4F17D083CD00606BAE /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; };
45 | 49AC2F5117D083CD00606BAE /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; };
46 | 49AC2F5317D083CD00606BAE /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; };
47 | /* End PBXFileReference section */
48 |
49 | /* Begin PBXFrameworksBuildPhase section */
50 | 49AC2F3717D083CD00606BAE /* Frameworks */ = {
51 | isa = PBXFrameworksBuildPhase;
52 | buildActionMask = 2147483647;
53 | files = (
54 | 49AC2F3E17D083CD00606BAE /* UIKit.framework in Frameworks */,
55 | 49AC2F4017D083CD00606BAE /* Foundation.framework in Frameworks */,
56 | 49AC2F4217D083CD00606BAE /* CoreGraphics.framework in Frameworks */,
57 | );
58 | runOnlyForDeploymentPostprocessing = 0;
59 | };
60 | /* End PBXFrameworksBuildPhase section */
61 |
62 | /* Begin PBXGroup section */
63 | 496B9C9B17D092CA00712F95 /* FSPhotoView */ = {
64 | isa = PBXGroup;
65 | children = (
66 | 496B9C9C17D092D900712F95 /* FSPhotoView.h */,
67 | 496B9C9D17D092D900712F95 /* FSPhotoView.m */,
68 | );
69 | name = FSPhotoView;
70 | sourceTree = "";
71 | };
72 | 49AC2F3117D083CD00606BAE = {
73 | isa = PBXGroup;
74 | children = (
75 | 49AC2F4317D083CD00606BAE /* FSPhotoViewDemo */,
76 | 49AC2F3C17D083CD00606BAE /* Frameworks */,
77 | 49AC2F3B17D083CD00606BAE /* Products */,
78 | );
79 | sourceTree = "";
80 | };
81 | 49AC2F3B17D083CD00606BAE /* Products */ = {
82 | isa = PBXGroup;
83 | children = (
84 | 49AC2F3A17D083CD00606BAE /* FSPhotoViewDemo.app */,
85 | );
86 | name = Products;
87 | sourceTree = "";
88 | };
89 | 49AC2F3C17D083CD00606BAE /* Frameworks */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 49AC2F3D17D083CD00606BAE /* UIKit.framework */,
93 | 49AC2F3F17D083CD00606BAE /* Foundation.framework */,
94 | 49AC2F4117D083CD00606BAE /* CoreGraphics.framework */,
95 | );
96 | name = Frameworks;
97 | sourceTree = "";
98 | };
99 | 49AC2F4317D083CD00606BAE /* FSPhotoViewDemo */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 496B9C9B17D092CA00712F95 /* FSPhotoView */,
103 | 49AC2F4C17D083CD00606BAE /* AppDelegate.h */,
104 | 49AC2F4D17D083CD00606BAE /* AppDelegate.m */,
105 | 496B9C9017D0848500712F95 /* FSViewController.h */,
106 | 496B9C9117D0848500712F95 /* FSViewController.m */,
107 | 49AC2F4417D083CD00606BAE /* Supporting Files */,
108 | );
109 | path = FSPhotoViewDemo;
110 | sourceTree = "";
111 | };
112 | 49AC2F4417D083CD00606BAE /* Supporting Files */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 496B9C9317D0896E00712F95 /* demo_pic1.png */,
116 | 496B9C9717D08E6200712F95 /* demo_pic2.png */,
117 | 496B9C9917D08F6000712F95 /* demo_pic3.png */,
118 | 49AC2F4517D083CD00606BAE /* FSPhotoViewDemo-Info.plist */,
119 | 49AC2F4617D083CD00606BAE /* InfoPlist.strings */,
120 | 49AC2F4917D083CD00606BAE /* main.m */,
121 | 49AC2F4B17D083CD00606BAE /* FSPhotoViewDemo-Prefix.pch */,
122 | 49AC2F4F17D083CD00606BAE /* Default.png */,
123 | 49AC2F5117D083CD00606BAE /* Default@2x.png */,
124 | 49AC2F5317D083CD00606BAE /* Default-568h@2x.png */,
125 | );
126 | name = "Supporting Files";
127 | sourceTree = "";
128 | };
129 | /* End PBXGroup section */
130 |
131 | /* Begin PBXNativeTarget section */
132 | 49AC2F3917D083CD00606BAE /* FSPhotoViewDemo */ = {
133 | isa = PBXNativeTarget;
134 | buildConfigurationList = 49AC2F5717D083CD00606BAE /* Build configuration list for PBXNativeTarget "FSPhotoViewDemo" */;
135 | buildPhases = (
136 | 49AC2F3617D083CD00606BAE /* Sources */,
137 | 49AC2F3717D083CD00606BAE /* Frameworks */,
138 | 49AC2F3817D083CD00606BAE /* Resources */,
139 | );
140 | buildRules = (
141 | );
142 | dependencies = (
143 | );
144 | name = FSPhotoViewDemo;
145 | productName = FSPhotoViewDemo;
146 | productReference = 49AC2F3A17D083CD00606BAE /* FSPhotoViewDemo.app */;
147 | productType = "com.apple.product-type.application";
148 | };
149 | /* End PBXNativeTarget section */
150 |
151 | /* Begin PBXProject section */
152 | 49AC2F3217D083CD00606BAE /* Project object */ = {
153 | isa = PBXProject;
154 | attributes = {
155 | LastUpgradeCheck = 0460;
156 | ORGANIZATIONNAME = "Stephen Jin";
157 | };
158 | buildConfigurationList = 49AC2F3517D083CD00606BAE /* Build configuration list for PBXProject "FSPhotoViewDemo" */;
159 | compatibilityVersion = "Xcode 3.2";
160 | developmentRegion = English;
161 | hasScannedForEncodings = 0;
162 | knownRegions = (
163 | en,
164 | );
165 | mainGroup = 49AC2F3117D083CD00606BAE;
166 | productRefGroup = 49AC2F3B17D083CD00606BAE /* Products */;
167 | projectDirPath = "";
168 | projectRoot = "";
169 | targets = (
170 | 49AC2F3917D083CD00606BAE /* FSPhotoViewDemo */,
171 | );
172 | };
173 | /* End PBXProject section */
174 |
175 | /* Begin PBXResourcesBuildPhase section */
176 | 49AC2F3817D083CD00606BAE /* Resources */ = {
177 | isa = PBXResourcesBuildPhase;
178 | buildActionMask = 2147483647;
179 | files = (
180 | 49AC2F4817D083CD00606BAE /* InfoPlist.strings in Resources */,
181 | 49AC2F5017D083CD00606BAE /* Default.png in Resources */,
182 | 496B9C9A17D08F6000712F95 /* demo_pic3.png in Resources */,
183 | 496B9C9417D0896E00712F95 /* demo_pic1.png in Resources */,
184 | 49AC2F5217D083CD00606BAE /* Default@2x.png in Resources */,
185 | 496B9C9817D08E6200712F95 /* demo_pic2.png in Resources */,
186 | 49AC2F5417D083CD00606BAE /* Default-568h@2x.png in Resources */,
187 | );
188 | runOnlyForDeploymentPostprocessing = 0;
189 | };
190 | /* End PBXResourcesBuildPhase section */
191 |
192 | /* Begin PBXSourcesBuildPhase section */
193 | 49AC2F3617D083CD00606BAE /* Sources */ = {
194 | isa = PBXSourcesBuildPhase;
195 | buildActionMask = 2147483647;
196 | files = (
197 | 496B9C9E17D0936100712F95 /* FSPhotoView.m in Sources */,
198 | 49AC2F4A17D083CD00606BAE /* main.m in Sources */,
199 | 49AC2F4E17D083CD00606BAE /* AppDelegate.m in Sources */,
200 | 496B9C9217D0848500712F95 /* FSViewController.m in Sources */,
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | };
204 | /* End PBXSourcesBuildPhase section */
205 |
206 | /* Begin PBXVariantGroup section */
207 | 49AC2F4617D083CD00606BAE /* InfoPlist.strings */ = {
208 | isa = PBXVariantGroup;
209 | children = (
210 | 49AC2F4717D083CD00606BAE /* en */,
211 | );
212 | name = InfoPlist.strings;
213 | sourceTree = "";
214 | };
215 | /* End PBXVariantGroup section */
216 |
217 | /* Begin XCBuildConfiguration section */
218 | 49AC2F5517D083CD00606BAE /* Debug */ = {
219 | isa = XCBuildConfiguration;
220 | buildSettings = {
221 | ALWAYS_SEARCH_USER_PATHS = NO;
222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
223 | CLANG_CXX_LIBRARY = "libc++";
224 | CLANG_WARN_CONSTANT_CONVERSION = YES;
225 | CLANG_WARN_EMPTY_BODY = YES;
226 | CLANG_WARN_ENUM_CONVERSION = YES;
227 | CLANG_WARN_INT_CONVERSION = YES;
228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
229 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
230 | COPY_PHASE_STRIP = NO;
231 | GCC_C_LANGUAGE_STANDARD = gnu99;
232 | GCC_DYNAMIC_NO_PIC = NO;
233 | GCC_OPTIMIZATION_LEVEL = 0;
234 | GCC_PREPROCESSOR_DEFINITIONS = (
235 | "DEBUG=1",
236 | "$(inherited)",
237 | );
238 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
239 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
240 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
241 | GCC_WARN_UNUSED_VARIABLE = YES;
242 | IPHONEOS_DEPLOYMENT_TARGET = 6.1;
243 | ONLY_ACTIVE_ARCH = YES;
244 | SDKROOT = iphoneos;
245 | };
246 | name = Debug;
247 | };
248 | 49AC2F5617D083CD00606BAE /* Release */ = {
249 | isa = XCBuildConfiguration;
250 | buildSettings = {
251 | ALWAYS_SEARCH_USER_PATHS = NO;
252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
253 | CLANG_CXX_LIBRARY = "libc++";
254 | CLANG_WARN_CONSTANT_CONVERSION = YES;
255 | CLANG_WARN_EMPTY_BODY = YES;
256 | CLANG_WARN_ENUM_CONVERSION = YES;
257 | CLANG_WARN_INT_CONVERSION = YES;
258 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
259 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
260 | COPY_PHASE_STRIP = YES;
261 | GCC_C_LANGUAGE_STANDARD = gnu99;
262 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
263 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
264 | GCC_WARN_UNUSED_VARIABLE = YES;
265 | IPHONEOS_DEPLOYMENT_TARGET = 6.1;
266 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
267 | SDKROOT = iphoneos;
268 | VALIDATE_PRODUCT = YES;
269 | };
270 | name = Release;
271 | };
272 | 49AC2F5817D083CD00606BAE /* Debug */ = {
273 | isa = XCBuildConfiguration;
274 | buildSettings = {
275 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
276 | GCC_PREFIX_HEADER = "FSPhotoViewDemo/FSPhotoViewDemo-Prefix.pch";
277 | INFOPLIST_FILE = "FSPhotoViewDemo/FSPhotoViewDemo-Info.plist";
278 | PRODUCT_NAME = "$(TARGET_NAME)";
279 | WRAPPER_EXTENSION = app;
280 | };
281 | name = Debug;
282 | };
283 | 49AC2F5917D083CD00606BAE /* Release */ = {
284 | isa = XCBuildConfiguration;
285 | buildSettings = {
286 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
287 | GCC_PREFIX_HEADER = "FSPhotoViewDemo/FSPhotoViewDemo-Prefix.pch";
288 | INFOPLIST_FILE = "FSPhotoViewDemo/FSPhotoViewDemo-Info.plist";
289 | PRODUCT_NAME = "$(TARGET_NAME)";
290 | WRAPPER_EXTENSION = app;
291 | };
292 | name = Release;
293 | };
294 | /* End XCBuildConfiguration section */
295 |
296 | /* Begin XCConfigurationList section */
297 | 49AC2F3517D083CD00606BAE /* Build configuration list for PBXProject "FSPhotoViewDemo" */ = {
298 | isa = XCConfigurationList;
299 | buildConfigurations = (
300 | 49AC2F5517D083CD00606BAE /* Debug */,
301 | 49AC2F5617D083CD00606BAE /* Release */,
302 | );
303 | defaultConfigurationIsVisible = 0;
304 | defaultConfigurationName = Release;
305 | };
306 | 49AC2F5717D083CD00606BAE /* Build configuration list for PBXNativeTarget "FSPhotoViewDemo" */ = {
307 | isa = XCConfigurationList;
308 | buildConfigurations = (
309 | 49AC2F5817D083CD00606BAE /* Debug */,
310 | 49AC2F5917D083CD00606BAE /* Release */,
311 | );
312 | defaultConfigurationIsVisible = 0;
313 | defaultConfigurationName = Release;
314 | };
315 | /* End XCConfigurationList section */
316 | };
317 | rootObject = 49AC2F3217D083CD00606BAE /* Project object */;
318 | }
319 |
--------------------------------------------------------------------------------
/FSPhotoView/FSPhotoView.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSPhotoView.m
3 | // FSPhotoView
4 | //
5 | // Created by Stephen Jin on 7/26/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import "FSPhotoView.h"
10 |
11 | @interface FSPhotoView () {
12 | BOOL _disableAutoLayout;
13 | UIViewContentMode ivContentMode;
14 | }
15 | @property (nonatomic, retain) UIImageView *imageView;
16 | @property (nonatomic, assign) CGRect senderFrame;
17 | @property (nonatomic, assign) CGRect clipsFrame;
18 | @property (nonatomic, assign) CGFloat offscreenY;
19 | @property (copy) DisappearBlcok block;
20 |
21 | - (void)displayImage:(UIImage *)newImage withSenderView:(UIImageView*)senderView;
22 | @end
23 |
24 | @implementation FSPhotoView
25 |
26 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView;
27 | {
28 | if (nil == senderView.image) return nil;
29 | return [FSPhotoView showImageWithSenderView:senderView completion:nil];
30 | }
31 |
32 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView completion:(DisappearBlcok)completed;
33 | {
34 | if (nil == senderView.image) return nil;
35 |
36 | // Deal with staus bar.
37 | CGRect rect = [UIScreen mainScreen].bounds;
38 |
39 | if (![UIApplication sharedApplication].statusBarHidden)
40 | {
41 | rect.size.height = [UIScreen mainScreen].bounds.size.height-20.f;
42 | rect.origin.y += [UIApplication sharedApplication].statusBarHidden?0:20.f;
43 | }
44 |
45 | FSPhotoView *photoView = [[FSPhotoView alloc] initWithFrame:rect andCompleteBlock:completed];
46 | if (photoView)
47 | {
48 | [[UIApplication sharedApplication].keyWindow addSubview:photoView];
49 | [photoView displayImage:senderView.image withSenderView:senderView];
50 | }
51 | return [photoView autorelease];
52 | }
53 |
54 | - (id)initWithFrame:(CGRect)frame andCompleteBlock:(DisappearBlcok)block
55 | {
56 | self = [super initWithFrame:frame];
57 | if (self) {
58 | // Initialization code
59 | self.block = block;
60 | [self setDelegate:self];
61 | [self setMaximumZoomScale:2.f];
62 | [self setShowsHorizontalScrollIndicator:NO];
63 | [self setShowsVerticalScrollIndicator:NO];
64 | [self loadSubviewsWithFrame:frame];
65 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(removePhotoView)];
66 | [singleTap setNumberOfTapsRequired:1];
67 | [self addGestureRecognizer:singleTap];
68 | [singleTap release];
69 | UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(zoomToLocation:)];
70 | [doubleTap setNumberOfTapsRequired:2];
71 | [self addGestureRecognizer:doubleTap];
72 | [singleTap requireGestureRecognizerToFail:doubleTap];
73 | [doubleTap release];
74 | }
75 | return self;
76 | }
77 |
78 | - (void)loadSubviewsWithFrame:(CGRect)frame
79 | {
80 | self.imageView = [[[UIImageView alloc] initWithFrame:frame] autorelease];
81 | [self addSubview:self.imageView];
82 | }
83 |
84 | #pragma mark - Display Functions
85 | - (void)displayImage:(UIImage *)newImage withSenderView:(UIImageView*)senderView
86 | {
87 | CGSize frameSize = self.frame.size;
88 | CGSize imageSize = newImage.size;
89 | CGFloat xScale = frameSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
90 | CGFloat yScale = frameSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
91 | CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
92 | CGRect photoImageViewFrame;
93 |
94 | if (xScale > 1 && yScale > 1)
95 | {
96 | photoImageViewFrame.origin = CGPointZero;
97 | photoImageViewFrame.size.height = newImage.size.height*minScale;
98 | photoImageViewFrame.size.width = newImage.size.width*minScale;
99 | [self imageView].frame = photoImageViewFrame;
100 | self.contentSize = photoImageViewFrame.size;
101 | [self imageView].image = newImage;
102 | minScale = 1;
103 | }
104 | else
105 | {
106 | photoImageViewFrame.origin = CGPointZero;
107 | photoImageViewFrame.size = newImage.size;
108 | [self imageView].frame = photoImageViewFrame;
109 | self.contentSize = photoImageViewFrame.size;
110 | [self imageView].image = newImage;
111 | }
112 |
113 | self.maximumZoomScale = 2.f*minScale;//maxScale;
114 | self.minimumZoomScale = minScale;
115 | self.zoomScale = minScale;
116 |
117 | _disableAutoLayout = NO;
118 | [self layoutIfNeeded]; // call layoutSubviews
119 |
120 | [self configAnimationDisplay:senderView];
121 | }
122 |
123 | //Image in PhotoView cannot be zoom in without this function.
124 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
125 | {
126 | return self.imageView;
127 | }
128 |
129 | - (BOOL)isZoomed
130 | {
131 | return !([self zoomScale] == [self minimumZoomScale]);
132 | }
133 |
134 | - (void)layoutSubviews
135 | {
136 | [super layoutSubviews];
137 |
138 | if (_disableAutoLayout) return;
139 |
140 | // Center the image as it becomes smaller than the size of the screen
141 | CGSize boundsSize = [self bounds].size;
142 | CGRect frameToCenter = [self imageView].frame;
143 |
144 | // Horizontally
145 | if (frameToCenter.size.width < boundsSize.width)
146 | {
147 | frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
148 | }
149 | else
150 | {
151 | frameToCenter.origin.x = 0;
152 | }
153 |
154 | // Vertically
155 | if (frameToCenter.size.height < boundsSize.height)
156 | {
157 | frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
158 | }
159 | else
160 | {
161 | frameToCenter.origin.y = 0;
162 | }
163 |
164 | // Center
165 | if (!CGRectEqualToRect([self imageView].frame, frameToCenter))
166 | {
167 | [self imageView].frame = frameToCenter;
168 | }
169 | }
170 |
171 | - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center
172 | {
173 | CGRect zoomRect;
174 |
175 | zoomRect.size.height = self.frame.size.height / scale;
176 | zoomRect.size.width = self.frame.size.width / scale;
177 | zoomRect.origin.x = center.x/self.zoomScale - (zoomRect.size.width /2.0 );
178 | zoomRect.origin.y = center.y/self.zoomScale - (zoomRect.size.height /2.0 );
179 |
180 | zoomRect.origin.x -= (self.bounds.size.width - self.imageView.frame.size.width)/2/self.zoomScale;
181 |
182 | if (center.y < self.bounds.size.height/2 && self.imageView.frame.size.height < self.imageView.frame.size.width)
183 | {
184 | zoomRect.origin.y -= (self.bounds.size.height - self.imageView.frame.size.height)/self.zoomScale ;
185 | }
186 | else
187 | {
188 | zoomRect.origin.y -= (self.bounds.size.height - self.imageView.frame.size.height)/2/self.zoomScale;
189 | }
190 | return zoomRect;
191 | }
192 |
193 | #pragma mark - Config Animation Methods
194 | - (void)normalModeShowWithSenderView:(UIView*)senderView
195 | {
196 | UIImageView *oriImageView = (UIImageView *)senderView;
197 | CGRect curFrame = [self imageView].frame;
198 |
199 | CGRect oriFrame = [oriImageView.superview convertRect:oriImageView.frame toView:self];
200 | self.senderFrame = oriFrame;
201 | [[self imageView] setFrame:oriFrame];
202 |
203 | if (oriFrame.origin.y<44)
204 | {
205 | _disableAutoLayout = YES;
206 |
207 | self.offscreenY = (oriFrame.origin.y<0)?(fabs(oriFrame.origin.y)+44):(44-oriFrame.origin.y);
208 | [[self imageView] setFrame:CGRectMake(oriFrame.origin.x, -self.offscreenY, oriFrame.size.width, oriFrame.size.height)];
209 |
210 | oriFrame.origin.y = 44.f;
211 | oriFrame.size.height -= self.offscreenY;
212 |
213 | CGRect clipRect = oriFrame;
214 | clipRect.origin.x = 0.f;
215 | clipRect.size.width = 320.f;
216 | UIView *clipsView = [[[UIView alloc] initWithFrame:clipRect] autorelease];
217 | [clipsView setClipsToBounds:YES];
218 | [clipsView setBackgroundColor:[UIColor clearColor]];
219 | [[self imageView] removeFromSuperview];
220 | [clipsView addSubview:self.imageView];
221 | [self addSubview:clipsView];
222 |
223 | [self setBackgroundColor:[UIColor clearColor]];
224 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
225 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
226 | [clipsView setFrame:curFrame];
227 | [self.imageView setFrame:CGRectMake((320-curFrame.size.width)/2, 0, curFrame.size.width, curFrame.size.height)];
228 | } completion:^(BOOL finished) {
229 | _disableAutoLayout = NO;
230 | [self.imageView removeFromSuperview];
231 | [self.imageView setFrame:curFrame];
232 | [clipsView removeFromSuperview];
233 | [self addSubview:self.imageView];
234 | }];
235 | }
236 | else
237 | {
238 | [self setBackgroundColor:[UIColor clearColor]];
239 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
240 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
241 | [[self imageView] setFrame:curFrame];
242 | } completion:^(BOOL finished) {}];
243 | }
244 | }
245 |
246 | - (void)configAnimationDisplay:(UIImageView *)senderView
247 | {
248 | if (nil == senderView) return;
249 |
250 | self.offscreenY = 0.f;
251 |
252 | switch (senderView.contentMode) {
253 | case UIViewContentModeScaleAspectFit:
254 | ivContentMode = UIViewContentModeScaleAspectFit;
255 | [self aspectFillModeShowWithSenderView:senderView];
256 | break;
257 | case UIViewContentModeScaleAspectFill:
258 | ivContentMode = UIViewContentModeScaleAspectFill;
259 | [self aspectFillModeShowWithSenderView:senderView];
260 | break;
261 | default:
262 | ivContentMode = UIViewContentModeScaleToFill;
263 | [self normalModeShowWithSenderView:senderView];
264 | break;
265 | }
266 | }
267 |
268 | - (void)aspectFillModeShowWithSenderView:(UIView*)senderView
269 | {
270 | UIImageView *oriImageView = (UIImageView *)senderView;
271 | CGRect curFrame = [self imageView].frame;
272 |
273 | _disableAutoLayout = YES;
274 |
275 | CGRect clipFrame = [senderView.superview convertRect:senderView.frame toView:self];
276 |
277 | if (clipFrame.origin.y<44)
278 | {
279 | self.offscreenY = (clipFrame.origin.y<0)?(fabs(clipFrame.origin.y)+44):(44-clipFrame.origin.y);
280 | clipFrame.origin.y = 44.f;
281 | clipFrame.size.height -= self.offscreenY;
282 | }
283 | self.clipsFrame = clipFrame;
284 |
285 | UIView *clipsView = [[[UIView alloc] initWithFrame:clipFrame] autorelease];
286 | [clipsView setClipsToBounds:YES];
287 | [[self imageView] removeFromSuperview];
288 | [clipsView addSubview:self.imageView];
289 | [self addSubview:clipsView];
290 |
291 | CGRect convertRect = senderView.frame;
292 | CGFloat hImageRate = oriImageView.image.size.height/oriImageView.image.size.width;
293 | CGFloat hViewRate = oriImageView.frame.size.height/oriImageView.frame.size.width;
294 |
295 | if ( (ivContentMode == UIViewContentModeScaleAspectFill && hImageRate > hViewRate) ||
296 | (ivContentMode == UIViewContentModeScaleAspectFit && hImageRate < hViewRate) )
297 | {
298 | convertRect.size.height = oriImageView.image.size.height*senderView.frame.size.width/oriImageView.image.size.width;
299 | convertRect.origin.y = senderView.frame.origin.y - (convertRect.size.height-senderView.frame.size.height)*0.5f;
300 | }
301 | else
302 | {
303 | convertRect.size.width = oriImageView.image.size.width*senderView.frame.size.height/oriImageView.image.size.height;
304 | convertRect.origin.x = senderView.frame.origin.x - (convertRect.size.width-senderView.frame.size.width)*0.5f;
305 | }
306 |
307 | CGRect oriFrame = [senderView.superview convertRect:convertRect toView:clipsView];
308 | self.senderFrame = oriFrame;
309 | [[self imageView] setFrame:oriFrame];
310 |
311 | [self setBackgroundColor:[UIColor clearColor]];
312 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
313 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
314 | [clipsView setFrame:curFrame];
315 | [self.imageView setFrame:CGRectMake(0, 0, curFrame.size.width, curFrame.size.height)];
316 | } completion:^(BOOL finished) {
317 | _disableAutoLayout = NO;
318 | [self.imageView removeFromSuperview];
319 | [self.imageView setFrame:curFrame];
320 | [clipsView removeFromSuperview];
321 | [self addSubview:self.imageView];
322 | }];
323 | }
324 |
325 | #pragma mark - PhotoView Clicked Methods
326 | - (void)zoomToLocation:(UITapGestureRecognizer *)tapGes
327 | {
328 | if ([self isZoomed])
329 | {
330 | [self setZoomScale:self.minimumZoomScale animated:YES];
331 | }
332 | else
333 | {
334 | CGRect zoomRect = [self zoomRectForScale:self.maximumZoomScale withCenter:[tapGes locationInView:self]];
335 | [self zoomToRect:zoomRect animated:YES];
336 | }
337 | }
338 |
339 | - (void)removePhotoView
340 | {
341 | _disableAutoLayout = YES;
342 |
343 | if (ivContentMode != UIViewContentModeScaleToFill)
344 | {
345 | UIView *clipView = [[[UIView alloc] initWithFrame:[self imageView].frame] autorelease];
346 | [clipView setClipsToBounds:YES];
347 | [self.imageView removeFromSuperview];
348 | [self.imageView setFrame:CGRectMake(0, 0, CGRectGetWidth([self imageView].frame), CGRectGetHeight([self imageView].frame))];
349 | [clipView addSubview:self.imageView];
350 | [self addSubview:clipView];
351 |
352 | [UIView animateWithDuration:0.3f animations:^{
353 | [clipView setFrame:self.clipsFrame];
354 | if ([self isZoomed])
355 | {
356 | [self setContentOffset:CGPointMake(0.f, 0.f)];
357 | }
358 | [[self imageView] setFrame:self.senderFrame];
359 | }];
360 |
361 | [UIView animateWithDuration:0.5f animations:^{
362 | [self setBackgroundColor:[UIColor clearColor]];
363 | } completion:^(BOOL finished) {
364 | [self removeFromSuperview];
365 | if (_block) _block(YES);
366 | }];
367 | }
368 | else
369 | {
370 | if (self.offscreenY != 0)
371 | {
372 | UIView *clipView = [[[UIView alloc] initWithFrame:[self imageView].frame] autorelease];
373 | [clipView setClipsToBounds:YES];
374 | [self.imageView removeFromSuperview];
375 | [self.imageView setFrame:CGRectMake(0, 0, CGRectGetWidth([self imageView].frame), CGRectGetHeight([self imageView].frame))];
376 | [clipView addSubview:self.imageView];
377 | [self addSubview:clipView];
378 |
379 | CGRect clipOriFrame = self.imageView.frame;
380 | clipOriFrame.origin.y = 44.f;
381 | clipOriFrame.size.height -= self.offscreenY;
382 |
383 | [UIView animateWithDuration:0.3f animations:^{
384 | [clipView setFrame:clipOriFrame];
385 | if ([self isZoomed])
386 | {
387 | [self setContentOffset:CGPointMake(0.f, 0.f)];
388 | }
389 | [[self imageView] setFrame:CGRectMake(self.senderFrame.origin.x, -self.offscreenY, self.senderFrame.size.width, self.senderFrame.size.height)];
390 | }];
391 |
392 | [UIView animateWithDuration:.5f animations:^{
393 | [self setBackgroundColor:[UIColor clearColor]];
394 | } completion:^(BOOL finished) {
395 | [self removeFromSuperview];
396 | if (_block) _block(YES);
397 | }];
398 | }
399 | else
400 | {
401 | [UIView animateWithDuration:0.3f animations:^{
402 | if ([self isZoomed])
403 | {
404 | [self setContentOffset:CGPointMake(0.f, 0.f)];
405 | }
406 | [[self imageView] setFrame:self.senderFrame];
407 | }];
408 |
409 | [UIView animateWithDuration:.5f animations:^{
410 | [self setBackgroundColor:[UIColor clearColor]];
411 | } completion:^(BOOL finished) {
412 | [self removeFromSuperview];
413 | if (_block) _block(YES);
414 | }];
415 | }
416 | }
417 | }
418 |
419 | - (void)dealloc
420 | {
421 | self.imageView = nil;
422 | [super dealloc];
423 | }
424 |
425 | @end
426 |
--------------------------------------------------------------------------------
/FSPhotoViewDemo/FSPhotoViewDemo/FSPhotoView/FSPhotoView.m:
--------------------------------------------------------------------------------
1 | //
2 | // FSPhotoView.m
3 | // FSPhotoView
4 | //
5 | // Created by Stephen Jin on 7/26/13.
6 | // Copyright (c) 2013 Stephen Jin. All rights reserved.
7 | //
8 |
9 | #import "FSPhotoView.h"
10 |
11 | @interface FSPhotoView () {
12 | BOOL _disableAutoLayout;
13 | UIViewContentMode ivContentMode;
14 | }
15 | @property (nonatomic, retain) UIImageView *imageView;
16 | @property (nonatomic, assign) CGRect senderFrame;
17 | @property (nonatomic, assign) CGRect clipsFrame;
18 | @property (nonatomic, assign) CGFloat offscreenY;
19 | @property (copy) DisappearBlcok block;
20 |
21 | - (void)displayImage:(UIImage *)newImage withSenderView:(UIImageView*)senderView;
22 | @end
23 |
24 | @implementation FSPhotoView
25 |
26 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView;
27 | {
28 | if (nil == senderView.image) return nil;
29 | return [FSPhotoView showImageWithSenderView:senderView completion:nil];
30 | }
31 |
32 | + (FSPhotoView *)showImageWithSenderView:(UIImageView*)senderView completion:(DisappearBlcok)completed;
33 | {
34 | if (nil == senderView.image) return nil;
35 |
36 | // Deal with staus bar.
37 | CGRect rect = [UIScreen mainScreen].bounds;
38 |
39 | if (![UIApplication sharedApplication].statusBarHidden)
40 | {
41 | rect.size.height = [UIScreen mainScreen].bounds.size.height-20.f;
42 | rect.origin.y += [UIApplication sharedApplication].statusBarHidden?0:20.f;
43 | }
44 |
45 | FSPhotoView *photoView = [[FSPhotoView alloc] initWithFrame:rect andCompleteBlock:completed];
46 | if (photoView)
47 | {
48 | [[UIApplication sharedApplication].keyWindow addSubview:photoView];
49 | [photoView displayImage:senderView.image withSenderView:senderView];
50 | }
51 | return [photoView autorelease];
52 | }
53 |
54 | - (id)initWithFrame:(CGRect)frame andCompleteBlock:(DisappearBlcok)block
55 | {
56 | self = [super initWithFrame:frame];
57 | if (self) {
58 | // Initialization code
59 | self.block = block;
60 | [self setDelegate:self];
61 | [self setMaximumZoomScale:2.f];
62 | [self setShowsHorizontalScrollIndicator:NO];
63 | [self setShowsVerticalScrollIndicator:NO];
64 | [self loadSubviewsWithFrame:frame];
65 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(removePhotoView)];
66 | [singleTap setNumberOfTapsRequired:1];
67 | [self addGestureRecognizer:singleTap];
68 | [singleTap release];
69 | UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(zoomToLocation:)];
70 | [doubleTap setNumberOfTapsRequired:2];
71 | [self addGestureRecognizer:doubleTap];
72 | [singleTap requireGestureRecognizerToFail:doubleTap];
73 | [doubleTap release];
74 | }
75 | return self;
76 | }
77 |
78 | - (void)loadSubviewsWithFrame:(CGRect)frame
79 | {
80 | self.imageView = [[[UIImageView alloc] initWithFrame:frame] autorelease];
81 | [self addSubview:self.imageView];
82 | }
83 |
84 | #pragma mark - Display Functions
85 | - (void)displayImage:(UIImage *)newImage withSenderView:(UIImageView*)senderView
86 | {
87 | CGSize frameSize = self.frame.size;
88 | CGSize imageSize = newImage.size;
89 | CGFloat xScale = frameSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
90 | CGFloat yScale = frameSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
91 | CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
92 | CGRect photoImageViewFrame;
93 |
94 | if (xScale > 1 && yScale > 1)
95 | {
96 | photoImageViewFrame.origin = CGPointZero;
97 | photoImageViewFrame.size.height = newImage.size.height*minScale;
98 | photoImageViewFrame.size.width = newImage.size.width*minScale;
99 | [self imageView].frame = photoImageViewFrame;
100 | self.contentSize = photoImageViewFrame.size;
101 | [self imageView].image = newImage;
102 | minScale = 1;
103 | }
104 | else
105 | {
106 | photoImageViewFrame.origin = CGPointZero;
107 | photoImageViewFrame.size = newImage.size;
108 | [self imageView].frame = photoImageViewFrame;
109 | self.contentSize = photoImageViewFrame.size;
110 | [self imageView].image = newImage;
111 | }
112 |
113 | self.maximumZoomScale = 2.f*minScale;//maxScale;
114 | self.minimumZoomScale = minScale;
115 | self.zoomScale = minScale;
116 |
117 | _disableAutoLayout = NO;
118 | [self layoutIfNeeded]; // call layoutSubviews
119 |
120 | [self configAnimationDisplay:senderView];
121 | }
122 |
123 | //Image in PhotoView cannot be zoom in without this function.
124 | - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
125 | {
126 | return self.imageView;
127 | }
128 |
129 | - (BOOL)isZoomed
130 | {
131 | return !([self zoomScale] == [self minimumZoomScale]);
132 | }
133 |
134 | - (void)layoutSubviews
135 | {
136 | [super layoutSubviews];
137 |
138 | if (_disableAutoLayout) return;
139 |
140 | // Center the image as it becomes smaller than the size of the screen
141 | CGSize boundsSize = [self bounds].size;
142 | CGRect frameToCenter = [self imageView].frame;
143 |
144 | // Horizontally
145 | if (frameToCenter.size.width < boundsSize.width)
146 | {
147 | frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
148 | }
149 | else
150 | {
151 | frameToCenter.origin.x = 0;
152 | }
153 |
154 | // Vertically
155 | if (frameToCenter.size.height < boundsSize.height)
156 | {
157 | frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
158 | }
159 | else
160 | {
161 | frameToCenter.origin.y = 0;
162 | }
163 |
164 | // Center
165 | if (!CGRectEqualToRect([self imageView].frame, frameToCenter))
166 | {
167 | [self imageView].frame = frameToCenter;
168 | }
169 | }
170 |
171 | - (CGRect)zoomRectForScale:(float)scale withCenter:(CGPoint)center
172 | {
173 | CGRect zoomRect;
174 |
175 | zoomRect.size.height = self.frame.size.height / scale;
176 | zoomRect.size.width = self.frame.size.width / scale;
177 | zoomRect.origin.x = center.x/self.zoomScale - (zoomRect.size.width /2.0 );
178 | zoomRect.origin.y = center.y/self.zoomScale - (zoomRect.size.height /2.0 );
179 |
180 | zoomRect.origin.x -= (self.bounds.size.width - self.imageView.frame.size.width)/2/self.zoomScale;
181 |
182 | if (center.y < self.bounds.size.height/2 && self.imageView.frame.size.height < self.imageView.frame.size.width)
183 | {
184 | zoomRect.origin.y -= (self.bounds.size.height - self.imageView.frame.size.height)/self.zoomScale ;
185 | }
186 | else
187 | {
188 | zoomRect.origin.y -= (self.bounds.size.height - self.imageView.frame.size.height)/2/self.zoomScale;
189 | }
190 | return zoomRect;
191 | }
192 |
193 | #pragma mark - Config Animation Methods
194 | - (void)normalModeShowWithSenderView:(UIView*)senderView
195 | {
196 | UIImageView *oriImageView = (UIImageView *)senderView;
197 | CGRect curFrame = [self imageView].frame;
198 |
199 | CGRect oriFrame = [oriImageView.superview convertRect:oriImageView.frame toView:self];
200 | self.senderFrame = oriFrame;
201 | [[self imageView] setFrame:oriFrame];
202 |
203 | if (oriFrame.origin.y<44)
204 | {
205 | _disableAutoLayout = YES;
206 |
207 | self.offscreenY = (oriFrame.origin.y<0)?(fabs(oriFrame.origin.y)+44):(44-oriFrame.origin.y);
208 | [[self imageView] setFrame:CGRectMake(oriFrame.origin.x, -self.offscreenY, oriFrame.size.width, oriFrame.size.height)];
209 |
210 | oriFrame.origin.y = 44.f;
211 | oriFrame.size.height -= self.offscreenY;
212 |
213 | CGRect clipRect = oriFrame;
214 | clipRect.origin.x = 0.f;
215 | clipRect.size.width = 320.f;
216 | UIView *clipsView = [[[UIView alloc] initWithFrame:clipRect] autorelease];
217 | [clipsView setClipsToBounds:YES];
218 | [clipsView setBackgroundColor:[UIColor clearColor]];
219 | [[self imageView] removeFromSuperview];
220 | [clipsView addSubview:self.imageView];
221 | [self addSubview:clipsView];
222 |
223 | [self setBackgroundColor:[UIColor clearColor]];
224 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
225 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
226 | [clipsView setFrame:curFrame];
227 | [self.imageView setFrame:CGRectMake((320-curFrame.size.width)/2, 0, curFrame.size.width, curFrame.size.height)];
228 | } completion:^(BOOL finished) {
229 | _disableAutoLayout = NO;
230 | [self.imageView removeFromSuperview];
231 | [self.imageView setFrame:curFrame];
232 | [clipsView removeFromSuperview];
233 | [self addSubview:self.imageView];
234 | }];
235 | }
236 | else
237 | {
238 | [self setBackgroundColor:[UIColor clearColor]];
239 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
240 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
241 | [[self imageView] setFrame:curFrame];
242 | } completion:^(BOOL finished) {}];
243 | }
244 | }
245 |
246 | - (void)configAnimationDisplay:(UIImageView *)senderView
247 | {
248 | if (nil == senderView) return;
249 |
250 | self.offscreenY = 0.f;
251 |
252 | switch (senderView.contentMode) {
253 | case UIViewContentModeScaleAspectFit:
254 | ivContentMode = UIViewContentModeScaleAspectFit;
255 | [self aspectFillModeShowWithSenderView:senderView];
256 | break;
257 | case UIViewContentModeScaleAspectFill:
258 | ivContentMode = UIViewContentModeScaleAspectFill;
259 | [self aspectFillModeShowWithSenderView:senderView];
260 | break;
261 | default:
262 | ivContentMode = UIViewContentModeScaleToFill;
263 | [self normalModeShowWithSenderView:senderView];
264 | break;
265 | }
266 | }
267 |
268 | - (void)aspectFillModeShowWithSenderView:(UIView*)senderView
269 | {
270 | UIImageView *oriImageView = (UIImageView *)senderView;
271 | CGRect curFrame = [self imageView].frame;
272 |
273 | _disableAutoLayout = YES;
274 |
275 | CGRect clipFrame = [senderView.superview convertRect:senderView.frame toView:self];
276 |
277 | if (clipFrame.origin.y<44)
278 | {
279 | self.offscreenY = (clipFrame.origin.y<0)?(fabs(clipFrame.origin.y)+44):(44-clipFrame.origin.y);
280 | clipFrame.origin.y = 44.f;
281 | clipFrame.size.height -= self.offscreenY;
282 | }
283 | self.clipsFrame = clipFrame;
284 |
285 | UIView *clipsView = [[[UIView alloc] initWithFrame:clipFrame] autorelease];
286 | [clipsView setClipsToBounds:YES];
287 | [[self imageView] removeFromSuperview];
288 | [clipsView addSubview:self.imageView];
289 | [self addSubview:clipsView];
290 |
291 | CGRect convertRect = senderView.frame;
292 | CGFloat hImageRate = oriImageView.image.size.height/oriImageView.image.size.width;
293 | CGFloat hViewRate = oriImageView.frame.size.height/oriImageView.frame.size.width;
294 |
295 | if ( (ivContentMode == UIViewContentModeScaleAspectFill && hImageRate > hViewRate) ||
296 | (ivContentMode == UIViewContentModeScaleAspectFit && hImageRate < hViewRate) )
297 | {
298 | convertRect.size.height = oriImageView.image.size.height*senderView.frame.size.width/oriImageView.image.size.width;
299 | convertRect.origin.y = senderView.frame.origin.y - (convertRect.size.height-senderView.frame.size.height)*0.5f;
300 | }
301 | else
302 | {
303 | convertRect.size.width = oriImageView.image.size.width*senderView.frame.size.height/oriImageView.image.size.height;
304 | convertRect.origin.x = senderView.frame.origin.x - (convertRect.size.width-senderView.frame.size.width)*0.5f;
305 | }
306 |
307 | CGRect oriFrame = [senderView.superview convertRect:convertRect toView:clipsView];
308 | self.senderFrame = oriFrame;
309 | [[self imageView] setFrame:oriFrame];
310 |
311 | [self setBackgroundColor:[UIColor clearColor]];
312 | [UIView animateWithDuration:.3f delay:.0f options:UIViewAnimationOptionCurveEaseInOut animations:^{
313 | [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.95f]];
314 | [clipsView setFrame:curFrame];
315 | [self.imageView setFrame:CGRectMake(0, 0, curFrame.size.width, curFrame.size.height)];
316 | } completion:^(BOOL finished) {
317 | _disableAutoLayout = NO;
318 | [self.imageView removeFromSuperview];
319 | [self.imageView setFrame:curFrame];
320 | [clipsView removeFromSuperview];
321 | [self addSubview:self.imageView];
322 | }];
323 | }
324 |
325 | #pragma mark - PhotoView Clicked Methods
326 | - (void)zoomToLocation:(UITapGestureRecognizer *)tapGes
327 | {
328 | if ([self isZoomed])
329 | {
330 | [self setZoomScale:self.minimumZoomScale animated:YES];
331 | }
332 | else
333 | {
334 | CGRect zoomRect = [self zoomRectForScale:self.maximumZoomScale withCenter:[tapGes locationInView:self]];
335 | [self zoomToRect:zoomRect animated:YES];
336 | }
337 | }
338 |
339 | - (void)removePhotoView
340 | {
341 | _disableAutoLayout = YES;
342 |
343 | if (ivContentMode != UIViewContentModeScaleToFill)
344 | {
345 | UIView *clipView = [[[UIView alloc] initWithFrame:[self imageView].frame] autorelease];
346 | [clipView setClipsToBounds:YES];
347 | [self.imageView removeFromSuperview];
348 | [self.imageView setFrame:CGRectMake(0, 0, CGRectGetWidth([self imageView].frame), CGRectGetHeight([self imageView].frame))];
349 | [clipView addSubview:self.imageView];
350 | [self addSubview:clipView];
351 |
352 | [UIView animateWithDuration:0.3f animations:^{
353 | [clipView setFrame:self.clipsFrame];
354 | if ([self isZoomed])
355 | {
356 | [self setContentOffset:CGPointMake(0.f, 0.f)];
357 | }
358 | [[self imageView] setFrame:self.senderFrame];
359 | }];
360 |
361 | [UIView animateWithDuration:0.5f animations:^{
362 | [self setBackgroundColor:[UIColor clearColor]];
363 | } completion:^(BOOL finished) {
364 | [self removeFromSuperview];
365 | if (_block) _block(YES);
366 | }];
367 | }
368 | else
369 | {
370 | if (self.offscreenY != 0)
371 | {
372 | UIView *clipView = [[[UIView alloc] initWithFrame:[self imageView].frame] autorelease];
373 | [clipView setClipsToBounds:YES];
374 | [self.imageView removeFromSuperview];
375 | [self.imageView setFrame:CGRectMake(0, 0, CGRectGetWidth([self imageView].frame), CGRectGetHeight([self imageView].frame))];
376 | [clipView addSubview:self.imageView];
377 | [self addSubview:clipView];
378 |
379 | CGRect clipOriFrame = self.imageView.frame;
380 | clipOriFrame.origin.y = 44.f;
381 | clipOriFrame.size.height -= self.offscreenY;
382 |
383 | [UIView animateWithDuration:0.3f animations:^{
384 | [clipView setFrame:clipOriFrame];
385 | if ([self isZoomed])
386 | {
387 | [self setContentOffset:CGPointMake(0.f, 0.f)];
388 | }
389 | [[self imageView] setFrame:CGRectMake(self.senderFrame.origin.x, -self.offscreenY, self.senderFrame.size.width, self.senderFrame.size.height)];
390 | }];
391 |
392 | [UIView animateWithDuration:.5f animations:^{
393 | [self setBackgroundColor:[UIColor clearColor]];
394 | } completion:^(BOOL finished) {
395 | [self removeFromSuperview];
396 | if (_block) _block(YES);
397 | }];
398 | }
399 | else
400 | {
401 | [UIView animateWithDuration:0.3f animations:^{
402 | if ([self isZoomed])
403 | {
404 | [self setContentOffset:CGPointMake(0.f, 0.f)];
405 | }
406 | [[self imageView] setFrame:self.senderFrame];
407 | }];
408 |
409 | [UIView animateWithDuration:.5f animations:^{
410 | [self setBackgroundColor:[UIColor clearColor]];
411 | } completion:^(BOOL finished) {
412 | [self removeFromSuperview];
413 | if (_block) _block(YES);
414 | }];
415 | }
416 | }
417 | }
418 |
419 | - (void)dealloc
420 | {
421 | self.imageView = nil;
422 | [super dealloc];
423 | }
424 |
425 | @end
426 |
--------------------------------------------------------------------------------