-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
247 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// | ||
// VIPhotoView.h | ||
// VIPhotoViewDemo | ||
// | ||
// Created by Vito on 1/7/15. | ||
// Copyright (c) 2015 vito. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface VIPhotoView : UIScrollView | ||
|
||
- (instancetype)initWithFrame:(CGRect)frame andImage:(UIImage *)image; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
// | ||
// VIPhotoView.m | ||
// VIPhotoViewDemo | ||
// | ||
// Created by Vito on 1/7/15. | ||
// Copyright (c) 2015 vito. All rights reserved. | ||
// | ||
|
||
#import "VIPhotoView.h" | ||
|
||
@interface UIImage (VIUtil) | ||
|
||
- (CGSize)sizeThatFits:(CGSize)size; | ||
|
||
@end | ||
|
||
@implementation UIImage (VIUtil) | ||
|
||
- (CGSize)sizeThatFits:(CGSize)size | ||
{ | ||
CGSize imageSize = CGSizeMake(self.size.width / self.scale, | ||
self.size.height / self.scale); | ||
|
||
CGFloat widthRatio = imageSize.width / size.width; | ||
CGFloat heightRatio = imageSize.height / size.height; | ||
|
||
if (widthRatio > heightRatio) { | ||
imageSize = CGSizeMake(imageSize.width / widthRatio, imageSize.height / widthRatio); | ||
} else { | ||
imageSize = CGSizeMake(imageSize.width / heightRatio, imageSize.height / heightRatio); | ||
} | ||
|
||
return imageSize; | ||
} | ||
|
||
@end | ||
|
||
@interface UIImageView (VIUtil) | ||
|
||
- (CGSize)contentSize; | ||
|
||
@end | ||
|
||
@implementation UIImageView (VIUtil) | ||
|
||
- (CGSize)contentSize | ||
{ | ||
return [self.image sizeThatFits:self.bounds.size]; | ||
} | ||
|
||
@end | ||
|
||
@interface VIPhotoView () <UIScrollViewDelegate> | ||
|
||
@property (nonatomic, strong) UIView *containerView; | ||
@property (nonatomic, strong) UIImageView *imageView; | ||
|
||
@property (nonatomic) BOOL rotating; | ||
@property (nonatomic) CGSize minSize; | ||
|
||
@end | ||
|
||
@implementation VIPhotoView | ||
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame andImage:(UIImage *)image | ||
{ | ||
self = [super initWithFrame:frame]; | ||
if (self) { | ||
self.delegate = self; | ||
self.bouncesZoom = YES; | ||
|
||
// Add container view | ||
UIView *containerView = [[UIView alloc] initWithFrame:self.bounds]; | ||
containerView.backgroundColor = [UIColor clearColor]; | ||
[self addSubview:containerView]; | ||
_containerView = containerView; | ||
|
||
// Add image view | ||
UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; | ||
imageView.frame = containerView.bounds; | ||
imageView.contentMode = UIViewContentModeScaleAspectFit; | ||
[containerView addSubview:imageView]; | ||
_imageView = imageView; | ||
|
||
// Fit container view's size to image size | ||
CGSize imageSize = imageView.contentSize; | ||
self.containerView.frame = CGRectMake(0, 0, imageSize.width, imageSize.height); | ||
imageView.bounds = CGRectMake(0, 0, imageSize.width, imageSize.height); | ||
imageView.center = CGPointMake(imageSize.width / 2, imageSize.height / 2); | ||
|
||
self.contentSize = imageSize; | ||
self.minSize = imageSize; | ||
|
||
|
||
[self setMaxMinZoomScale]; | ||
|
||
// Center containerView by set insets | ||
[self centerContent]; | ||
|
||
// Setup other events | ||
[self setupGestureRecognizer]; | ||
[self setupRotationNotification]; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (void)layoutSubviews | ||
{ | ||
[super layoutSubviews]; | ||
|
||
if (self.rotating) { | ||
self.rotating = NO; | ||
|
||
// update container view frame | ||
CGSize containerSize = self.containerView.frame.size; | ||
BOOL containerSmallerThanSelf = (containerSize.width < CGRectGetWidth(self.bounds)) && (containerSize.height < CGRectGetHeight(self.bounds)); | ||
|
||
CGSize imageSize = [self.imageView.image sizeThatFits:self.bounds.size]; | ||
CGFloat minZoomScale = imageSize.width / self.minSize.width; | ||
self.minimumZoomScale = minZoomScale; | ||
if (containerSmallerThanSelf || self.zoomScale == self.minimumZoomScale) { // 宽度或高度 都小于 self 的宽度和高度 | ||
self.zoomScale = minZoomScale; | ||
} | ||
|
||
// Center container view | ||
[self centerContent]; | ||
} | ||
} | ||
|
||
- (void)dealloc | ||
{ | ||
[[NSNotificationCenter defaultCenter] removeObserver:self]; | ||
} | ||
|
||
#pragma mark - Setup | ||
|
||
- (void)setupRotationNotification | ||
{ | ||
[[NSNotificationCenter defaultCenter] addObserver:self | ||
selector:@selector(orientationChanged:) | ||
name:UIApplicationDidChangeStatusBarOrientationNotification | ||
object:nil]; | ||
} | ||
|
||
- (void)setupGestureRecognizer | ||
{ | ||
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapHandler:)]; | ||
tapGestureRecognizer.numberOfTapsRequired = 2; | ||
[_containerView addGestureRecognizer:tapGestureRecognizer]; | ||
} | ||
|
||
#pragma mark - UIScrollViewDelegate | ||
|
||
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView | ||
{ | ||
return self.containerView; | ||
} | ||
|
||
- (void)scrollViewDidZoom:(UIScrollView *)scrollView | ||
{ | ||
[self centerContent]; | ||
} | ||
|
||
#pragma mark - GestureRecognizer | ||
|
||
- (void)tapHandler:(UITapGestureRecognizer *)recognizer | ||
{ | ||
if (self.zoomScale > self.minimumZoomScale) { | ||
[self setZoomScale:self.minimumZoomScale animated:YES]; | ||
} else if (self.zoomScale < self.maximumZoomScale) { | ||
CGPoint location = [recognizer locationInView:recognizer.view]; | ||
CGRect zoomToRect = CGRectMake(0, 0, 50, 50); | ||
zoomToRect.origin = CGPointMake(location.x - CGRectGetWidth(zoomToRect)/2, location.y - CGRectGetHeight(zoomToRect)/2); | ||
[self zoomToRect:zoomToRect animated:YES]; | ||
} | ||
} | ||
|
||
#pragma mark - Notification | ||
|
||
- (void)orientationChanged:(NSNotification *)notification | ||
{ | ||
self.rotating = YES; | ||
} | ||
|
||
#pragma mark - Helper | ||
|
||
- (void)setMaxMinZoomScale | ||
{ | ||
CGSize imageSize = self.imageView.image.size; | ||
CGSize imagePresentationSize = self.imageView.contentSize; | ||
CGFloat maxScale = MAX(imageSize.height / imagePresentationSize.height, imageSize.width / imagePresentationSize.width); | ||
self.maximumZoomScale = MAX(1, maxScale); // Should not less than 1 | ||
self.minimumZoomScale = 1.0; | ||
} | ||
|
||
- (void)centerContent | ||
{ | ||
CGRect frame = self.containerView.frame; | ||
|
||
CGFloat top = 0, left = 0; | ||
if (self.contentSize.width < self.bounds.size.width) { | ||
left = (self.bounds.size.width - self.contentSize.width) * 0.5f; | ||
} | ||
if (self.contentSize.height < self.bounds.size.height) { | ||
top = (self.bounds.size.height - self.contentSize.height) * 0.5f; | ||
} | ||
|
||
top -= frame.origin.y; | ||
left -= frame.origin.x; | ||
|
||
self.contentInset = UIEdgeInsetsMake(top, left, top, left); | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters