Skip to content

Commit

Permalink
Update file directory
Browse files Browse the repository at this point in the history
  • Loading branch information
vitoziv committed Jan 10, 2015
1 parent 7f97adf commit e1ec1d9
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 14 deletions.
15 changes: 15 additions & 0 deletions VIPhotoView/VIPhotoView.h
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
216 changes: 216 additions & 0 deletions VIPhotoView/VIPhotoView.m
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
30 changes: 16 additions & 14 deletions VIPhotoViewDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
objects = {

/* Begin PBXBuildFile section */
5F0A786B1A60D672006FB7E6 /* VIPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F0A786A1A60D672006FB7E6 /* VIPhotoView.m */; };
5F856E071A5D791C0011786E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F856E061A5D791C0011786E /* main.m */; };
5F856E0A1A5D791C0011786E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F856E091A5D791C0011786E /* AppDelegate.m */; };
5F856E0D1A5D791C0011786E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F856E0C1A5D791C0011786E /* ViewController.m */; };
5F856E101A5D791C0011786E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F856E0E1A5D791C0011786E /* Main.storyboard */; };
5F856E121A5D791C0011786E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F856E111A5D791C0011786E /* Images.xcassets */; };
5F856E151A5D791C0011786E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5F856E131A5D791C0011786E /* LaunchScreen.xib */; };
5F856E211A5D791C0011786E /* VIPhotoViewDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F856E201A5D791C0011786E /* VIPhotoViewDemoTests.m */; };
5F856E2D1A5D794C0011786E /* VIPhotoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F856E2C1A5D794C0011786E /* VIPhotoView.m */; };
5F856E2F1A5D7E290011786E /* test.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 5F856E2E1A5D7E290011786E /* test.jpg */; };
/* End PBXBuildFile section */

Expand All @@ -29,6 +29,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
5F0A78691A60D672006FB7E6 /* VIPhotoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VIPhotoView.h; sourceTree = "<group>"; };
5F0A786A1A60D672006FB7E6 /* VIPhotoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VIPhotoView.m; sourceTree = "<group>"; };
5F856E011A5D791C0011786E /* VIPhotoViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VIPhotoViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
5F856E051A5D791C0011786E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5F856E061A5D791C0011786E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand All @@ -42,8 +44,6 @@
5F856E1A1A5D791C0011786E /* VIPhotoViewDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VIPhotoViewDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5F856E1F1A5D791C0011786E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5F856E201A5D791C0011786E /* VIPhotoViewDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VIPhotoViewDemoTests.m; sourceTree = "<group>"; };
5F856E2B1A5D794C0011786E /* VIPhotoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VIPhotoView.h; sourceTree = "<group>"; };
5F856E2C1A5D794C0011786E /* VIPhotoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VIPhotoView.m; sourceTree = "<group>"; };
5F856E2E1A5D7E290011786E /* test.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test.jpg; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand All @@ -65,6 +65,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
5F0A78681A60D672006FB7E6 /* VIPhotoView */ = {
isa = PBXGroup;
children = (
5F0A78691A60D672006FB7E6 /* VIPhotoView.h */,
5F0A786A1A60D672006FB7E6 /* VIPhotoView.m */,
);
path = VIPhotoView;
sourceTree = "<group>";
};
5F856DF81A5D791C0011786E = {
isa = PBXGroup;
children = (
Expand All @@ -86,7 +95,7 @@
5F856E031A5D791C0011786E /* VIPhotoViewDemo */ = {
isa = PBXGroup;
children = (
5F856E2A1A5D79300011786E /* VIPhotoView */,
5F0A78681A60D672006FB7E6 /* VIPhotoView */,
5F856E081A5D791C0011786E /* AppDelegate.h */,
5F856E091A5D791C0011786E /* AppDelegate.m */,
5F856E0B1A5D791C0011786E /* ViewController.h */,
Expand Down Expand Up @@ -126,15 +135,6 @@
name = "Supporting Files";
sourceTree = "<group>";
};
5F856E2A1A5D79300011786E /* VIPhotoView */ = {
isa = PBXGroup;
children = (
5F856E2B1A5D794C0011786E /* VIPhotoView.h */,
5F856E2C1A5D794C0011786E /* VIPhotoView.m */,
);
path = VIPhotoView;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -236,7 +236,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F856E2D1A5D794C0011786E /* VIPhotoView.m in Sources */,
5F0A786B1A60D672006FB7E6 /* VIPhotoView.m in Sources */,
5F856E0D1A5D791C0011786E /* ViewController.m in Sources */,
5F856E0A1A5D791C0011786E /* AppDelegate.m in Sources */,
5F856E071A5D791C0011786E /* main.m in Sources */,
Expand Down Expand Up @@ -432,6 +432,7 @@
5F856E261A5D791C0011786E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5F856E271A5D791C0011786E /* Build configuration list for PBXNativeTarget "VIPhotoViewDemoTests" */ = {
isa = XCConfigurationList;
Expand All @@ -440,6 +441,7 @@
5F856E291A5D791C0011786E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down

0 comments on commit e1ec1d9

Please sign in to comment.