Full Code of cuzv/PhotoBrowser for AI

master bbbd1b7a4a1f cached
40 files
154.3 KB
43.4k tokens
1 requests
Download .txt
Repository: cuzv/PhotoBrowser
Branch: master
Commit: bbbd1b7a4a1f
Files: 40
Total size: 154.3 KB

Directory structure:
gitextract_xp5uz9xr/

├── .gitignore
├── CHANGELOG.md
├── Example/
│   ├── Application/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.m
│   │   ├── Info.plist
│   │   ├── LaunchScreen.storyboard
│   │   ├── Main.storyboard
│   │   ├── RootViewController.h
│   │   ├── RootViewController.m
│   │   ├── UIView+LayerImage.h
│   │   ├── UIView+LayerImage.m
│   │   └── main.m
│   ├── Assets.xcassets/
│   │   └── AppIcon.appiconset/
│   │       └── Contents.json
│   ├── LocalImagesExample/
│   │   ├── LocalImagesExampleViewController.h
│   │   ├── LocalImagesExampleViewController.m
│   │   ├── PBImageView.h
│   │   └── PBImageView.m
│   └── RemoteImagesExample/
│       ├── RemoteImagesExampleViewController.h
│       └── RemoteImagesExampleViewController.m
├── LICENSE
├── PhotoBrowser.podspec
├── PhotoBrowser.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           └── PhotoBrowser.xcscheme
├── PhotoBrowser.xcworkspace/
│   └── contents.xcworkspacedata
├── Podfile
├── README.md
└── Sources/
    ├── Info.plist
    ├── PBImageScrollView+internal.h
    ├── PBImageScrollView.h
    ├── PBImageScrollView.m
    ├── PBImageScrollViewController.h
    ├── PBImageScrollViewController.m
    ├── PBModalTransitionController.h
    ├── PBModalTransitionController.m
    ├── PBViewController.h
    ├── PBViewController.m
    ├── PhotoBrowser.h
    ├── UIView+PBSnapshot.h
    └── UIView+PBSnapshot.m

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
Pods/


================================================
FILE: CHANGELOG.md
================================================
# PhotoBrowser

PhotoBrowser is a light weight photo browser, like the wechat, weibo image viewer.

### 0.3.1

-   修复 issue [#3 ](https://github.com/cuzv/PhotoBrowser/issues/3)
-   废弃 `setInitializePageIndex:` ,改为 `pb_startPage`。
-   废弃 `PBViewControllerDataSource` 中的 `viewController:presentImageView:forPageAtIndex:`方法,改为 `viewController:presentImageView:forPageAtIndex:progressHandler`。
-   去除 `PBViewControllerDelegate` 中的 `viewController:didSingleTapedPageAtIndex:presentedImage` 退出事件。交由实现者来决定。(例如 Twitter 单击并不退出,而是切换显示工具栏。)


### 0.3.2

-   修复 thumb 图片为加载的情况下动画问题。
-   修复状态栏显示可能不复原的问题。



### 0.4

- 添加`reload`, `reloadWithCurrentPage:`方法,`numberOfPages`, `currentPage` 属性。

### 0.5

- 增加图片显示模式兼容,长图显示头部的时候 dismiss 动画图片会还原到 thumb 显示的位置。
- 修复 present 的时候画面拉升。
- 切换图片的时候隐藏对应的 thumb view。
- 优化内部实现逻辑(0.5.9)。


### 0.6

- 调节背景亮度。

### 0.6.1

- 修复 dismiss 后滑动界面奔溃的问题。

### 0.6.2

- 支持部不隐藏 thumb view 选项。

### 0.6.3

- 添加自定义退出操作

### 0.6.4

- Fix Issues #10


### 0.6.5

- Close Issues #4 & #12

### 0.7.0

- iOS 11 compatibility

### 0.8.0

- Support custom ImageView class to display.

### 0.8.1

- Support Storyboard using `PBImageScrollView`.

================================================
FILE: Example/Application/AppDelegate.h
================================================
//
//  AppDelegate.h
//  Example
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Moch. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;


@end



================================================
FILE: Example/Application/AppDelegate.m
================================================
//
//  AppDelegate.m
//  Example
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Moch. All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // 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.
    // 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.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    // 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.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    // 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.
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // 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.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end


================================================
FILE: Example/Application/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>PhotoBrowser</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>


================================================
FILE: Example/Application/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <animations/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>


================================================
FILE: Example/Application/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="NnH-CM-elI">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--PhotoBrowser Example-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="RootViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="pxy-rZ-lyi">
                                <rect key="frame" x="111.5" y="298.5" width="152" height="30"/>
                                <state key="normal" title="Local Images Example">
                                    <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                </state>
                                <connections>
                                    <segue destination="pDg-ap-afj" kind="show" id="M6V-fB-NYc"/>
                                </connections>
                            </button>
                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nmG-v5-ZGQ">
                                <rect key="frame" x="103" y="368.5" width="169" height="30"/>
                                <state key="normal" title="Remote Images Example">
                                    <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                </state>
                                <connections>
                                    <segue destination="X57-PX-3g3" kind="show" id="egG-OG-Sc8"/>
                                </connections>
                            </button>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="pxy-rZ-lyi" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" constant="-20" id="74J-vi-ffh"/>
                            <constraint firstItem="nmG-v5-ZGQ" firstAttribute="top" secondItem="pxy-rZ-lyi" secondAttribute="bottom" constant="40" id="8ez-dW-jQR"/>
                            <constraint firstItem="nmG-v5-ZGQ" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Ctd-rs-kga"/>
                            <constraint firstItem="pxy-rZ-lyi" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="Wp2-0g-7Sf"/>
                        </constraints>
                    </view>
                    <navigationItem key="navigationItem" title="PhotoBrowser Example" id="uDO-HD-xoC"/>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1036" y="988"/>
        </scene>
        <!--Local Images Example-->
        <scene sceneID="Jbr-48-l0d">
            <objects>
                <viewController id="pDg-ap-afj" customClass="LocalImagesExampleViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="MuC-Cf-kSi"/>
                        <viewControllerLayoutGuide type="bottom" id="fvt-6w-mlG"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="S3I-gP-uPz">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    </view>
                    <navigationItem key="navigationItem" title="Local Images Example" id="ahw-hQ-vNR"/>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="gQJ-5V-7hZ" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1908" y="729"/>
        </scene>
        <!--Remote Images Example-->
        <scene sceneID="5ID-Xn-YIa">
            <objects>
                <viewController id="X57-PX-3g3" customClass="RemoteImagesExampleViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="WrA-D3-nA2"/>
                        <viewControllerLayoutGuide type="bottom" id="oO5-Oq-lky"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="YK2-wT-8DW">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    </view>
                    <navigationItem key="navigationItem" title="Remote Images Example" id="QX6-Q1-hdc"/>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="XLi-20-rfg" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1076" y="1748"/>
        </scene>
        <!--Navigation Controller-->
        <scene sceneID="6qy-NB-9qR">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="NnH-CM-elI" sceneMemberID="viewController">
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="NqE-HQ-4l4">
                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
                        <autoresizingMask key="autoresizingMask"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="ZCJ-nY-HNL"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="0Ks-mz-C9Y" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="329" y="997"/>
        </scene>
    </scenes>
</document>


================================================
FILE: Example/Application/RootViewController.h
================================================
//
//  ViewController.h
//  Example
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Moch. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface RootViewController : UIViewController


@end



================================================
FILE: Example/Application/RootViewController.m
================================================
//
//  ViewController.m
//  Example
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Moch. All rights reserved.
//

#import "RootViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
@interface RootViewController ()
@end

@implementation RootViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end


================================================
FILE: Example/Application/UIView+LayerImage.h
================================================
//
//  UIView+LayerImage.h
//  PhotoBrowser
//
//  Created by Shaw on 3/1/17.
//  Copyright © 2017 RedRain. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UIView (LayerImage)

@property (nonatomic, strong, nullable) UIImage *layerImage;

@end


================================================
FILE: Example/Application/UIView+LayerImage.m
================================================
//
//  UIView+LayerImage.m
//  PhotoBrowser
//
//  Created by Shaw on 3/1/17.
//  Copyright © 2017 RedRain. All rights reserved.
//

#import "UIView+LayerImage.h"

@implementation UIView (LayerImage)

- (UIImage *)layerImage {
    return [[UIImage alloc] initWithCGImage:(__bridge CGImageRef _Nonnull)(self.layer.contents)];
}

- (void)setLayerImage:(UIImage *)layerImage {
    CGFloat iw = layerImage.size.width;
    CGFloat ih = layerImage.size.height;
    CGFloat vw = CGRectGetWidth(self.bounds);
    CGFloat vh = CGRectGetHeight(self.bounds);
    CGFloat scale = (ih / iw) / (vh / vw);
    if (!isnan(scale) && scale > 1) {
        // 高图只保留顶部
        self.contentMode = UIViewContentModeScaleToFill;
        self.layer.contentsRect = CGRectMake(0, 0, 1, (iw / ih) * (vh / vw));
    } else {
        // 宽图把左右两边裁掉
        self.contentMode = UIViewContentModeScaleAspectFill;
        self.layer.contentsRect = CGRectMake(0, 0, 1, 1);
    }
    self.layer.contents = (__bridge id _Nullable)(layerImage.CGImage);
}


@end


================================================
FILE: Example/Application/main.m
================================================
//
//  main.m
//  Example
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Moch. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}


================================================
FILE: Example/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "3x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "83.5x83.5",
      "scale" : "2x"
    },
    {
      "idiom" : "ios-marketing",
      "size" : "1024x1024",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/LocalImagesExample/LocalImagesExampleViewController.h
================================================
//
//  ViewController.h
//  PhotoBrowserSample
//
//  Created by Shaw on 8/31/15.
//  Copyright (c) 2015 Shaw. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface LocalImagesExampleViewController : UIViewController


@end



================================================
FILE: Example/LocalImagesExample/LocalImagesExampleViewController.m
================================================
//
//  ViewController.m
//  PhotoBrowserSample
//
//  Created by Shaw on 8/31/15.
//  Copyright (c) 2015 Shaw. All rights reserved.
//

#import "LocalImagesExampleViewController.h"
#import "PBViewController.h"
#import "UIView+LayerImage.h"
#import <SDWebImage/UIImage+GIF.h>
#import "PBImageView.h"

@interface LocalImagesExampleViewController () <PBViewControllerDataSource, PBViewControllerDelegate>
@property (nonatomic, strong) NSArray *frames;
@property (nonatomic, strong) NSMutableArray<UIView *> *imageViews;
@property (nonatomic, assign) BOOL thumb;
@end

@implementation LocalImagesExampleViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.imageViews = [@[] mutableCopy];
    for (NSInteger index = 0; index < self.frames.count; ++index) {
        UIView *imageView = [UIView new];
        imageView.clipsToBounds = YES;
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.backgroundColor = [UIColor orangeColor];
        imageView.frame = [self.frames[index] CGRectValue];
        imageView.tag = index;
        imageView.userInteractionEnabled = YES;
        imageView.layer.borderColor = [UIColor redColor].CGColor;
        imageView.layer.borderWidth = 1;
        NSString *imageName = [NSString stringWithFormat:@"little_%@", @(index + 1)];
        UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"]];
//        imageView.layer.contents = (__bridge id _Nullable)(image.CGImage);
        imageView.layerImage = image;
        [self.view addSubview:imageView];
        [self.imageViews addObject:imageView];
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapedImageView:)];
        [imageView addGestureRecognizer:tap];
    }
    
    self.thumb = YES;
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Don't Use Thumb" style:UIBarButtonItemStylePlain target:self action:@selector(_thumb:)];

}

- (void)handleTapedImageView:(UITapGestureRecognizer *)sender {
    [self _showPhotoBrowser:sender.view];
}

- (void)_showPhotoBrowser:(UIView *)sender {
    PBViewController *pbViewController = [PBViewController new];
    pbViewController.imageViewClass = PBImageView.class;
    pbViewController.blurBackground = NO;
//    pbViewController.hideThumb = NO;
    pbViewController.pb_dataSource = self;
    pbViewController.pb_delegate = self;
    pbViewController.pb_startPage = sender.tag;
    [self presentViewController:pbViewController animated:YES completion:^{
    }];
}

- (void)_thumb:(UIBarButtonItem *)sender {
    self.thumb = !self.thumb;
    sender.title = !self.thumb ? @"Use Thumb" : @"Don't Use Thumb";
}

- (NSArray *)frames {
    NSValue *frame1 = [NSValue valueWithCGRect:CGRectMake(20, 70, 80, 80)]; // 正方形
    NSValue *frame2 = [NSValue valueWithCGRect:CGRectMake(110, 70, 120, 80)]; // 长方形 (w>h)
    NSValue *frame3 = [NSValue valueWithCGRect:CGRectMake(240, 70, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame4 = [NSValue valueWithCGRect:CGRectMake(20, 180, 80, 80)]; // 正方形
    NSValue *frame5 = [NSValue valueWithCGRect:CGRectMake(110, 180, 120, 80)]; // 长方形 (w>h)
    NSValue *frame6 = [NSValue valueWithCGRect:CGRectMake(240, 180, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame7 = [NSValue valueWithCGRect:CGRectMake(20, 290, 80, 80)]; // 正方形
    NSValue *frame8 = [NSValue valueWithCGRect:CGRectMake(110, 290, 120, 80)]; // 长方形 (w>h)
    NSValue *frame9 = [NSValue valueWithCGRect:CGRectMake(240, 290, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame10 = [NSValue valueWithCGRect:CGRectMake(20, 400, 80, 80)]; // 正方形
    NSValue *frame11 = [NSValue valueWithCGRect:CGRectMake(110, 400, 120, 80)]; // 长方形 (w>h)
    NSValue *frame12 = [NSValue valueWithCGRect:CGRectMake(270, 400, 80, 130)]; // 长方形 (h>w)
    
    NSValue *frame13 = [NSValue valueWithCGRect:CGRectMake(20, 490, 120, 120)]; // 正方形
    NSValue *frame14 = [NSValue valueWithCGRect:CGRectMake(150, 490, 100, 160)]; // 等比例
    NSValue *frame15 = [NSValue valueWithCGRect:CGRectMake(270, 550, 100, 100)]; // GIF
    
    return @[frame1, frame2, frame3, frame4, frame5, frame6, frame7, frame8, frame9, frame10, frame11, frame12, frame13, frame14, frame15];
}

#pragma mark - PBViewControllerDataSource

- (NSInteger)numberOfPagesInViewController:(PBViewController *)viewController {
    return self.frames.count;
}

- (UIImage *)viewController:(PBViewController *)viewController imageForPageAtIndex:(NSInteger)index {
    NSString *path = [[NSBundle mainBundle] pathForResource:[@(index + 1) stringValue] ofType:@"jpg"];
    if (index == 14) {
        // GIF Support.
        NSData *rawData = [NSData dataWithContentsOfFile:path];
        return [UIImage sd_animatedGIFWithData:rawData];
    } else {
        return [UIImage imageWithContentsOfFile:path];
    }
}

- (UIView *)thumbViewForPageAtIndex:(NSInteger)index {
    if (self.thumb) {
        return self.imageViews[index];
    }

    return nil;
}

#pragma mark - PBViewControllerDelegate

- (void)viewController:(PBViewController *)viewController didSingleTapedPageAtIndex:(NSInteger)index presentedImage:(__kindof UIImage *)presentedImage {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewController:(PBViewController *)viewController didLongPressedPageAtIndex:(NSInteger)index presentedImage:(__kindof UIImage *)presentedImage {
    NSLog(@"didLongPressedPageAtIndex: %@", @(index));
}

@end



================================================
FILE: Example/LocalImagesExample/PBImageView.h
================================================
//
//  PBImageView.h
//  Example
//
//  Created by Shaw on 6/9/18.
//  Copyright © 2018 Shaw. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface PBImageView : UIImageView

@end


================================================
FILE: Example/LocalImagesExample/PBImageView.m
================================================
//
//  PBImageView.m
//  Example
//
//  Created by Shaw on 6/9/18.
//  Copyright © 2018 Shaw. All rights reserved.
//

#import "PBImageView.h"

@implementation PBImageView

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end


================================================
FILE: Example/RemoteImagesExample/RemoteImagesExampleViewController.h
================================================
//
//  DemoViewController.h
//  PhotoBrowser
//
//  Created by Shaw on 5/13/16.
//  Copyright © 2016 Shaw. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface RemoteImagesExampleViewController : UIViewController

@end


================================================
FILE: Example/RemoteImagesExample/RemoteImagesExampleViewController.m
================================================
//
//  DemoViewController.m
//  PhotoBrowser
//
//  Created by Shaw on 5/13/16.
//  Copyright © 2016 Shaw. All rights reserved.
//

#import "RemoteImagesExampleViewController.h"
#import "PBViewController.h"
#import <SDWebImage/UIImageView+WebCache.h>
#import "PBImageView.h"

@interface RemoteImagesExampleViewController () <PBViewControllerDataSource, PBViewControllerDelegate>
@property (nonatomic, strong) NSArray *frames;
@property (nonatomic, strong) NSMutableArray<UIImageView *> *imageViews;
@property (nonatomic, assign) BOOL thumb;
@end

@implementation RemoteImagesExampleViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thumb = YES;
    self.imageViews = [@[] mutableCopy];
    
    for (NSInteger index = 0; index < self.frames.count; ++index) {
        UIImageView *imageView = [UIImageView new];
        imageView.clipsToBounds = YES;
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.backgroundColor = [UIColor blackColor];
        imageView.frame = [self.frames[index] CGRectValue];
        imageView.tag = index;
        imageView.userInteractionEnabled = YES;
        imageView.layer.borderColor = [UIColor redColor].CGColor;
        imageView.layer.borderWidth = 1;
        NSString *imageName = [NSString stringWithFormat:@"little_%@", @(index + 1)];
        imageView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"]];
        [self.view addSubview:imageView];

        [self.imageViews addObject:imageView];

        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapedImageView:)];
        [imageView addGestureRecognizer:tap];
    }
    
    UIBarButtonItem *clear = [[UIBarButtonItem alloc] initWithTitle:@"Clear" style:UIBarButtonItemStylePlain target:self action:@selector(_clear)];
    UIBarButtonItem *thumb = [[UIBarButtonItem alloc] initWithTitle:@"Don't Use Thumb" style:UIBarButtonItemStylePlain target:self action:@selector(_thumb:)];
    UIToolbar *toolbar = [[UIToolbar alloc] init];
    toolbar.frame = CGRectMake(0, CGRectGetHeight(self.view.bounds) - 44, CGRectGetWidth(self.view.bounds), 44);
    [toolbar setItems:@[clear, thumb] animated:NO];
    [self.view addSubview:toolbar];
}

- (void)handleTapedImageView:(UITapGestureRecognizer *)sender {
    [self _showPhotoBrowser:sender.view];
}

- (void)_showPhotoBrowser:(UIView *)sender {
    PBViewController *pbViewController = [PBViewController new];
    pbViewController.imageViewClass = PBImageView.class;
    pbViewController.pb_dataSource = self;
    pbViewController.pb_delegate = self;
    pbViewController.pb_startPage = sender.tag;
//    pbViewController.exit = ^(PBViewController *sender) {
//        [sender.navigationController popViewControllerAnimated:YES];
//    };
    [self presentViewController:pbViewController animated:YES completion:nil];
}


- (void)_clear {
    [[SDImageCache sharedImageCache] clearMemory];
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
        NSLog(@"Cache clear complete.");
    }];
}

- (void)_thumb:(UIBarButtonItem *)sender {
    self.thumb = !self.thumb;
    sender.title = !self.thumb ? @"Use Thumb" : @"Don't Use Thumb";
}


- (NSArray *)frames {
    NSValue *frame1 = [NSValue valueWithCGRect:CGRectMake(20, 70, 80, 80)]; // 正方形
    NSValue *frame2 = [NSValue valueWithCGRect:CGRectMake(110, 70, 120, 80)]; // 长方形 (w>h)
    NSValue *frame3 = [NSValue valueWithCGRect:CGRectMake(240, 70, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame4 = [NSValue valueWithCGRect:CGRectMake(20, 180, 80, 80)]; // 正方形
    NSValue *frame5 = [NSValue valueWithCGRect:CGRectMake(110, 180, 120, 80)]; // 长方形 (w>h)
    NSValue *frame6 = [NSValue valueWithCGRect:CGRectMake(240, 180, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame7 = [NSValue valueWithCGRect:CGRectMake(20, 290, 80, 80)]; // 正方形
    NSValue *frame8 = [NSValue valueWithCGRect:CGRectMake(110, 290, 120, 80)]; // 长方形 (w>h)
    NSValue *frame9 = [NSValue valueWithCGRect:CGRectMake(240, 290, 80, 100)]; // 长方形 (h>w)
    
    NSValue *frame10 = [NSValue valueWithCGRect:CGRectMake(20, 400, 80, 80)]; // 正方形
    NSValue *frame11 = [NSValue valueWithCGRect:CGRectMake(110, 400, 120, 80)]; // 长方形 (w>h)
    NSValue *frame12 = [NSValue valueWithCGRect:CGRectMake(270, 400, 80, 130)]; // 长方形 (h>w)
    
    NSValue *frame13 = [NSValue valueWithCGRect:CGRectMake(20, 490, 120, 120)]; // 正方形
    NSValue *frame14 = [NSValue valueWithCGRect:CGRectMake(150, 490, 100, 160)]; // 等比例
    NSValue *frame15 = [NSValue valueWithCGRect:CGRectMake(270, 550, 100, 100)]; // GIF
    
    return @[frame1, frame2, frame3, frame4, frame5, frame6, frame7, frame8, frame9, frame10, frame11, frame12, frame13, frame14, frame15];
}

#pragma mark - PBViewControllerDataSource

- (NSInteger)numberOfPagesInViewController:(PBViewController *)viewController {
    return self.frames.count;
}

- (void)viewController:(PBViewController *)viewController presentImageView:(__kindof UIImageView *)imageView forPageAtIndex:(NSInteger)index progressHandler:(void (^)(NSInteger, NSInteger))progressHandler {
    NSString *url = [NSString stringWithFormat:@"https://raw.githubusercontent.com/cuzv/PhotoBrowser/master/Example/Assets/%@.jpg", @(index + 1)];
    UIImage *placeholder = self.imageViews[index].image;
    [imageView sd_setImageWithURL:[NSURL URLWithString:url]
                 placeholderImage:placeholder
                          options:0
                         progress:progressHandler
                        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                        }];
}

- (UIView *)thumbViewForPageAtIndex:(NSInteger)index {
    if (self.thumb) {
        return self.imageViews[index];
    }
    return nil;
}

#pragma mark - PBViewControllerDelegate

- (void)viewController:(PBViewController *)viewController didSingleTapedPageAtIndex:(NSInteger)index presentedImage:(UIImage *)presentedImage {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewController:(PBViewController *)viewController didLongPressedPageAtIndex:(NSInteger)index presentedImage:(UIImage *)presentedImage {
    NSLog(@"didLongPressedPageAtIndex: %@", @(index));
}

@end


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Moch Xiao

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: PhotoBrowser.podspec
================================================
Pod::Spec.new do |s|
  s.name         = "PhotoBrowser"
  s.version      = "0.8.1"
  s.summary      = "PhotoBrowser is a light weight photo browser, like the wechat, weibo image viewer."

  s.homepage     = "https://github.com/cuzv/PhotoBrowser.git"
  s.license      = "MIT"
  s.author       = { "Roy Shaw" => "cuzval@gmail.com" }
  s.platform     = :ios, "8.0"
  s.requires_arc = true
  s.source       = { :git => "https://github.com/cuzv/PhotoBrowser.git",
:tag => s.version.to_s }
  s.source_files = "Sources/*.{h,m}"
  s.frameworks   = 'Foundation', 'UIKit'
end


================================================
FILE: PhotoBrowser.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		464879031E6B1099009BEF95 /* 12.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879011E6B1099009BEF95 /* 12.jpg */; };
		464879041E6B1099009BEF95 /* 13.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879021E6B1099009BEF95 /* 13.jpg */; };
		464879061E6B122D009BEF95 /* 14.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879051E6B122D009BEF95 /* 14.jpg */; };
		464879181E6C099E009BEF95 /* little_1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4648790A1E6C099E009BEF95 /* little_1.jpg */; };
		4648791A1E6C099E009BEF95 /* little_3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4648790C1E6C099E009BEF95 /* little_3.jpg */; };
		4648791B1E6C099E009BEF95 /* little_4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4648790D1E6C099E009BEF95 /* little_4.jpg */; };
		4648791D1E6C099E009BEF95 /* little_6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4648790F1E6C099E009BEF95 /* little_6.jpg */; };
		4648791E1E6C099E009BEF95 /* little_7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879101E6C099E009BEF95 /* little_7.jpg */; };
		4648791F1E6C099E009BEF95 /* little_8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879111E6C099E009BEF95 /* little_8.jpg */; };
		464879201E6C099E009BEF95 /* little_9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879121E6C099E009BEF95 /* little_9.jpg */; };
		464879211E6C099E009BEF95 /* little_10.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879131E6C099E009BEF95 /* little_10.jpg */; };
		464879221E6C099E009BEF95 /* little_11.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879141E6C099E009BEF95 /* little_11.jpg */; };
		464879231E6C099E009BEF95 /* little_12.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879151E6C099E009BEF95 /* little_12.jpg */; };
		4648792A1E6C0A11009BEF95 /* little_14.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879261E6C0A11009BEF95 /* little_14.jpg */; };
		4648792B1E6C0A11009BEF95 /* little_13.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879271E6C0A11009BEF95 /* little_13.jpg */; };
		4648792C1E6C0A11009BEF95 /* little_5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879281E6C0A11009BEF95 /* little_5.jpg */; };
		4648792D1E6C0A11009BEF95 /* little_2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464879291E6C0A11009BEF95 /* little_2.jpg */; };
		464FF3DD1CEB5B390060DD84 /* PBModalTransitionController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464FF3DB1CEB5B390060DD84 /* PBModalTransitionController.h */; };
		464FF3F21CED60F50060DD84 /* 3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464FF3EF1CED60F50060DD84 /* 3.jpg */; };
		464FF3F31CED60F50060DD84 /* 8.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464FF3F01CED60F50060DD84 /* 8.jpg */; };
		464FF3F41CED60F50060DD84 /* 9.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 464FF3F11CED60F50060DD84 /* 9.jpg */; };
		4686F8401CEA2C7A00AFEA65 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4686F83F1CEA2C7A00AFEA65 /* Assets.xcassets */; };
		4686F8551CEA2EC200AFEA65 /* PBImageScrollViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F84A1CEA2EC200AFEA65 /* PBImageScrollViewController.h */; };
		4686F8561CEA2EC200AFEA65 /* PBImageScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F84B1CEA2EC200AFEA65 /* PBImageScrollViewController.m */; };
		4686F8571CEA2EC200AFEA65 /* PBImageScrollView+internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F84C1CEA2EC200AFEA65 /* PBImageScrollView+internal.h */; };
		4686F8581CEA2EC200AFEA65 /* PBImageScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F84D1CEA2EC200AFEA65 /* PBImageScrollView.h */; };
		4686F8591CEA2EC200AFEA65 /* PBImageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F84E1CEA2EC200AFEA65 /* PBImageScrollView.m */; };
		4686F85A1CEA2EC200AFEA65 /* PBViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F84F1CEA2EC200AFEA65 /* PBViewController.h */; };
		4686F85B1CEA2EC200AFEA65 /* PBViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8501CEA2EC200AFEA65 /* PBViewController.m */; };
		4686F85C1CEA2EC200AFEA65 /* PhotoBrowser.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F8511CEA2EC200AFEA65 /* PhotoBrowser.h */; settings = {ATTRIBUTES = (Public, ); }; };
		4686F85D1CEA2EC200AFEA65 /* UIView+PBSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = 4686F8521CEA2EC200AFEA65 /* UIView+PBSnapshot.h */; };
		4686F85E1CEA2EC200AFEA65 /* UIView+PBSnapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8531CEA2EC200AFEA65 /* UIView+PBSnapshot.m */; };
		4686F86D1CEA30EF00AFEA65 /* 1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8631CEA30EF00AFEA65 /* 1.jpg */; };
		4686F86E1CEA30EF00AFEA65 /* 10.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8641CEA30EF00AFEA65 /* 10.jpg */; };
		4686F86F1CEA30EF00AFEA65 /* 2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8651CEA30EF00AFEA65 /* 2.jpg */; };
		4686F8711CEA30EF00AFEA65 /* 4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8671CEA30EF00AFEA65 /* 4.jpg */; };
		4686F8721CEA30EF00AFEA65 /* 5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8681CEA30EF00AFEA65 /* 5.jpg */; };
		4686F8731CEA30EF00AFEA65 /* 6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F8691CEA30EF00AFEA65 /* 6.jpg */; };
		4686F8741CEA30EF00AFEA65 /* 7.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4686F86A1CEA30EF00AFEA65 /* 7.jpg */; };
		4686F87D1CEA30FA00AFEA65 /* LocalImagesExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8791CEA30FA00AFEA65 /* LocalImagesExampleViewController.m */; };
		4686F87E1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F87C1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.m */; };
		4686F8861CEA312B00AFEA65 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8811CEA312B00AFEA65 /* AppDelegate.m */; };
		4686F8881CEA312B00AFEA65 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8831CEA312B00AFEA65 /* main.m */; };
		4686F8891CEA312B00AFEA65 /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4686F8851CEA312B00AFEA65 /* RootViewController.m */; };
		4686F88C1CEA315200AFEA65 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4686F88A1CEA315200AFEA65 /* LaunchScreen.storyboard */; };
		4686F88D1CEA315200AFEA65 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4686F88B1CEA315200AFEA65 /* Main.storyboard */; };
		4686F88E1CEA329D00AFEA65 /* PhotoBrowser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4686F8191CEA2C4000AFEA65 /* PhotoBrowser.framework */; };
		4686F88F1CEA329D00AFEA65 /* PhotoBrowser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4686F8191CEA2C4000AFEA65 /* PhotoBrowser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
		4689283F1E698A440067E5E0 /* 11.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4689283E1E698A440067E5E0 /* 11.jpg */; };
		46EA44C91EBDAACA00416710 /* 15.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 46EA44C71EBDAACA00416710 /* 15.jpg */; };
		46EA44CA1EBDAACA00416710 /* little_15.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 46EA44C81EBDAACA00416710 /* little_15.jpg */; };
		46FE95A01E67090D00D36D6E /* UIView+LayerImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 46FE959F1E67090D00D36D6E /* UIView+LayerImage.m */; };
		688D8F2E0D2DF290D2734E5B /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0FDBFEF6AB6111BE128F7140 /* Pods_Example.framework */; };
		F902516B20CBB8ED00067CB8 /* PBImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = F902516A20CBB8ED00067CB8 /* PBImageView.m */; };
		F902516D20CBC15000067CB8 /* PBModalTransitionController.m in Sources */ = {isa = PBXBuildFile; fileRef = F902516C20CBC14F00067CB8 /* PBModalTransitionController.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		4686F8901CEA329D00AFEA65 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 4686F8101CEA2C4000AFEA65 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 4686F8181CEA2C4000AFEA65;
			remoteInfo = PhotoBrowser;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		4686F8921CEA329D00AFEA65 /* Embed Frameworks */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
				4686F88F1CEA329D00AFEA65 /* PhotoBrowser.framework in Embed Frameworks */,
			);
			name = "Embed Frameworks";
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		0FDBFEF6AB6111BE128F7140 /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		464879011E6B1099009BEF95 /* 12.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 12.jpg; sourceTree = "<group>"; };
		464879021E6B1099009BEF95 /* 13.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 13.jpg; sourceTree = "<group>"; };
		464879051E6B122D009BEF95 /* 14.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 14.jpg; sourceTree = "<group>"; };
		4648790A1E6C099E009BEF95 /* little_1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_1.jpg; sourceTree = "<group>"; };
		4648790C1E6C099E009BEF95 /* little_3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_3.jpg; sourceTree = "<group>"; };
		4648790D1E6C099E009BEF95 /* little_4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_4.jpg; sourceTree = "<group>"; };
		4648790F1E6C099E009BEF95 /* little_6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_6.jpg; sourceTree = "<group>"; };
		464879101E6C099E009BEF95 /* little_7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_7.jpg; sourceTree = "<group>"; };
		464879111E6C099E009BEF95 /* little_8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_8.jpg; sourceTree = "<group>"; };
		464879121E6C099E009BEF95 /* little_9.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_9.jpg; sourceTree = "<group>"; };
		464879131E6C099E009BEF95 /* little_10.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_10.jpg; sourceTree = "<group>"; };
		464879141E6C099E009BEF95 /* little_11.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_11.jpg; sourceTree = "<group>"; };
		464879151E6C099E009BEF95 /* little_12.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_12.jpg; sourceTree = "<group>"; };
		464879261E6C0A11009BEF95 /* little_14.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_14.jpg; sourceTree = "<group>"; };
		464879271E6C0A11009BEF95 /* little_13.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_13.jpg; sourceTree = "<group>"; };
		464879281E6C0A11009BEF95 /* little_5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_5.jpg; sourceTree = "<group>"; };
		464879291E6C0A11009BEF95 /* little_2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_2.jpg; sourceTree = "<group>"; };
		464FF3DB1CEB5B390060DD84 /* PBModalTransitionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBModalTransitionController.h; sourceTree = "<group>"; };
		464FF3EF1CED60F50060DD84 /* 3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 3.jpg; sourceTree = "<group>"; };
		464FF3F01CED60F50060DD84 /* 8.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 8.jpg; sourceTree = "<group>"; };
		464FF3F11CED60F50060DD84 /* 9.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 9.jpg; sourceTree = "<group>"; };
		4686F8191CEA2C4000AFEA65 /* PhotoBrowser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PhotoBrowser.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		4686F8311CEA2C7A00AFEA65 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
		4686F83F1CEA2C7A00AFEA65 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		4686F8491CEA2EC200AFEA65 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		4686F84A1CEA2EC200AFEA65 /* PBImageScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBImageScrollViewController.h; sourceTree = "<group>"; };
		4686F84B1CEA2EC200AFEA65 /* PBImageScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBImageScrollViewController.m; sourceTree = "<group>"; };
		4686F84C1CEA2EC200AFEA65 /* PBImageScrollView+internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PBImageScrollView+internal.h"; sourceTree = "<group>"; };
		4686F84D1CEA2EC200AFEA65 /* PBImageScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBImageScrollView.h; sourceTree = "<group>"; };
		4686F84E1CEA2EC200AFEA65 /* PBImageScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBImageScrollView.m; sourceTree = "<group>"; };
		4686F84F1CEA2EC200AFEA65 /* PBViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBViewController.h; sourceTree = "<group>"; };
		4686F8501CEA2EC200AFEA65 /* PBViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBViewController.m; sourceTree = "<group>"; };
		4686F8511CEA2EC200AFEA65 /* PhotoBrowser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoBrowser.h; sourceTree = "<group>"; };
		4686F8521CEA2EC200AFEA65 /* UIView+PBSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+PBSnapshot.h"; sourceTree = "<group>"; };
		4686F8531CEA2EC200AFEA65 /* UIView+PBSnapshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+PBSnapshot.m"; sourceTree = "<group>"; };
		4686F8631CEA30EF00AFEA65 /* 1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 1.jpg; sourceTree = "<group>"; };
		4686F8641CEA30EF00AFEA65 /* 10.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 10.jpg; sourceTree = "<group>"; };
		4686F8651CEA30EF00AFEA65 /* 2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 2.jpg; sourceTree = "<group>"; };
		4686F8671CEA30EF00AFEA65 /* 4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 4.jpg; sourceTree = "<group>"; };
		4686F8681CEA30EF00AFEA65 /* 5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 5.jpg; sourceTree = "<group>"; };
		4686F8691CEA30EF00AFEA65 /* 6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 6.jpg; sourceTree = "<group>"; };
		4686F86A1CEA30EF00AFEA65 /* 7.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 7.jpg; sourceTree = "<group>"; };
		4686F8781CEA30FA00AFEA65 /* LocalImagesExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalImagesExampleViewController.h; sourceTree = "<group>"; };
		4686F8791CEA30FA00AFEA65 /* LocalImagesExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LocalImagesExampleViewController.m; sourceTree = "<group>"; };
		4686F87B1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteImagesExampleViewController.h; sourceTree = "<group>"; };
		4686F87C1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RemoteImagesExampleViewController.m; sourceTree = "<group>"; };
		4686F8801CEA312B00AFEA65 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
		4686F8811CEA312B00AFEA65 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
		4686F8821CEA312B00AFEA65 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		4686F8831CEA312B00AFEA65 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		4686F8841CEA312B00AFEA65 /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = "<group>"; };
		4686F8851CEA312B00AFEA65 /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = "<group>"; };
		4686F88A1CEA315200AFEA65 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
		4686F88B1CEA315200AFEA65 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
		4689283E1E698A440067E5E0 /* 11.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 11.jpg; sourceTree = "<group>"; };
		46EA44C71EBDAACA00416710 /* 15.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 15.jpg; sourceTree = "<group>"; };
		46EA44C81EBDAACA00416710 /* little_15.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = little_15.jpg; sourceTree = "<group>"; };
		46FE959E1E67090D00D36D6E /* UIView+LayerImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LayerImage.h"; sourceTree = "<group>"; };
		46FE959F1E67090D00D36D6E /* UIView+LayerImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LayerImage.m"; sourceTree = "<group>"; };
		50F46F9330F91C01C080BDC8 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = "<group>"; };
		69D32124846D71E6131AAB59 /* Pods-Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.debug.xcconfig"; sourceTree = "<group>"; };
		F902516920CBB8ED00067CB8 /* PBImageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBImageView.h; sourceTree = "<group>"; };
		F902516A20CBB8ED00067CB8 /* PBImageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBImageView.m; sourceTree = "<group>"; };
		F902516C20CBC14F00067CB8 /* PBModalTransitionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBModalTransitionController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		4686F8151CEA2C4000AFEA65 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		4686F82E1CEA2C7A00AFEA65 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				4686F88E1CEA329D00AFEA65 /* PhotoBrowser.framework in Frameworks */,
				688D8F2E0D2DF290D2734E5B /* Pods_Example.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		3D34CC32B190B0D579E9B1E6 /* Pods */ = {
			isa = PBXGroup;
			children = (
				69D32124846D71E6131AAB59 /* Pods-Example.debug.xcconfig */,
				50F46F9330F91C01C080BDC8 /* Pods-Example.release.xcconfig */,
			);
			name = Pods;
			sourceTree = "<group>";
		};
		4686F80F1CEA2C4000AFEA65 = {
			isa = PBXGroup;
			children = (
				4686F8481CEA2EC200AFEA65 /* Sources */,
				4686F8321CEA2C7A00AFEA65 /* Example */,
				4686F81A1CEA2C4000AFEA65 /* Products */,
				3D34CC32B190B0D579E9B1E6 /* Pods */,
				C08A56159CB8E42DB26E607C /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		4686F81A1CEA2C4000AFEA65 /* Products */ = {
			isa = PBXGroup;
			children = (
				4686F8191CEA2C4000AFEA65 /* PhotoBrowser.framework */,
				4686F8311CEA2C7A00AFEA65 /* Example.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		4686F8321CEA2C7A00AFEA65 /* Example */ = {
			isa = PBXGroup;
			children = (
				4686F87F1CEA312B00AFEA65 /* Application */,
				4686F8771CEA30FA00AFEA65 /* LocalImagesExample */,
				4686F87A1CEA30FA00AFEA65 /* RemoteImagesExample */,
				4686F8621CEA30EF00AFEA65 /* Assets */,
				4686F83F1CEA2C7A00AFEA65 /* Assets.xcassets */,
			);
			path = Example;
			sourceTree = "<group>";
		};
		4686F8481CEA2EC200AFEA65 /* Sources */ = {
			isa = PBXGroup;
			children = (
				4686F8491CEA2EC200AFEA65 /* Info.plist */,
				4686F8511CEA2EC200AFEA65 /* PhotoBrowser.h */,
				4686F84C1CEA2EC200AFEA65 /* PBImageScrollView+internal.h */,
				4686F84D1CEA2EC200AFEA65 /* PBImageScrollView.h */,
				4686F84E1CEA2EC200AFEA65 /* PBImageScrollView.m */,
				4686F84A1CEA2EC200AFEA65 /* PBImageScrollViewController.h */,
				4686F84B1CEA2EC200AFEA65 /* PBImageScrollViewController.m */,
				4686F84F1CEA2EC200AFEA65 /* PBViewController.h */,
				4686F8501CEA2EC200AFEA65 /* PBViewController.m */,
				4686F8521CEA2EC200AFEA65 /* UIView+PBSnapshot.h */,
				4686F8531CEA2EC200AFEA65 /* UIView+PBSnapshot.m */,
				464FF3DB1CEB5B390060DD84 /* PBModalTransitionController.h */,
				F902516C20CBC14F00067CB8 /* PBModalTransitionController.m */,
			);
			path = Sources;
			sourceTree = "<group>";
		};
		4686F8621CEA30EF00AFEA65 /* Assets */ = {
			isa = PBXGroup;
			children = (
				464879261E6C0A11009BEF95 /* little_14.jpg */,
				464879271E6C0A11009BEF95 /* little_13.jpg */,
				464879281E6C0A11009BEF95 /* little_5.jpg */,
				464879291E6C0A11009BEF95 /* little_2.jpg */,
				4648790A1E6C099E009BEF95 /* little_1.jpg */,
				4648790C1E6C099E009BEF95 /* little_3.jpg */,
				4648790D1E6C099E009BEF95 /* little_4.jpg */,
				4648790F1E6C099E009BEF95 /* little_6.jpg */,
				46EA44C71EBDAACA00416710 /* 15.jpg */,
				46EA44C81EBDAACA00416710 /* little_15.jpg */,
				464879101E6C099E009BEF95 /* little_7.jpg */,
				464879111E6C099E009BEF95 /* little_8.jpg */,
				464879121E6C099E009BEF95 /* little_9.jpg */,
				464879131E6C099E009BEF95 /* little_10.jpg */,
				464879141E6C099E009BEF95 /* little_11.jpg */,
				464879151E6C099E009BEF95 /* little_12.jpg */,
				464879051E6B122D009BEF95 /* 14.jpg */,
				464879011E6B1099009BEF95 /* 12.jpg */,
				464879021E6B1099009BEF95 /* 13.jpg */,
				4689283E1E698A440067E5E0 /* 11.jpg */,
				4686F8631CEA30EF00AFEA65 /* 1.jpg */,
				4686F8651CEA30EF00AFEA65 /* 2.jpg */,
				464FF3EF1CED60F50060DD84 /* 3.jpg */,
				4686F8671CEA30EF00AFEA65 /* 4.jpg */,
				4686F8681CEA30EF00AFEA65 /* 5.jpg */,
				4686F8691CEA30EF00AFEA65 /* 6.jpg */,
				4686F86A1CEA30EF00AFEA65 /* 7.jpg */,
				464FF3F01CED60F50060DD84 /* 8.jpg */,
				464FF3F11CED60F50060DD84 /* 9.jpg */,
				4686F8641CEA30EF00AFEA65 /* 10.jpg */,
			);
			path = Assets;
			sourceTree = "<group>";
		};
		4686F8771CEA30FA00AFEA65 /* LocalImagesExample */ = {
			isa = PBXGroup;
			children = (
				4686F8781CEA30FA00AFEA65 /* LocalImagesExampleViewController.h */,
				4686F8791CEA30FA00AFEA65 /* LocalImagesExampleViewController.m */,
				F902516920CBB8ED00067CB8 /* PBImageView.h */,
				F902516A20CBB8ED00067CB8 /* PBImageView.m */,
			);
			path = LocalImagesExample;
			sourceTree = "<group>";
		};
		4686F87A1CEA30FA00AFEA65 /* RemoteImagesExample */ = {
			isa = PBXGroup;
			children = (
				4686F87B1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.h */,
				4686F87C1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.m */,
			);
			path = RemoteImagesExample;
			sourceTree = "<group>";
		};
		4686F87F1CEA312B00AFEA65 /* Application */ = {
			isa = PBXGroup;
			children = (
				4686F88A1CEA315200AFEA65 /* LaunchScreen.storyboard */,
				4686F88B1CEA315200AFEA65 /* Main.storyboard */,
				4686F8801CEA312B00AFEA65 /* AppDelegate.h */,
				4686F8811CEA312B00AFEA65 /* AppDelegate.m */,
				4686F8821CEA312B00AFEA65 /* Info.plist */,
				4686F8831CEA312B00AFEA65 /* main.m */,
				4686F8841CEA312B00AFEA65 /* RootViewController.h */,
				4686F8851CEA312B00AFEA65 /* RootViewController.m */,
				46FE959E1E67090D00D36D6E /* UIView+LayerImage.h */,
				46FE959F1E67090D00D36D6E /* UIView+LayerImage.m */,
			);
			path = Application;
			sourceTree = "<group>";
		};
		C08A56159CB8E42DB26E607C /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				0FDBFEF6AB6111BE128F7140 /* Pods_Example.framework */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
		4686F8161CEA2C4000AFEA65 /* Headers */ = {
			isa = PBXHeadersBuildPhase;
			buildActionMask = 2147483647;
			files = (
				4686F85C1CEA2EC200AFEA65 /* PhotoBrowser.h in Headers */,
				4686F85A1CEA2EC200AFEA65 /* PBViewController.h in Headers */,
				4686F8581CEA2EC200AFEA65 /* PBImageScrollView.h in Headers */,
				4686F8551CEA2EC200AFEA65 /* PBImageScrollViewController.h in Headers */,
				4686F8571CEA2EC200AFEA65 /* PBImageScrollView+internal.h in Headers */,
				4686F85D1CEA2EC200AFEA65 /* UIView+PBSnapshot.h in Headers */,
				464FF3DD1CEB5B390060DD84 /* PBModalTransitionController.h in Headers */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXHeadersBuildPhase section */

/* Begin PBXNativeTarget section */
		4686F8181CEA2C4000AFEA65 /* PhotoBrowser */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 4686F8211CEA2C4000AFEA65 /* Build configuration list for PBXNativeTarget "PhotoBrowser" */;
			buildPhases = (
				4686F8141CEA2C4000AFEA65 /* Sources */,
				4686F8151CEA2C4000AFEA65 /* Frameworks */,
				4686F8161CEA2C4000AFEA65 /* Headers */,
				4686F8171CEA2C4000AFEA65 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = PhotoBrowser;
			productName = PhotoBrowser;
			productReference = 4686F8191CEA2C4000AFEA65 /* PhotoBrowser.framework */;
			productType = "com.apple.product-type.framework";
		};
		4686F8301CEA2C7A00AFEA65 /* Example */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 4686F8451CEA2C7A00AFEA65 /* Build configuration list for PBXNativeTarget "Example" */;
			buildPhases = (
				39E6299993AFE1918518CDC2 /* Check Pods Manifest.lock */,
				4686F82D1CEA2C7A00AFEA65 /* Sources */,
				4686F82E1CEA2C7A00AFEA65 /* Frameworks */,
				4686F82F1CEA2C7A00AFEA65 /* Resources */,
				A852E9CF19A7F9D45F222DD1 /* Embed Pods Frameworks */,
				47B131481AFF66BA0673EE9A /* Copy Pods Resources */,
				4686F8921CEA329D00AFEA65 /* Embed Frameworks */,
			);
			buildRules = (
			);
			dependencies = (
				4686F8911CEA329D00AFEA65 /* PBXTargetDependency */,
			);
			name = Example;
			productName = Example;
			productReference = 4686F8311CEA2C7A00AFEA65 /* Example.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		4686F8101CEA2C4000AFEA65 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 0940;
				ORGANIZATIONNAME = Shaw;
				TargetAttributes = {
					4686F8181CEA2C4000AFEA65 = {
						CreatedOnToolsVersion = 7.3.1;
						DevelopmentTeam = XDN35K5RF2;
						ProvisioningStyle = Automatic;
					};
					4686F8301CEA2C7A00AFEA65 = {
						CreatedOnToolsVersion = 7.3.1;
						DevelopmentTeam = XDN35K5RF2;
						LastSwiftMigration = 0940;
						ProvisioningStyle = Automatic;
					};
				};
			};
			buildConfigurationList = 4686F8131CEA2C4000AFEA65 /* Build configuration list for PBXProject "PhotoBrowser" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = English;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 4686F80F1CEA2C4000AFEA65;
			productRefGroup = 4686F81A1CEA2C4000AFEA65 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				4686F8181CEA2C4000AFEA65 /* PhotoBrowser */,
				4686F8301CEA2C7A00AFEA65 /* Example */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		4686F8171CEA2C4000AFEA65 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		4686F82F1CEA2C7A00AFEA65 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				464FF3F31CED60F50060DD84 /* 8.jpg in Resources */,
				464FF3F21CED60F50060DD84 /* 3.jpg in Resources */,
				4648791B1E6C099E009BEF95 /* little_4.jpg in Resources */,
				4686F8741CEA30EF00AFEA65 /* 7.jpg in Resources */,
				4686F8711CEA30EF00AFEA65 /* 4.jpg in Resources */,
				4648792C1E6C0A11009BEF95 /* little_5.jpg in Resources */,
				464FF3F41CED60F50060DD84 /* 9.jpg in Resources */,
				4648792A1E6C0A11009BEF95 /* little_14.jpg in Resources */,
				4648792B1E6C0A11009BEF95 /* little_13.jpg in Resources */,
				464879031E6B1099009BEF95 /* 12.jpg in Resources */,
				4689283F1E698A440067E5E0 /* 11.jpg in Resources */,
				4648791D1E6C099E009BEF95 /* little_6.jpg in Resources */,
				4686F86F1CEA30EF00AFEA65 /* 2.jpg in Resources */,
				464879061E6B122D009BEF95 /* 14.jpg in Resources */,
				4686F86E1CEA30EF00AFEA65 /* 10.jpg in Resources */,
				4648792D1E6C0A11009BEF95 /* little_2.jpg in Resources */,
				46EA44CA1EBDAACA00416710 /* little_15.jpg in Resources */,
				464879041E6B1099009BEF95 /* 13.jpg in Resources */,
				464879201E6C099E009BEF95 /* little_9.jpg in Resources */,
				4648791F1E6C099E009BEF95 /* little_8.jpg in Resources */,
				4686F88D1CEA315200AFEA65 /* Main.storyboard in Resources */,
				4686F88C1CEA315200AFEA65 /* LaunchScreen.storyboard in Resources */,
				464879211E6C099E009BEF95 /* little_10.jpg in Resources */,
				4686F8721CEA30EF00AFEA65 /* 5.jpg in Resources */,
				4648791A1E6C099E009BEF95 /* little_3.jpg in Resources */,
				4686F8731CEA30EF00AFEA65 /* 6.jpg in Resources */,
				464879221E6C099E009BEF95 /* little_11.jpg in Resources */,
				464879231E6C099E009BEF95 /* little_12.jpg in Resources */,
				46EA44C91EBDAACA00416710 /* 15.jpg in Resources */,
				4686F86D1CEA30EF00AFEA65 /* 1.jpg in Resources */,
				4686F8401CEA2C7A00AFEA65 /* Assets.xcassets in Resources */,
				464879181E6C099E009BEF95 /* little_1.jpg in Resources */,
				4648791E1E6C099E009BEF95 /* little_7.jpg in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		39E6299993AFE1918518CDC2 /* Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "Check Pods Manifest.lock";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
			showEnvVarsInLog = 0;
		};
		47B131481AFF66BA0673EE9A /* Copy Pods Resources */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "Copy Pods Resources";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-resources.sh\"\n";
			showEnvVarsInLog = 0;
		};
		A852E9CF19A7F9D45F222DD1 /* Embed Pods Frameworks */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			name = "Embed Pods Frameworks";
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Example/Pods-Example-frameworks.sh\"\n";
			showEnvVarsInLog = 0;
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		4686F8141CEA2C4000AFEA65 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				4686F85E1CEA2EC200AFEA65 /* UIView+PBSnapshot.m in Sources */,
				4686F8561CEA2EC200AFEA65 /* PBImageScrollViewController.m in Sources */,
				4686F85B1CEA2EC200AFEA65 /* PBViewController.m in Sources */,
				F902516D20CBC15000067CB8 /* PBModalTransitionController.m in Sources */,
				4686F8591CEA2EC200AFEA65 /* PBImageScrollView.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		4686F82D1CEA2C7A00AFEA65 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				4686F87D1CEA30FA00AFEA65 /* LocalImagesExampleViewController.m in Sources */,
				4686F87E1CEA30FA00AFEA65 /* RemoteImagesExampleViewController.m in Sources */,
				46FE95A01E67090D00D36D6E /* UIView+LayerImage.m in Sources */,
				4686F8881CEA312B00AFEA65 /* main.m in Sources */,
				4686F8861CEA312B00AFEA65 /* AppDelegate.m in Sources */,
				4686F8891CEA312B00AFEA65 /* RootViewController.m in Sources */,
				F902516B20CBB8ED00067CB8 /* PBImageView.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		4686F8911CEA329D00AFEA65 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 4686F8181CEA2C4000AFEA65 /* PhotoBrowser */;
			targetProxy = 4686F8901CEA329D00AFEA65 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
		4686F81F1CEA2C4000AFEA65 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
			};
			name = Debug;
		};
		4686F8201CEA2C4000AFEA65 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
			};
			name = Release;
		};
		4686F8221CEA2C4000AFEA65 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_IDENTITY = "";
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
				DEFINES_MODULE = YES;
				DEVELOPMENT_TEAM = XDN35K5RF2;
				DYLIB_COMPATIBILITY_VERSION = 1;
				DYLIB_CURRENT_VERSION = 1;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				GCC_PREPROCESSOR_DEFINITIONS = (
					"$(inherited)",
					"DEBUG=1",
					"INPB=1",
				);
				INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = com.mochxiao.PhotoBrowser;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
			};
			name = Debug;
		};
		4686F8231CEA2C4000AFEA65 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CODE_SIGN_IDENTITY = "";
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
				DEFINES_MODULE = YES;
				DEVELOPMENT_TEAM = XDN35K5RF2;
				DYLIB_COMPATIBILITY_VERSION = 1;
				DYLIB_CURRENT_VERSION = 1;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = com.mochxiao.PhotoBrowser;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
			};
			name = Release;
		};
		4686F8461CEA2C7A00AFEA65 /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 69D32124846D71E6131AAB59 /* Pods-Example.debug.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_IDENTITY = "";
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				DEVELOPMENT_TEAM = XDN35K5RF2;
				INFOPLIST_FILE = Example/Application/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = com.mochxiao.PhotoBrowserExample;
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE = "";
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 3.0;
			};
			name = Debug;
		};
		4686F8471CEA2C7A00AFEA65 /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 50F46F9330F91C01C080BDC8 /* Pods-Example.release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CLANG_ENABLE_MODULES = YES;
				CODE_SIGN_IDENTITY = "";
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				DEVELOPMENT_TEAM = XDN35K5RF2;
				INFOPLIST_FILE = Example/Application/Info.plist;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = com.mochxiao.PhotoBrowserExample;
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE = "";
				PROVISIONING_PROFILE_SPECIFIER = "";
				SWIFT_VERSION = 3.0;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		4686F8131CEA2C4000AFEA65 /* Build configuration list for PBXProject "PhotoBrowser" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				4686F81F1CEA2C4000AFEA65 /* Debug */,
				4686F8201CEA2C4000AFEA65 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		4686F8211CEA2C4000AFEA65 /* Build configuration list for PBXNativeTarget "PhotoBrowser" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				4686F8221CEA2C4000AFEA65 /* Debug */,
				4686F8231CEA2C4000AFEA65 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		4686F8451CEA2C7A00AFEA65 /* Build configuration list for PBXNativeTarget "Example" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				4686F8461CEA2C7A00AFEA65 /* Debug */,
				4686F8471CEA2C7A00AFEA65 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 4686F8101CEA2C4000AFEA65 /* Project object */;
}


================================================
FILE: PhotoBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:PhotoBrowser.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: PhotoBrowser.xcodeproj/xcshareddata/xcschemes/PhotoBrowser.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "0940"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "4686F8181CEA2C4000AFEA65"
               BuildableName = "PhotoBrowser.framework"
               BlueprintName = "PhotoBrowser"
               ReferencedContainer = "container:PhotoBrowser.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
      <AdditionalOptions>
      </AdditionalOptions>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "4686F8181CEA2C4000AFEA65"
            BuildableName = "PhotoBrowser.framework"
            BlueprintName = "PhotoBrowser"
            ReferencedContainer = "container:PhotoBrowser.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <AdditionalOptions>
      </AdditionalOptions>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "4686F8181CEA2C4000AFEA65"
            BuildableName = "PhotoBrowser.framework"
            BlueprintName = "PhotoBrowser"
            ReferencedContainer = "container:PhotoBrowser.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: PhotoBrowser.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:PhotoBrowser.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: Podfile
================================================
source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'8.0'
inhibit_all_warnings!
use_frameworks!

target 'Example' do

pod 'SDWebImage', '~> 3.7'
pod 'Reveal-iOS-SDK', :configurations => ['Debug']

end


================================================
FILE: README.md
================================================
[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/cuzv/PhotoBrowser/blob/master/LICENSE)
[![CocoaPods Compatible](https://img.shields.io/badge/CocoaPods-v0.8.1-green.svg)](https://github.com/CocoaPods/CocoaPods)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Weibo](https://img.shields.io/badge/Weibo-cuzval-yellowgreen.svg)](https://weibo.com/cuzval/)
[![Twitter](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/cuzval)

# PhotoBrowser

PhotoBrowser is a light weight photo browser, like the wechat, weibo image viewer.

Now, the pure Swift version named [SlidingPhoto](https://github.com/cuzv/SlidingPhoto) is available.

### Features

-   [x] Present & Dismissal animation & gesture
-   [x] GIF support
-   [x] Display long picture

## How does it look like?

<p align="left">
<img src="./Preview/1.gif" width=240px">&nbsp;<img src="./Preview/2.gif" width=240px">&nbsp;<img src="./Preview/3.gif" width=240px">
</p>

## Usage

- Like the `UITableView` API, We have `DataSource` an `Delegate` for load data and handle action
- Tell `PhotoBrowser` how many pages would you like to present by conforms protocol `PBViewControllerDataSource` and implement `numberOfPagesInViewController:` selector
- Optional set the initialize page by `pb_startPage` property
- Use for static Image - Conforms protocol `PBViewControllerDataSource` and implement `viewController:imageForPageAtIndex:` selector
- Use for web image - Conforms protocol `PBViewControllerDataSource` and implement `viewController:presentImageView:forPageAtIndex:progressHandler` selector
- Support animation - Conforms protocol `PBViewControllerDataSource` and implement `thumbViewForPageAtIndex:` tell the start and ended imageView position
- Action callbacks - Conforms protocol `PBViewControllerDelegate` and implement `viewController:didSingleTapedPageAtIndex:presentedImage:` or `viewController:didLongPressedPageAtIndex:presentedImage:` handle single tap or long press action

## Demo code

``` objective-c
...
PBViewController *pbViewController = [PBViewController new];
// Use your own subclass of UIImageView to display.
pbViewController.imageViewClass = PBImageView.class;
pbViewController.pb_dataSource = self;
pbViewController.pb_delegate = self;
pbViewController.pb_startPage = sender.tag;
[self presentViewController:pbViewController animated:YES completion:nil];
...

...
#pragma mark - PBViewControllerDataSource

- (NSInteger)numberOfPagesInViewController:(PBViewController *)viewController {
    return self.frames.count;
}

- (void)viewController:(PBViewController *)viewController presentImageView:(UIImageView *)imageView forPageAtIndex:(NSInteger)index progressHandler:(void (^)(NSInteger, NSInteger))progressHandler {
    NSString *url = [NSString stringWithFormat:@"https://raw.githubusercontent.com/cuzv/PhotoBrowser/dev/Example/Assets/%@.jpg", @(index + 1)];
    UIImage *placeholder = self.imageViews[index].image;
    [imageView sd_setImageWithURL:[NSURL URLWithString:url]
                 placeholderImage:placeholder
                          options:0
                         progress:progressHandler
                        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                        }];
}

- (UIView *)thumbViewForPageAtIndex:(NSInteger)index {
    if (self.thumb) {
        return self.imageViews[index];
    }
    return nil;
}

#pragma mark - PBViewControllerDelegate

- (void)viewController:(PBViewController *)viewController didSingleTapedPageAtIndex:(NSInteger)index presentedImage:(UIImage *)presentedImage {
    [self dismissViewControllerAnimated:YES completion:nil];
}
```

For more information checkout the Example in project.

## License

`PhotoBrowser` is available under the MIT license. See the LICENSE file for more info.


================================================
FILE: Sources/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


================================================
FILE: Sources/PBImageScrollView+internal.h
================================================
//
//  PBImageScrollView+internal.h
//  PhotoBrowser
//
//  Created by Shaw on 5/13/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#ifndef PBImageScrollView_internal_h
#define PBImageScrollView_internal_h

#ifndef PBLog
#    if INPB
#       define PBLog(FORMAT, ...)    \
            do {    \
                fprintf(stderr,"<%s> %s %s [%d] %s\n",    \
                (NSThread.isMainThread ? "UI" : "BG"),    \
                (sel_getName(_cmd)),\
                [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],    \
                __LINE__,    \
                [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);    \
            } while(0)
#   else
#        define PBLog(FORMAT, ...)
#   endif
#endif

#import "PBImageScrollView.h"

@interface PBImageScrollView()

- (void)_handleZoomForLocation:(CGPoint)location;
- (void)_scrollToTopAnimated:(BOOL)animated;

/// Scrolling content offset'y percent.
@property (nonatomic, copy) void(^contentOffSetVerticalPercentHandler)(CGFloat);

/// loosen hand with decelerate
/// velocity: > 0 up, < 0 dwon, == 0 others(no swipe, e.g. tap).
@property (nonatomic, copy) void(^didEndDraggingInProperpositionHandler)(CGFloat velocity);

@end


#endif /* PBImageScrollView_internal_h */


================================================
FILE: Sources/PBImageScrollView.h
================================================
//
//  PBImageScrollView.h
//  PhotoBrowser
//
//  Created by Shaw on 5/12/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <UIKit/UIKit.h>

@interface PBImageScrollView : UIScrollView <UIScrollViewDelegate>

@property (nonatomic, strong, readonly) __kindof UIImageView *imageView;
/// Use for init your own `SubClassOfUIImageView`
@property (nonatomic, strong, nullable) Class imageViewClass;

@end


================================================
FILE: Sources/PBImageScrollView.m
================================================
//
//  PBImageScrollView.m
//  PhotoBrowser
//
//  Created by Shaw on 5/12/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import "PBImageScrollView.h"
#import "PBImageScrollView+internal.h"

#define system_version ([UIDevice currentDevice].systemVersion.doubleValue)
#define observe_keypath_image @"image"

@interface PBImageScrollView ()

@property (nonatomic, strong, readwrite) __kindof UIImageView *imageView;
@property (nonatomic, weak) id <NSObject> notification;
/// velocity: > 0 up, < 0 dwon, == 0 others(no swipe, e.g. tap).
@property (nonatomic, assign) CGFloat velocity;
@property (nonatomic, assign) BOOL dismissing;

@end

@implementation PBImageScrollView

- (void)dealloc {
    [self _removeNotificationIfNeeded];
    PBLog(@"%s", __FUNCTION__);
}

#pragma mark - respondsToSelector

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (!self) {
        return nil;
    }
    [self _setup];
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (!self) {
        return nil;
    }
    [self _setup];
    return self;
}

- (void)_setup {
#ifdef __IPHONE_11_0
    if (@available(iOS 11.0, *)) {
        self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
#endif
    self.frame = [UIScreen mainScreen].bounds;
    self.multipleTouchEnabled = YES;
    self.showsVerticalScrollIndicator = YES;
    self.showsHorizontalScrollIndicator = YES;
    self.alwaysBounceVertical = YES;
    self.alwaysBounceHorizontal = NO;
    self.minimumZoomScale = 1.0f;
    self.maximumZoomScale = 1.0f;
    self.delegate = self;
    [self _addNotificationIfNeeded];
}

- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
    [self _updateFrame];
    [self _recenterImage];
}

- (void)willMoveToWindow:(UIWindow *)newWindow {
    [super willMoveToWindow:newWindow];
    
    if (newWindow) {
        [self _addObserver];
    } else {
        [self _removeObserver];
    }
}

- (void)didMoveToWindow {
    [super didMoveToWindow];
    
    if (self.window) {
        [self _updateUserInterfaces];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if (![keyPath isEqualToString:observe_keypath_image]) {
        return;
    }
    if (![object isEqual:self.imageView]) {
        return;
    }
    if (!self.imageView.image) {
        return;
    }

    [self _updateUserInterfaces];
}

#pragma mark - Internal Methods

- (void)_handleZoomForLocation:(CGPoint)location {
    CGPoint touchPoint = [self.superview convertPoint:location toView:self.imageView];
    if (self.zoomScale > 1) {
        [self setZoomScale:1 animated:YES];
    } else if (self.maximumZoomScale > 1) {
        CGFloat newZoomScale = self.maximumZoomScale;
        CGFloat horizontalSize = CGRectGetWidth(self.bounds) / newZoomScale;
        CGFloat verticalSize = CGRectGetHeight(self.bounds) / newZoomScale;
        [self zoomToRect:CGRectMake(touchPoint.x - horizontalSize / 2.0f, touchPoint.y - verticalSize / 2.0f, horizontalSize, verticalSize) animated:YES];
    }
}

- (void)_scrollToTopAnimated:(BOOL)animated {
    CGPoint offset = self.contentOffset;
    offset.y = -self.contentInset.top;
    [self setContentOffset:offset animated:animated];
}

#pragma mark - Private methods

- (void)_addObserver {
    [self.imageView addObserver:self forKeyPath:observe_keypath_image options:NSKeyValueObservingOptionNew context:nil];
}

- (void)_removeObserver {
    [self.imageView removeObserver:self forKeyPath:observe_keypath_image];
}

- (void)_addNotificationIfNeeded {
    if (system_version >= 8.0) {
        return;
    }
    
    __weak typeof(self) weak_self = self;
    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _updateFrame];
        [strong_self _recenterImage];
    }];
}

- (void)_removeNotificationIfNeeded {
    if (system_version >= 8.0) {
        return;
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self.notification];
}

- (void)_updateUserInterfaces {
    [self setZoomScale:1.0f animated:YES];
    [self _updateFrame];
    [self _recenterImage];
    [self _setMaximumZoomScale];
}

- (void)_updateFrame {
    self.frame = [UIScreen mainScreen].bounds;
    
    __kindof UIImage *image = self.imageView.image;
    if (!image) {
        return;
    }
    
    CGSize properSize = [self _properPresentSizeForImage:image];
    self.imageView.frame = CGRectMake(0, 0, properSize.width, properSize.height);
    self.contentSize = properSize;
}

- (CGSize)_properPresentSizeForImage:(__kindof UIImage *)image {
    CGFloat ratio = CGRectGetWidth(self.bounds) / image.size.width;
    return CGSizeMake(CGRectGetWidth(self.bounds), ceil(ratio * image.size.height));
}

- (void)_recenterImage {
    CGFloat contentWidth = self.contentSize.width;
    CGFloat horizontalDiff = CGRectGetWidth(self.bounds) - contentWidth;
    CGFloat horizontalAddition = horizontalDiff > 0.f ? horizontalDiff : 0.f;
    
    CGFloat contentHeight = self.contentSize.height;
    CGFloat verticalDiff = CGRectGetHeight(self.bounds) - contentHeight;
    CGFloat verticalAdditon = verticalDiff > 0 ? verticalDiff : 0.f;

    self.imageView.center = CGPointMake((contentWidth + horizontalAddition) / 2.0f, (contentHeight + verticalAdditon) / 2.0f);
}

- (void)_setMaximumZoomScale {
    CGSize imageSize = self.imageView.image.size;
    CGFloat selfWidth = CGRectGetWidth(self.bounds);
    CGFloat selfHeight = CGRectGetHeight(self.bounds);
    if (imageSize.width <= selfWidth && imageSize.height <= selfHeight) {
        self.maximumZoomScale = 1.0f;
    } else {
        self.maximumZoomScale = MAX(MIN(imageSize.width / selfWidth, imageSize.height / selfHeight), 3.0f);
    }
}

/// Only + percent.
- (CGFloat)_contentOffSetVerticalPercent {
    return fabs([self _rawContentOffSetVerticalPercent]);
}

/// +/- percent.
- (CGFloat)_rawContentOffSetVerticalPercent {
    CGFloat percent = 0;
    
    CGFloat contentHeight = self.contentSize.height;
    CGFloat scrollViewHeight = CGRectGetHeight(self.bounds);
    CGFloat offsetY = self.contentOffset.y;
    CGFloat factor = 1.3;

    if (offsetY < 0) {
        percent = MIN(offsetY / (scrollViewHeight * factor), 1.0f);
    } else {
        if (contentHeight < scrollViewHeight) {
            percent = MIN(offsetY / (scrollViewHeight * factor), 1.0f);
        } else {
            offsetY += scrollViewHeight;
            CGFloat contentHeight = self.contentSize.height;
            if (offsetY > contentHeight) {
                percent = MIN((offsetY - contentHeight) / (scrollViewHeight * factor), 1.0f);
            }
        }
    }

    return percent;
}

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.dismissing) {
        return;
    }
    if (!self.contentOffSetVerticalPercentHandler) {
        return;
    }
    self.contentOffSetVerticalPercentHandler([self _contentOffSetVerticalPercent]);
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    [self _recenterImage];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        return;
    }
    
    // 停止时有加速度不够取消操作
    // 如果距离够,不取消
    CGFloat rawPercent = [self _rawContentOffSetVerticalPercent];
    CGFloat velocity = self.velocity;
    if (fabs(rawPercent) <= 0) {
        return;
    }
    PBLog(@"rawPercent: %@", @(rawPercent));
    PBLog(@"velocity: %@", @(velocity));
    if (fabs(rawPercent) < 0.15f && fabs(velocity) < 1) {
        return;
    }
    // 停止时有相反方向滑动操作时取消退出操作
    if (rawPercent * velocity < 0) {
        return;
    }
    // 如果是长图,并且是向上滑动,需要滑到底部以下才能结束
    if (self.contentSize.height > CGRectGetHeight(self.bounds)) {
        if (velocity > 0) {
            // 向上滑动
            // 速度过快且滑过区域较小,防止误操作,取消
            if (velocity > 2.8 && rawPercent < 0.1) {
                return;
            }
            if (self.contentOffset.y + CGRectGetHeight(self.bounds) <= self.contentSize.height) {
                return;
            }
        } else {
            // 向下滑动
            // 速度过快,防止误操作,取消
            if (fabs(velocity) > 2.8) {
                return;
            }
            if (self.contentOffset.y > 0) {
                return;
            }
        }
    }
    if (self.didEndDraggingInProperpositionHandler) {
        // 取消回弹效果,所以计算 imageView 的 frame 的时候需要注意 contentInset.
        scrollView.bounces = NO;
        scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
        self.didEndDraggingInProperpositionHandler(self.velocity);
        self.dismissing = YES;
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    self.velocity = velocity.y;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (self.dismissing) {
        return;
    }
    if (scrollView.zoomScale < 1) {
        [scrollView setZoomScale:1.0f animated:YES];
    }
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.imageView;
}

#pragma mark - Accessor

- (__kindof UIImageView *)imageView {
    if (!_imageView) {
        _imageView = self.imageViewClass ? [self.imageViewClass new] : [UIImageView new];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        _imageView.clipsToBounds = YES;
        [self addSubview:_imageView];
    }
    return _imageView;
}

@end


================================================
FILE: Sources/PBImageScrollViewController.h
================================================
//
//  PBImageScrollViewController.h
//  PhotoBrowser
//
//  Created by Shaw on 8/24/15.
//  Copyright © 2015 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <UIKit/UIKit.h>

@class PBImageScrollView;
typedef void(^PBImageDownloadProgressHandler)(NSInteger receivedSize, NSInteger expectedSize);

@interface PBImageScrollViewController : UIViewController

@property (nonatomic, assign) NSInteger page;

/// Return the image for current imageView
@property (nonatomic, copy) __kindof UIImage *(^fetchImageHandler)(void);
/// Configure image for current imageView
@property (nonatomic, copy) void (^configureImageViewHandler)(__kindof UIImageView *imageView);

/// Configure image for current imageView with progress
@property (nonatomic, copy) void (^configureImageViewWithDownloadProgressHandler)(__kindof UIImageView *imageView, PBImageDownloadProgressHandler handler);

@property (nonatomic, strong, readonly) PBImageScrollView *imageScrollView;

- (void)reloadData;

/// Use for init your own `SubClassOfUIImageView`
@property (nonatomic, strong, nullable) Class imageViewClass;

@end


================================================
FILE: Sources/PBImageScrollViewController.m
================================================
//
//  PBImageScrollViewController.m
//  PhotoBrowser
//
//  Created by Shaw on 8/24/15.
//  Copyright © 2015 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import "PBImageScrollViewController.h"
#import "PBImageScrollView.h"
#import "PBImageScrollView+internal.h"

@interface PBImageScrollViewController ()
@property (nonatomic, strong, readwrite) PBImageScrollView *imageScrollView;
@property (nonatomic, weak, readwrite) __kindof UIImageView *imageView;
@property (nonatomic, strong, readwrite) CAShapeLayer *progressLayer;
@property (nonatomic, assign) BOOL dismissing;
@end

@implementation PBImageScrollViewController

- (void)dealloc {
    PBLog(@"%s", __FUNCTION__);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.imageScrollView];
    [self.view.layer addSublayer:self.progressLayer];
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    
    CGPoint center = CGPointMake(CGRectGetWidth(self.view.bounds) / 2.0, CGRectGetHeight(self.view.bounds) / 2.0);
    CGRect frame = self.progressLayer.frame;
    frame.origin.x = center.x - CGRectGetWidth(frame) / 2.0f;
    frame.origin.y = center.y - CGRectGetHeight(frame) / 2.0f;
    self.progressLayer.frame = frame;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self reloadData];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    // 干掉加载动画。
    self.progressLayer.hidden = YES;
    self.dismissing = YES;
}

- (void)reloadData {
    [self _prepareForReuse];
    [self _loadData];
}

#pragma mark - Private methods

- (void)_prepareForReuse {
    self.imageView.image = nil;
    self.progressLayer.hidden = YES;
    self.progressLayer.strokeStart = 0;
    self.progressLayer.strokeEnd = 0;
    self.dismissing = NO;
}

- (void)_loadData {
    if (self.fetchImageHandler) {
        self.imageView.image = self.fetchImageHandler();
    } else if (self.configureImageViewWithDownloadProgressHandler) {
        __weak typeof(self) weak_self = self;
        self.configureImageViewWithDownloadProgressHandler(self.imageView, ^(NSInteger receivedSize, NSInteger expectedSize) {
            __strong typeof(weak_self) strong_self = weak_self;
            if (strong_self.dismissing || !strong_self.view.window) {
                strong_self.progressLayer.hidden = YES;
                return;
            }
            CGFloat progress = (receivedSize * 1.0f) / (expectedSize * 1.0f);
            if (0.0f >= progress || progress >= 1.0f) {
                strong_self.progressLayer.hidden = YES;
                return;
            }
            strong_self.progressLayer.hidden = NO;
            strong_self.progressLayer.strokeEnd = progress;
            
        });
    } else if (self.configureImageViewHandler) {
        self.configureImageViewHandler(self.imageView);
    }
}

#pragma mark - Accessor

- (PBImageScrollView *)imageScrollView {
    if (!_imageScrollView) {
        _imageScrollView = [PBImageScrollView new];
        _imageScrollView.imageViewClass = self.imageViewClass;
    }
    return _imageScrollView;
}

- (__kindof UIImageView *)imageView {
    return self.imageScrollView.imageView;
}

- (CAShapeLayer *)progressLayer {
    if (!_progressLayer) {
        _progressLayer = [CAShapeLayer layer];
        _progressLayer.frame = CGRectMake(0, 0, 40, 40);
        _progressLayer.cornerRadius = MIN(CGRectGetWidth(_progressLayer.bounds) / 2.0f, CGRectGetHeight(_progressLayer.bounds) / 2.0f);
        _progressLayer.lineWidth = 4;
        _progressLayer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5].CGColor;
        _progressLayer.fillColor = [UIColor clearColor].CGColor;
        _progressLayer.strokeColor = [UIColor whiteColor].CGColor;
        _progressLayer.lineCap = kCALineCapRound;
        _progressLayer.strokeStart = 0;
        _progressLayer.strokeEnd = 0;
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(_progressLayer.bounds, 7, 7) cornerRadius:_progressLayer.cornerRadius - 7];
        _progressLayer.path = path.CGPath;
        _progressLayer.hidden = YES;
    }
    return _progressLayer;
}

@end



================================================
FILE: Sources/PBModalTransitionController.h
================================================
//
//  PBPresentAnimatedTransitioningController.h
//  PhotoBrowser
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

typedef void(^PBContextBlock)(UIView * __nonnull fromView, UIView * __nonnull toView);

@interface PBModalTransitionController : NSObject <UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate>

@property (nonatomic, copy, nullable) PBContextBlock willPresent;
@property (nonatomic, copy, nullable) PBContextBlock inPresentation;
@property (nonatomic, copy, nullable) PBContextBlock didPresent;
@property (nonatomic, copy, nullable) PBContextBlock willDismiss;
@property (nonatomic, copy, nullable) PBContextBlock inDismissal;
@property (nonatomic, copy, nullable) PBContextBlock didDismiss;

/// Default cover is a dim view, you could override this property to your preferred style view.
@property (nonatomic, strong, nonnull) UIView *coverView;

@end


================================================
FILE: Sources/PBModalTransitionController.m
================================================
//
//  PBModalTransitionController.m
//  PhotoBrowser
//
//  Created by Shaw on 5/17/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import "PBModalTransitionController.h"

@interface PBModalTransitionController ()
@property (nonatomic, assign) BOOL isPresenting;
@end

@implementation PBModalTransitionController

#pragma mark - Private methods

- (UIViewAnimationOptions)_animationOptions {
    return 7 << 16;
}

- (void)_animateWithTransition:(nullable id <UIViewControllerContextTransitioning>)transitionContext
                    animations:(void (^)(void))animations
                    completion:(void (^)(BOOL flag))completion {
    // Prevent other interactions disturb.
    [[UIApplication sharedApplication] beginIgnoringInteractionEvents];
    [UIView animateWithDuration:[self transitionDuration:transitionContext]
                          delay:0
                        options:[self _animationOptions]
                     animations:animations
                     completion:^(BOOL finished) {
                         completion(finished);
                         [[UIApplication sharedApplication] endIgnoringInteractionEvents];
                     }];
}

- (void)_presentWithTransition:(id <UIViewControllerContextTransitioning>)transitionContext
                     container:(UIView *)container
                          from:(UIView *)fromView
                            to:(UIView *)toView
                    completion:(void (^)(BOOL flag))completion {
    self.coverView.frame = container.frame;
    self.coverView.alpha = 0;
    [container addSubview:self.coverView];
    toView.frame = container.bounds;
    [container addSubview:toView];
    
    if (self.willPresent) {
        self.willPresent(fromView, toView);
    }
    __weak typeof(self) weak_self = self;
    [self _animateWithTransition:transitionContext
                      animations:^{
                          __strong typeof(weak_self) strong_self = weak_self;
                          strong_self.coverView.alpha = 1;
                          if (strong_self.inPresentation) {
                              strong_self.inPresentation(fromView, toView);
                          }
                      }
                      completion:^(BOOL flag) {
                          __strong typeof(weak_self) strong_self = weak_self;
                          if (strong_self.didPresent) {
                              strong_self.didPresent(fromView, toView);
                          }
                          completion(flag);
                      }];
}

- (void)_dismissWithTransition:(id <UIViewControllerContextTransitioning>)transitionContext
                     container:(UIView *)container
                          from:(UIView *)fromView
                            to:(UIView *)toView
                    completion:(void (^)(BOOL flag))completion {
    [container addSubview:fromView];
    if (self.willDismiss) {
        self.willDismiss(fromView, toView);
    }
    __weak typeof(self) weak_self = self;
    [self _animateWithTransition:transitionContext
                      animations:^{
                          __strong typeof(weak_self) strong_self = weak_self;
                          strong_self.coverView.alpha = 0;
                          if (strong_self.inDismissal) {
                              strong_self.inDismissal(fromView, toView);
                          }
                      }
                      completion:^(BOOL flag) {
                          __strong typeof(weak_self) strong_self = weak_self;
                          if (strong_self.didDismiss) {
                              strong_self.didDismiss(fromView, toView);
                          }
                          completion(flag);
                      }];

}

- (nonnull PBModalTransitionController *)_prepareForPresent {
    self.isPresenting = YES;
    return self;
}

- (nonnull PBModalTransitionController *)_prepareForDismiss {
    self.isPresenting = NO;
    return self;
}

#pragma mark - UIViewControllerAnimatedTransitioning

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 0.25f;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    UIView *container = [transitionContext containerView];
    if (!container) {
        return;
    }
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    if (!fromController) {
        return;
    }
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    if (!toController) {
        return;
    }
    
    if (self.isPresenting) {
        [self _presentWithTransition:transitionContext
                           container:container
                                from:fromController.view
                                  to:toController.view
                          completion:^(BOOL flag) {
                              [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                          }];
    } else {
        [self _dismissWithTransition:transitionContext
                           container:container
                                from:fromController.view
                                  to:toController.view
                          completion:^(BOOL flag) {
                              [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
                          }];
    }
}

#pragma mark - UIViewControllerTransitioningDelegate

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented  presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    return [self _prepareForPresent];
}

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    return [self _prepareForDismiss];
}

#pragma mark - Accessor

- (UIView *)coverView {
    if (!_coverView) {
        _coverView = [UIView new];
        _coverView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
        _coverView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        _coverView.clipsToBounds = YES;
        _coverView.multipleTouchEnabled = NO;
        _coverView.userInteractionEnabled = NO;
    }
    return _coverView;
}

@end


================================================
FILE: Sources/PBViewController.h
================================================
//
//  PBViewController.h
//  PhotoBrowser
//
//  Created by Shaw on 8/24/15.
//  Copyright © 2015 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <UIKit/UIKit.h>

@class PBViewController;

#pragma mark - PBViewControllerDataSource

@protocol PBViewControllerDataSource <NSObject>

/// Return the pages count
- (NSInteger)numberOfPagesInViewController:(nonnull PBViewController *)viewController;

@optional

/// Return the image, implement one of this or follow method
- (nonnull __kindof UIImage *)viewController:(nonnull PBViewController *)viewController imageForPageAtIndex:(NSInteger)index;

/// Configure the imageView's image, implement one of this or upper method
- (void)viewController:(nonnull PBViewController *)viewController presentImageView:(nonnull __kindof UIImageView *)imageView forPageAtIndex:(NSInteger)index __attribute__((deprecated("use `viewController:presentImageView:forPageAtIndex:progressHandler` instead.")));
/// Configure the imageView's image, implement one of this or upper method
- (void)viewController:(nonnull PBViewController *)viewController presentImageView:(nonnull __kindof UIImageView *)imageView forPageAtIndex:(NSInteger)index progressHandler:(nullable void (^)(NSInteger receivedSize, NSInteger expectedSize))progressHandler;

/// Use for dismiss animation, will be an UIImageView in general.
- (nullable __kindof UIView *)thumbViewForPageAtIndex:(NSInteger)index;

@end

#pragma mark - PBViewControllerDelegate

@protocol PBViewControllerDelegate <NSObject>

@optional

/// Action call back for single tap, presentedImage will be nil untill loaded image
- (void)viewController:(nonnull PBViewController *)viewController didSingleTapedPageAtIndex:(NSInteger)index presentedImage:(nullable __kindof UIImage *)presentedImage;

/// Action call back for long press, presentedImage will be nil untill loaded image
- (void)viewController:(nonnull PBViewController *)viewController didLongPressedPageAtIndex:(NSInteger)index presentedImage:(nullable __kindof UIImage *)presentedImage;

@end


#pragma mark - PBViewController

@interface PBViewController : UIPageViewController

@property (nonatomic, weak, nullable) id<PBViewControllerDataSource> pb_dataSource;
@property (nonatomic, weak, nullable) id<PBViewControllerDelegate> pb_delegate;

@property (nonatomic, assign) NSInteger startPage;
@property (nonatomic, assign) NSInteger pb_startPage;
- (void)setInitializePageIndex:(NSInteger)pageIndex __attribute__((deprecated("use `pb_startPage` instead.")));

@property (nonatomic, assign, readonly) NSInteger numberOfPages;
@property (nonatomic, assign, readonly) NSInteger currentPage;
/// Will show first page.
- (void)reload;
/// Will show the specified page.
- (void)reloadWithCurrentPage:(NSInteger)index;
/// Default value is `YES`
@property (nonatomic, assign) BOOL blurBackground;
/// Default value is `YES`
@property (nonatomic, assign) BOOL hideThumb;
/// Custom exit method, if did not provide, use dismiss.
@property (nonatomic, copy, nullable) void (^exit)(PBViewController * _Nonnull sender);
/// Use for init your own `SubClassOfUIImageView`
@property (nonatomic, strong, nullable) Class imageViewClass;
@end


================================================
FILE: Sources/PBViewController.m
================================================
//
//  PBViewController.m
//  PhotoBrowser
//
//  Created by Shaw on 8/24/15.
//  Copyright © 2015 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import "PBViewController.h"
#import "PBImageScrollViewController.h"
#import "UIView+PBSnapshot.h"
#import "PBImageScrollView.h"
#import "PBImageScrollView+internal.h"
#import "PBModalTransitionController.h"

static const NSUInteger reusable_page_count = 3;

@interface PBViewController () <
    UIPageViewControllerDataSource,
    UIPageViewControllerDelegate
>

@property (nonatomic, strong) NSArray<PBImageScrollViewController *> *reusableImageScrollerViewControllers;
@property (nonatomic, assign, readwrite) NSInteger numberOfPages;
@property (nonatomic, assign, readwrite) NSInteger currentPage;

/// Images count >9, use this for indicate
@property (nonatomic, strong) UILabel *indicatorLabel;
/// Images count <=9, use this for indicate
@property (nonatomic, strong) UIPageControl *indicatorPageControl;
/// Hold the indicator control
@property (nonatomic, weak) UIView *indicator;
/// Blur background view
@property (nonatomic, strong) UIView *blurBackgroundView;

/// Gestures
@property (nonatomic, strong) UITapGestureRecognizer *singleTapGestureRecognizer;
@property (nonatomic, strong) UITapGestureRecognizer *doubleTapGestureRecognizer;
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGestureRecognizer;

@property (nonatomic, strong) PBModalTransitionController *transitioningController;
@property (nonatomic, assign) CGFloat velocity;

@property (nonatomic, assign) CGRect contentsRect;
@property (nonatomic, assign) CGRect originFrame;
@property (nonatomic, weak) UIView *lastThumbView;

@end

@implementation PBViewController

- (void)dealloc {
    PBLog(@"%s", __FUNCTION__);
}

#pragma mark - respondsToSelector

- (instancetype)initWithTransitionStyle:(UIPageViewControllerTransitionStyle)style
                  navigationOrientation:(UIPageViewControllerNavigationOrientation)navigationOrientation
                                options:(NSDictionary *)options {
    NSMutableDictionary *dict = [(options ?: @{}) mutableCopy];
    [dict setObject:@(20) forKey:UIPageViewControllerOptionInterPageSpacingKey];
    self = [super initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
                    navigationOrientation:navigationOrientation
                                  options:dict];
    if (!self) {
        return nil;
    }
    
    self.modalPresentationStyle = UIModalPresentationCustom;
    self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    self.transitioningDelegate = self.transitioningController;
    _contentsRect = CGRectMake(0, 0, 1, 1);
    _blurBackground = YES;
    _hideThumb = YES;
    _imageViewClass = UIImageView.class;
    
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set numberOfPages
    [self _setNumberOfPages];
    // Set visible view controllers
    [self _setCurrentPresentPageAnimated: NO];
    // Set indicatorLabel
    [self _addIndicator];
    // Blur background
    [self _addBlurBackgroundView];
    
    [self.view addGestureRecognizer:self.longPressGestureRecognizer];
    [self.view addGestureRecognizer:self.doubleTapGestureRecognizer];
    [self.view addGestureRecognizer:self.singleTapGestureRecognizer];
    [self.singleTapGestureRecognizer requireGestureRecognizerToFail:self.longPressGestureRecognizer];
    [self.singleTapGestureRecognizer requireGestureRecognizerToFail:self.doubleTapGestureRecognizer];
    
    self.dataSource = self;
    self.delegate = self;
    
    [self _setupTransitioningController];
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    [self _updateIndicator];
    [self _updateBlurBackgroundView];
}

#pragma mark - Public method

- (void)setInitializePageIndex:(NSInteger)pageIndex {
    self.pb_startPage = pageIndex;
}

- (void)setPb_startPage:(NSInteger)pb_startPage {
    _startPage = pb_startPage;
    _pb_startPage = pb_startPage;
    _currentPage = pb_startPage;
}

- (void)setStartPage:(NSInteger)startPage {
    self.pb_startPage = startPage;
}

- (void)reload {
    [self reloadWithCurrentPage:0];
}

- (void)reloadWithCurrentPage:(NSInteger)index {
    self.pb_startPage = index;
    [self _setNumberOfPages];
    NSAssert(index < _numberOfPages, @"index(%@) beyond boundary.", @(index));
    [self _setCurrentPresentPageAnimated: YES];
    [self _updateIndicator];
    [self _updateBlurBackgroundView];
    [self _hideThumbView];
}

#pragma mark - Private methods

- (void)_setNumberOfPages {
    if ([self.pb_dataSource conformsToProtocol:@protocol(PBViewControllerDataSource)] &&
        [self.pb_dataSource respondsToSelector:@selector(numberOfPagesInViewController:)]) {
        self.numberOfPages = [self.pb_dataSource numberOfPagesInViewController:self];
    }
}

- (void)_setCurrentPresentPageAnimated:(BOOL)animated {
    self.currentPage = 0 < self.currentPage && self.currentPage < self.numberOfPages ? self.currentPage : 0;
    PBImageScrollViewController *firstImageScrollerViewController = [self _imageScrollerViewControllerForPage:self.currentPage];
    [self setViewControllers:@[firstImageScrollerViewController] direction:UIPageViewControllerNavigationDirectionForward animated:animated completion:nil];
    [firstImageScrollerViewController reloadData];
}

- (void)_addIndicator {
    if (self.numberOfPages == 1) {
        return;
    }
    if (self.numberOfPages <= 9) {
        [self.view addSubview:self.indicatorPageControl];
        self.indicator = self.indicatorPageControl;
    } else {
        [self.view addSubview:self.indicatorLabel];
        self.indicator = self.indicatorLabel;
    }
    self.indicator.layer.zPosition = 1024;
}

- (void)_updateIndicator {
    if (!self.indicator) {
        return;
    }
    if (self.numberOfPages <= 9) {
        self.indicatorPageControl.numberOfPages = self.numberOfPages;
        self.indicatorPageControl.currentPage = self.currentPage;
        [self.indicatorPageControl sizeToFit];
        self.indicatorPageControl.center = CGPointMake(CGRectGetWidth(self.view.bounds) / 2.0f,
                                                       CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.indicatorPageControl.bounds) / 2.0f);
    } else {
        NSString *indicatorText = [NSString stringWithFormat:@"%@/%@", @(self.currentPage + 1), @(self.numberOfPages)];
        self.indicatorLabel.text = indicatorText;
        [self.indicatorLabel sizeToFit];
        self.indicatorLabel.center = CGPointMake(CGRectGetWidth(self.view.bounds) / 2.0f,
                                                 CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.indicatorLabel.bounds));
    }
}

- (void)_addBlurBackgroundView {
    [self.view addSubview:self.blurBackgroundView];
    [self.view sendSubviewToBack:self.blurBackgroundView];
}

- (void)_updateBlurBackgroundView {
    self.blurBackgroundView.frame = self.view.bounds;
}

- (void)_hideStatusBarIfNeeded {
    self.presentingViewController.view.window.windowLevel = UIWindowLevelStatusBar;
}

- (void)_showStatusBarIfNeeded {
    self.presentingViewController.view.window.windowLevel = UIWindowLevelNormal;
}

- (PBImageScrollViewController *)_imageScrollerViewControllerForPage:(NSInteger)page {
    if (page > self.numberOfPages - 1 || page < 0) {
        return nil;
    }
    
    // Get the reusable `PBImageScrollerViewController`
    PBImageScrollViewController *imageScrollerViewController = self.reusableImageScrollerViewControllers[page % reusable_page_count];
    
    // Set new data
    if (!self.pb_dataSource) {
        [NSException raise:@"Must implement `PBViewControllerDataSource` protocol." format:@""];
    }
    
    __weak typeof(self) weak_self = self;
    if ([self.pb_dataSource conformsToProtocol:@protocol(PBViewControllerDataSource)]) {
        imageScrollerViewController.page = page;
        
        if ([self.pb_dataSource respondsToSelector:@selector(viewController:imageForPageAtIndex:)]) {
            imageScrollerViewController.fetchImageHandler = ^ __kindof UIImage *(void) {
                __strong typeof(weak_self) strong_self = weak_self;
                if (page < strong_self.numberOfPages) {
                    return [strong_self.pb_dataSource viewController:strong_self imageForPageAtIndex:page];
                }
                return nil;
            };
        } else if ([self.pb_dataSource respondsToSelector:@selector(viewController:presentImageView:forPageAtIndex:progressHandler:)]) {
            imageScrollerViewController.configureImageViewWithDownloadProgressHandler = ^(__kindof UIImageView *imageView, PBImageDownloadProgressHandler handler) {
                __strong typeof(weak_self) strong_self = weak_self;
                if (page < strong_self.numberOfPages) {
                    [strong_self.pb_dataSource viewController:strong_self presentImageView:imageView forPageAtIndex:page progressHandler:handler];
                }
            };
        } else if ([self.pb_dataSource respondsToSelector:@selector(viewController:presentImageView:forPageAtIndex:)]) {
            imageScrollerViewController.configureImageViewHandler = ^(__kindof UIImageView *imageView) {
                __strong typeof(weak_self) strong_self = weak_self;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
                if (page < strong_self.numberOfPages) {
                    [strong_self.pb_dataSource viewController:strong_self presentImageView:imageView forPageAtIndex:page];
                }
#pragma clang diagnostic pop
            };
        }
    }
    
    return imageScrollerViewController;
}

- (void)_setupTransitioningController {
    __weak typeof(self) weak_self = self;
    self.transitioningController.willPresent = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _willPresent];
    };
    self.transitioningController.inPresentation = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _onPresent];
    };
    self.transitioningController.didPresent = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _didPresented];
    };
    self.transitioningController.willDismiss = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _willDismiss];
    };
    self.transitioningController.inDismissal = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _onDismiss];
    };
    self.transitioningController.didDismiss = ^(UIView *fromView, UIView *toView) {
        __strong typeof(weak_self) strong_self = weak_self;
        [strong_self _didDismiss];
    };
}

- (void)_willPresent {
    PBImageScrollViewController *currentScrollViewController = self.currentScrollViewController;
    currentScrollViewController.view.alpha = 0;
    self.blurBackgroundView.alpha = 0;
    UIView *thumbView = self.currentThumbView;
    if (!thumbView) {
        return;
    }
    [self _hideThumbView];

    currentScrollViewController.view.alpha = 1;
    PBImageScrollView *imageScrollView = currentScrollViewController.imageScrollView;
    __kindof UIImageView *imageView = imageScrollView.imageView;
    imageView.image = self.currentThumbImage;
    __kindof UIImage *image = imageView.image;

    // 长图
    if (self.thumbClippedToTop) {
        CGRect fromFrame = [thumbView.superview convertRect:thumbView.frame toView:self.view];
        CGRect originFrame = [imageView.superview convertRect:imageView.frame toView:self.view];
        // 长微博长图只取屏幕高度
        if (CGRectGetHeight(originFrame) > CGRectGetHeight(imageScrollView.bounds)) {
            originFrame.size.height = CGRectGetHeight(imageScrollView.bounds);
            
            CGFloat scale = CGRectGetWidth(fromFrame) / CGRectGetWidth(imageScrollView.bounds);
            // centerX
            imageScrollView.center = CGPointMake(CGRectGetMidX(fromFrame), CGRectGetMidY(imageScrollView.frame));
            // height
            CGRect newFrame = imageScrollView.frame;
            newFrame.size.height = CGRectGetHeight(fromFrame) / scale;
            imageScrollView.frame = newFrame;
            // layer animation
            [imageScrollView.layer setValue:@(scale) forKeyPath:@"transform.scale"];
            // centerY
            imageScrollView.center = CGPointMake(CGRectGetMidX(imageScrollView.frame), CGRectGetMidY(fromFrame));
        }
        // 长图但是长度不超过屏幕
        else {
            imageView.frame = fromFrame;
            CGFloat heightRatio = (image.size.width / image.size.height) * (CGRectGetHeight(imageView.bounds) / CGRectGetWidth(imageView.bounds));
            imageView.layer.contentsRect = CGRectMake(0, 0, 1, heightRatio);
            imageView.contentMode = UIViewContentModeScaleToFill;
        }
        
        // record
        self.originFrame = originFrame;
    }
    // 宽图 or 等比例
    else {
        // record
        self.originFrame = imageView.frame;
        CGRect frame = [thumbView.superview convertRect:thumbView.frame toView:self.view];
        imageView.frame = frame;
        imageView.contentMode = thumbView.contentMode;
        imageView.clipsToBounds = thumbView.clipsToBounds;
        imageView.backgroundColor = thumbView.backgroundColor;
    }
}

- (void)_onPresent {
    PBImageScrollViewController *currentScrollViewController = self.currentScrollViewController;
    self.blurBackgroundView.alpha = 1;
    [self _hideStatusBarIfNeeded];
    
    if (!self.currentThumbView) {
        currentScrollViewController.view.alpha = 1;
        return;
    }
    
    PBImageScrollView *imageScrollView = currentScrollViewController.imageScrollView;
    __kindof UIImageView *imageView = imageScrollView.imageView;
    CGRect originFrame = [imageView.superview convertRect:imageView.frame toView:self.view];
    
    if (CGRectEqualToRect(originFrame, CGRectZero)) {
        currentScrollViewController.view.alpha = 1;
        return;
    }

    // 长图
    if (self.thumbClippedToTop) {
        // 长微博长图
        if (CGRectGetHeight(self.originFrame) > CGRectGetHeight(imageScrollView.bounds)) {
            imageScrollView.frame = self.originFrame;
            [imageScrollView.layer setValue:@(1) forKeyPath:@"transform.scale"];
        }
        // 长图但是长度不超过屏幕
        else {
            imageView.frame = self.originFrame;
            imageView.layer.contentsRect = CGRectMake(0, 0, 1, 1);
        }
    }
    // 宽图 or 等比例
    else {
        imageView.frame = self.originFrame;
    }
}

- (void)_didPresented {
    self.currentScrollViewController.view.alpha = 1;
    self.currentScrollViewController.imageScrollView.imageView.contentMode = UIViewContentModeScaleAspectFill;
    [self.currentScrollViewController reloadData];
    [self _hideIndicator];
}

/// pic  :    正方形 | 长方形(w>h) | 长方形(h>w)
/// view :    正方形 | 长方形(w>h) | 长方形(h>w)
/// 3 * 3 = 9 种情况
- (void)_willDismiss {
    PBImageScrollViewController *currentScrollViewController = self.currentScrollViewController;
    PBImageScrollView *imageScrollView = currentScrollViewController.imageScrollView;
    // 还原 zoom.
    if (imageScrollView.zoomScale != 1) {
        [imageScrollView setZoomScale:1 animated:NO];
    }
    
    // 停止播放动画
    NSArray<UIImage *> *images = imageScrollView.imageView.image.images;
    if (images && images.count > 1) {
        UIImage *newImage = images.firstObject;
        imageScrollView.imageView.image = nil;
        imageScrollView.imageView.image = newImage;
    }
    
    // 有 thumbView
    if (self.currentThumbView) {
        // 裁剪过图片
        if (self.thumbClippedToTop) {
            // 记录 contentsRect
            __kindof UIImage *image = imageScrollView.imageView.image;
            CGFloat heightRatio = (image.size.width / image.size.height) * (CGRectGetHeight(self.currentThumbView.bounds) / CGRectGetWidth(self.currentThumbView.bounds));
            self.contentsRect = CGRectMake(0, 0, 1, heightRatio);
            
            // 图片长度超过屏幕(长微博形式),为裁剪动画做准备
            if (imageScrollView.contentSize.height > CGRectGetHeight(imageScrollView.bounds)) {
                [CATransaction begin];
                [CATransaction setDisableActions:YES];
                CGRect frame = imageScrollView.imageView.frame;
                imageScrollView.imageView.layer.anchorPoint = CGPointMake(0.5, self.isPullup ? 1 : 0);
                imageScrollView.imageView.frame = frame;
                [CATransaction commit];
            }
        }
        // 点击推出,需要先回到顶部
        if (self.dismissByClick) {
            [imageScrollView _scrollToTopAnimated:NO];
        }
    }
    // 无 thumbView
    else {
        // 点击退出模式,截取当前屏幕并替换图片
        if (self.dismissByClick) {
            __kindof UIImage *image = [self.view pb_snapshotAfterScreenUpdates:NO];
            imageScrollView.imageView.image = image;
        }
    }
}

- (void)_onDismiss {
    [self _showStatusBarIfNeeded];
    self.blurBackgroundView.alpha = 0;
    
    PBImageScrollViewController *currentScrollViewController = self.currentScrollViewController;
    PBImageScrollView *imageScrollView = currentScrollViewController.imageScrollView;
    __kindof UIImageView *imageView = imageScrollView.imageView;
    __kindof UIImage *currentImage = imageView.image;
    // 图片未加载,默认 CrossDissolve 动画。
    if (!currentImage) {
        return;
    }
    
    // present 之前显示的图片视图。
    UIView *thumbView = self.currentThumbView;
    CGRect destFrame = CGRectZero;
    if (thumbView) {
        // 还原到起始位置然后 dismiss.
        destFrame = [thumbView.superview convertRect:thumbView.frame toView:currentScrollViewController.view];
        // 把 contentInset 考虑进来。
        CGFloat verticalInset = imageScrollView.contentInset.top + imageScrollView.contentInset.bottom;
        destFrame = CGRectMake(
           CGRectGetMinX(destFrame),
           CGRectGetMinY(destFrame) - verticalInset,
           CGRectGetWidth(destFrame),
           CGRectGetHeight(destFrame)
       );
        
        // 同步裁剪图片位置
        imageView.layer.contentsRect = self.contentsRect;
        
        // 裁剪过图片的长微博
        if (self.thumbClippedToTop && imageScrollView.contentSize.height > CGRectGetHeight(imageScrollView.bounds)) {
            CGFloat height = CGRectGetHeight(thumbView.bounds) / CGRectGetWidth(thumbView.bounds) * CGRectGetWidth(imageView.bounds);
            if (isnan(height)) {
                height = CGRectGetWidth(imageView.bounds);
            }
            
            CGRect newFrame = imageView.frame;
            newFrame.size.height = height;
            imageView.frame = newFrame;
            imageView.center = CGPointMake(CGRectGetMidX(destFrame), CGRectGetMinY(destFrame) + (self.isPullup ? CGRectGetHeight(thumbView.bounds) : 0));

            CGFloat scale = CGRectGetWidth(thumbView.bounds) / CGRectGetWidth(imageView.bounds) * imageScrollView.zoomScale;
            [imageView.layer setValue:@(scale) forKeyPath:@"transform.scale"];
        } else {
            imageView.frame = destFrame;
        }
    } else {
        if (self.dismissByClick) {
            // 非滑动退出,点击中间
            destFrame = CGRectMake(CGRectGetWidth(imageScrollView.bounds) / 2, CGRectGetHeight(imageScrollView.bounds) / 2, 0, 0);
            // 图片渐变
            imageScrollView.alpha = 0;
        } else {
            // 移动到屏幕外然后 dismiss.
            CGFloat width = CGRectGetWidth(imageScrollView.imageView.bounds);
            CGFloat height = CGRectGetHeight(imageScrollView.imageView.bounds);
            if (self.isPullup) {
                // 向上
                destFrame = CGRectMake(0, -height, width, height);
            } else {
                // 向下
                destFrame = CGRectMake(0, CGRectGetHeight(imageScrollView.bounds), width, height);
            }
        }
        imageView.frame = destFrame;
    }
}

- (void)_didDismiss {
    self.currentScrollViewController.imageScrollView.imageView.layer.anchorPoint = CGPointMake(0.5, 0);
    self.currentThumbView.hidden = NO;
}

- (void)_hideIndicator {
    if (!self.indicator || 0 == self.indicator.alpha) {
        return;
    }
    [UIView animateWithDuration:0.25 delay:0.5 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:^{
        self.indicator.alpha = 0;
    } completion:^(BOOL finished) {
    }];
}

- (void)_showIndicator {
    if (!self.indicator || 1 == self.indicator.alpha) {
        return;
    }
    [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseOut animations:^{
        self.indicator.alpha = 1;
    } completion:^(BOOL finished) {
    }];
}

- (void)_hideThumbView {
    if (!_hideThumb) {
        return;
    }
    PBLog(@"%s", __FUNCTION__);
    self.lastThumbView.hidden = NO;
    UIView *currentThumbView = self.currentThumbView;
    currentThumbView.hidden = YES;
    self.lastThumbView = currentThumbView;
}

#pragma mark - Actions

- (void)_handleSingleTapAction:(UITapGestureRecognizer *)sender {
    if (sender.state != UIGestureRecognizerStateEnded) {
        return;
    }
    if (!self.pb_delegate) {
        return;
    }
    if ([self.pb_delegate conformsToProtocol:@protocol(PBViewControllerDelegate)]) {
        if ([self.pb_delegate respondsToSelector:@selector(viewController:didSingleTapedPageAtIndex:presentedImage:)]) {
            [self.pb_delegate viewController:self didSingleTapedPageAtIndex:self.currentPage presentedImage:self.currentScrollViewController.imageScrollView.imageView.image];
        }
    }
}

- (void)_handleDoubleTapAction:(UITapGestureRecognizer *)sender {
    if (sender.state != UIGestureRecognizerStateEnded) {
        return;
    }
    CGPoint location = [sender locationInView:self.view];
    PBImageScrollView *imageScrollView = self.currentScrollViewController.imageScrollView;
    [imageScrollView _handleZoomForLocation:location];
}

- (void)_handleLongPressAction:(UILongPressGestureRecognizer *)sender {
    if (sender.state != UIGestureRecognizerStateEnded) {
        return;
    }
    if (!self.pb_delegate) {
        return;
    }
    if ([self.pb_delegate conformsToProtocol:@protocol(PBViewControllerDelegate)]) {
        if ([self.pb_delegate respondsToSelector:@selector(viewController:didLongPressedPageAtIndex:presentedImage:)]) {
            [self.pb_delegate viewController:self didLongPressedPageAtIndex:self.currentPage presentedImage:self.currentScrollViewController.imageScrollView.imageView.image];
        }
    }
}

#pragma mark - UIPageViewControllerDataSource

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(PBImageScrollViewController *)viewController {
    return [self _imageScrollerViewControllerForPage:viewController.page - 1];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(PBImageScrollViewController *)viewController {
    return [self _imageScrollerViewControllerForPage:viewController.page + 1];
}

#pragma mark - UIPageViewControllerDelegate

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray<UIViewController *> *)pendingViewControllers {
    [self _showIndicator];
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
    PBImageScrollViewController *imageScrollerViewController = pageViewController.viewControllers.firstObject;
    self.currentPage = imageScrollerViewController.page;
    [self _updateIndicator];
    [self _hideIndicator];
    [self _hideThumbView];
}

#pragma mark - Accessor

- (NSArray<PBImageScrollViewController *> *)reusableImageScrollerViewControllers {
    if (!_reusableImageScrollerViewControllers) {
        NSMutableArray *controllers = [[NSMutableArray alloc] initWithCapacity:reusable_page_count];
        for (NSInteger index = 0; index < reusable_page_count; index++) {
            PBImageScrollViewController *imageScrollerViewController = [PBImageScrollViewController new];
            imageScrollerViewController.imageViewClass = self.imageViewClass;
            imageScrollerViewController.page = index;
            __weak typeof(self) weak_self = self;
            imageScrollerViewController.imageScrollView.contentOffSetVerticalPercentHandler = ^(CGFloat percent) {
                __strong typeof(weak_self) strong_self = weak_self;
                CGFloat alpha = 1.0f - percent * 4;
                if (alpha < 0) {
                    alpha = 0;
                }
                strong_self.blurBackgroundView.alpha = alpha;
            };
            imageScrollerViewController.imageScrollView.didEndDraggingInProperpositionHandler = ^(CGFloat velocity){
                __strong typeof(weak_self) strong_self = weak_self;
                strong_self.velocity = velocity;
                if (strong_self.exit) {
                    strong_self.exit(strong_self);
                } else {
                    [strong_self dismissViewControllerAnimated:YES completion:nil];
                }
            };
            [controllers addObject:imageScrollerViewController];
        }
        _reusableImageScrollerViewControllers = [[NSArray alloc] initWithArray:controllers];
    }
    return _reusableImageScrollerViewControllers;
}

- (UILabel *)indicatorLabel {
    if (!_indicatorLabel) {
        _indicatorLabel = [UILabel new];
        _indicatorLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
        _indicatorLabel.textAlignment = NSTextAlignmentCenter;
        _indicatorLabel.textColor = [UIColor whiteColor];
    }
    return _indicatorLabel;
}

- (UIPageControl *)indicatorPageControl {
    if (!_indicatorPageControl) {
        _indicatorPageControl = [UIPageControl new];
        _indicatorPageControl.numberOfPages = self.numberOfPages;
        _indicatorPageControl.currentPage = self.currentPage;
    }
    return _indicatorPageControl;
}

- (UIView *)blurBackgroundView {
    if (self.blurBackground) {
        if (!_blurBackgroundView) {
            _blurBackgroundView = [[UIToolbar alloc] initWithFrame:self.view.bounds];
            ((UIToolbar *)_blurBackgroundView).barStyle = UIBarStyleBlack;
            ((UIToolbar *)_blurBackgroundView).translucent = YES;
            _blurBackgroundView.clipsToBounds = YES;
            _blurBackgroundView.multipleTouchEnabled = NO;
            _blurBackgroundView.userInteractionEnabled = NO;
        }
    } else {
        if (!_blurBackgroundView) {
            _blurBackgroundView = [[UIView alloc] initWithFrame:self.view.bounds];
            _blurBackgroundView.backgroundColor = [UIColor blackColor];
            _blurBackgroundView.clipsToBounds = YES;
            _blurBackgroundView.multipleTouchEnabled = NO;
            _blurBackgroundView.userInteractionEnabled = NO;
        }
    }
    return _blurBackgroundView;
}

- (UITapGestureRecognizer *)singleTapGestureRecognizer {
    if (!_singleTapGestureRecognizer) {
        _singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_handleSingleTapAction:)];
    }
    return _singleTapGestureRecognizer;
}

- (UITapGestureRecognizer *)doubleTapGestureRecognizer {
    if (!_doubleTapGestureRecognizer) {
        _doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_handleDoubleTapAction:)];
        _doubleTapGestureRecognizer.numberOfTapsRequired = 2;
    }
    return _doubleTapGestureRecognizer;
}

- (UILongPressGestureRecognizer *)longPressGestureRecognizer {
    if (!_longPressGestureRecognizer) {
        _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_handleLongPressAction:)];
    }
    return _longPressGestureRecognizer;
}

- (PBImageScrollViewController *)currentScrollViewController {
    return self.reusableImageScrollerViewControllers[self.currentPage % reusable_page_count];
}

- (UIView *)currentThumbView {
    if (!self.pb_dataSource) {
        return nil;
    }
    if (![self.pb_dataSource conformsToProtocol:@protocol(PBViewControllerDataSource)]) {
        return nil;
    }
    if (![self.pb_dataSource respondsToSelector:@selector(thumbViewForPageAtIndex:)]) {
        return  nil;
    }
    return [self.pb_dataSource thumbViewForPageAtIndex:self.currentPage];
}

- (__kindof UIImage *)currentThumbImage {
    UIView *currentThumbView = self.currentThumbView;
    if (!currentThumbView) {
        return nil;
    }
    if ([currentThumbView isKindOfClass:[UIImageView class]]) {
        return ((__kindof UIImageView *)self.currentThumbView).image;
    }
    if (currentThumbView.layer.contents) {
        return [[UIImage alloc] initWithCGImage:(__bridge CGImageRef _Nonnull)(currentThumbView.layer.contents)];
    }
    return nil;
}

- (BOOL)thumbClippedToTop {
    UIView *currentThumbView = self.currentThumbView;
    if (!currentThumbView) {
        return NO;
    }
    return currentThumbView.layer.contentsRect.size.height < 1;
}

- (BOOL)dismissByClick {
    if (0 != self.velocity) {
        return NO;
    }
    PBImageScrollView *imageScrollView = self.currentScrollViewController.imageScrollView;
    if (imageScrollView.contentOffset.y < 0) {
        return NO;
    }
    if (imageScrollView.contentInset.top < 0) {
        return NO;
    }
    
    return YES;
}

- (BOOL)isPullup {
    return 0 < self.velocity;
}

- (PBModalTransitionController *)transitioningController {
    if (!_transitioningController) {
        _transitioningController = [PBModalTransitionController new];
    }
    return _transitioningController;
}

@end


================================================
FILE: Sources/PhotoBrowser.h
================================================
//
//  PhotoBrowser.h
//  PhotoBrowser
//
//  Created by Shaw on 8/24/15.
//  Copyright © 2015 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <UIKit/UIKit.h>

//! Project version number for PhotoBrowser.
FOUNDATION_EXPORT double PhotoBrowserVersionNumber;

//! Project version string for PhotoBrowser.
FOUNDATION_EXPORT const unsigned char PhotoBrowserVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <PhotoBrowser/PublicHeader.h>

#import <PhotoBrowser/PBViewController.h>



================================================
FILE: Sources/UIView+PBSnapshot.h
================================================
//
//  UIView+PBSnapshot.h
//  PhotoBrowser
//
//  Created by Shaw on 5/15/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import <UIKit/UIKit.h>

@interface UIView (PBSnapshot)
- (UIImage *)pb_snapshot;
- (UIImage *)pb_snapshotAfterScreenUpdates:(BOOL)afterUpdates;
@end


================================================
FILE: Sources/UIView+PBSnapshot.m
================================================
//
//  UIView+PBSnapshot.m
//  PhotoBrowser
//
//  Created by Shaw on 5/15/16.
//  Copyright © 2016 Shaw (https://github.com/cuzv).
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#import "UIView+PBSnapshot.h"

@implementation UIView (PBSnapshot)

- (UIImage *)pb_snapshot {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *outpu = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return outpu;
}

- (UIImage *)pb_snapshotAfterScreenUpdates:(BOOL)afterUpdates {
    if (![self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
        return [self pb_snapshot];
    }
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:afterUpdates];
    UIImage *outpu = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return outpu;
}

@end
Download .txt
gitextract_xp5uz9xr/

├── .gitignore
├── CHANGELOG.md
├── Example/
│   ├── Application/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.m
│   │   ├── Info.plist
│   │   ├── LaunchScreen.storyboard
│   │   ├── Main.storyboard
│   │   ├── RootViewController.h
│   │   ├── RootViewController.m
│   │   ├── UIView+LayerImage.h
│   │   ├── UIView+LayerImage.m
│   │   └── main.m
│   ├── Assets.xcassets/
│   │   └── AppIcon.appiconset/
│   │       └── Contents.json
│   ├── LocalImagesExample/
│   │   ├── LocalImagesExampleViewController.h
│   │   ├── LocalImagesExampleViewController.m
│   │   ├── PBImageView.h
│   │   └── PBImageView.m
│   └── RemoteImagesExample/
│       ├── RemoteImagesExampleViewController.h
│       └── RemoteImagesExampleViewController.m
├── LICENSE
├── PhotoBrowser.podspec
├── PhotoBrowser.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   └── contents.xcworkspacedata
│   └── xcshareddata/
│       └── xcschemes/
│           └── PhotoBrowser.xcscheme
├── PhotoBrowser.xcworkspace/
│   └── contents.xcworkspacedata
├── Podfile
├── README.md
└── Sources/
    ├── Info.plist
    ├── PBImageScrollView+internal.h
    ├── PBImageScrollView.h
    ├── PBImageScrollView.m
    ├── PBImageScrollViewController.h
    ├── PBImageScrollViewController.m
    ├── PBModalTransitionController.h
    ├── PBModalTransitionController.m
    ├── PBViewController.h
    ├── PBViewController.m
    ├── PhotoBrowser.h
    ├── UIView+PBSnapshot.h
    └── UIView+PBSnapshot.m
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (170K chars).
[
  {
    "path": ".gitignore",
    "chars": 493,
    "preview": "# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!defau"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1146,
    "preview": "# PhotoBrowser\n\nPhotoBrowser is a light weight photo browser, like the wechat, weibo image viewer.\n\n### 0.3.1\n\n-   修复 is"
  },
  {
    "path": "Example/Application/AppDelegate.h",
    "chars": 263,
    "preview": "//\n//  AppDelegate.h\n//  Example\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Moch. All rights reserved.\n//\n\n"
  },
  {
    "path": "Example/Application/AppDelegate.m",
    "chars": 2017,
    "preview": "//\n//  AppDelegate.m\n//  Example\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Moch. All rights reserved.\n//\n\n"
  },
  {
    "path": "Example/Application/Info.plist",
    "chars": 1558,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Application/LaunchScreen.storyboard",
    "chars": 1664,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "Example/Application/Main.storyboard",
    "chars": 8105,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Example/Application/RootViewController.h",
    "chars": 205,
    "preview": "//\n//  ViewController.h\n//  Example\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Moch. All rights reserved.\n/"
  },
  {
    "path": "Example/Application/RootViewController.m",
    "chars": 534,
    "preview": "//\n//  ViewController.m\n//  Example\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Moch. All rights reserved.\n/"
  },
  {
    "path": "Example/Application/UIView+LayerImage.h",
    "chars": 257,
    "preview": "//\n//  UIView+LayerImage.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 3/1/17.\n//  Copyright © 2017 RedRain. All rights r"
  },
  {
    "path": "Example/Application/UIView+LayerImage.m",
    "chars": 1022,
    "preview": "//\n//  UIView+LayerImage.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 3/1/17.\n//  Copyright © 2017 RedRain. All rights r"
  },
  {
    "path": "Example/Application/main.m",
    "chars": 320,
    "preview": "//\n//  main.m\n//  Example\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Moch. All rights reserved.\n//\n\n#import"
  },
  {
    "path": "Example/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1590,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "Example/LocalImagesExample/LocalImagesExampleViewController.h",
    "chars": 232,
    "preview": "//\n//  ViewController.h\n//  PhotoBrowserSample\n//\n//  Created by Shaw on 8/31/15.\n//  Copyright (c) 2015 Shaw. All right"
  },
  {
    "path": "Example/LocalImagesExample/LocalImagesExampleViewController.m",
    "chars": 5509,
    "preview": "//\n//  ViewController.m\n//  PhotoBrowserSample\n//\n//  Created by Shaw on 8/31/15.\n//  Copyright (c) 2015 Shaw. All right"
  },
  {
    "path": "Example/LocalImagesExample/PBImageView.h",
    "chars": 187,
    "preview": "//\n//  PBImageView.h\n//  Example\n//\n//  Created by Shaw on 6/9/18.\n//  Copyright © 2018 Shaw. All rights reserved.\n//\n\n#"
  },
  {
    "path": "Example/LocalImagesExample/PBImageView.m",
    "chars": 372,
    "preview": "//\n//  PBImageView.m\n//  Example\n//\n//  Created by Shaw on 6/9/18.\n//  Copyright © 2018 Shaw. All rights reserved.\n//\n\n#"
  },
  {
    "path": "Example/RemoteImagesExample/RemoteImagesExampleViewController.h",
    "chars": 227,
    "preview": "//\n//  DemoViewController.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/13/16.\n//  Copyright © 2016 Shaw. All rights re"
  },
  {
    "path": "Example/RemoteImagesExample/RemoteImagesExampleViewController.m",
    "chars": 6285,
    "preview": "//\n//  DemoViewController.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/13/16.\n//  Copyright © 2016 Shaw. All rights re"
  },
  {
    "path": "LICENSE",
    "chars": 1077,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Moch Xiao\n\nPermission is hereby granted, free of charge, to any person obtaini"
  },
  {
    "path": "PhotoBrowser.podspec",
    "chars": 565,
    "preview": "Pod::Spec.new do |s|\n  s.name         = \"PhotoBrowser\"\n  s.version      = \"0.8.1\"\n  s.summary      = \"PhotoBrowser is a "
  },
  {
    "path": "PhotoBrowser.xcodeproj/project.pbxproj",
    "chars": 43606,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "PhotoBrowser.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 157,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:PhotoBrowser.xc"
  },
  {
    "path": "PhotoBrowser.xcodeproj/xcshareddata/xcschemes/PhotoBrowser.xcscheme",
    "chars": 2895,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0940\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "PhotoBrowser.xcworkspace/contents.xcworkspacedata",
    "chars": 230,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:PhotoBrowser.x"
  },
  {
    "path": "Podfile",
    "chars": 211,
    "preview": "source 'https://github.com/CocoaPods/Specs.git'\nplatform :ios,'8.0'\ninhibit_all_warnings!\nuse_frameworks!\n\ntarget 'Examp"
  },
  {
    "path": "README.md",
    "chars": 3958,
    "preview": "[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/cuzv/PhotoBrowser/blob/master/LI"
  },
  {
    "path": "Sources/Info.plist",
    "chars": 806,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Sources/PBImageScrollView+internal.h",
    "chars": 2374,
    "preview": "//\n//  PBImageScrollView+internal.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/13/16.\n//  Copyright © 2016 Shaw (https"
  },
  {
    "path": "Sources/PBImageScrollView.h",
    "chars": 1510,
    "preview": "//\n//  PBImageScrollView.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/12/16.\n//  Copyright © 2016 Shaw (https://github"
  },
  {
    "path": "Sources/PBImageScrollView.m",
    "chars": 11017,
    "preview": "//\n//  PBImageScrollView.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/12/16.\n//  Copyright © 2016 Shaw (https://github"
  },
  {
    "path": "Sources/PBImageScrollViewController.h",
    "chars": 2174,
    "preview": "//\n//  PBImageScrollViewController.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 8/24/15.\n//  Copyright © 2015 Shaw (http"
  },
  {
    "path": "Sources/PBImageScrollViewController.m",
    "chars": 5259,
    "preview": "//\n//  PBImageScrollViewController.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 8/24/15.\n//  Copyright © 2015 Shaw (http"
  },
  {
    "path": "Sources/PBModalTransitionController.h",
    "chars": 2084,
    "preview": "//\n//  PBPresentAnimatedTransitioningController.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 20"
  },
  {
    "path": "Sources/PBModalTransitionController.m",
    "chars": 7671,
    "preview": "//\n//  PBModalTransitionController.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/17/16.\n//  Copyright © 2016 Shaw (http"
  },
  {
    "path": "Sources/PBViewController.h",
    "chars": 4249,
    "preview": "//\n//  PBViewController.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 8/24/15.\n//  Copyright © 2015 Shaw (https://github."
  },
  {
    "path": "Sources/PBViewController.m",
    "chars": 31073,
    "preview": "//\n//  PBViewController.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 8/24/15.\n//  Copyright © 2015 Shaw (https://github."
  },
  {
    "path": "Sources/PhotoBrowser.h",
    "chars": 1639,
    "preview": "//\n//  PhotoBrowser.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 8/24/15.\n//  Copyright © 2015 Shaw (https://github.com/"
  },
  {
    "path": "Sources/UIView+PBSnapshot.h",
    "chars": 1376,
    "preview": "//\n//  UIView+PBSnapshot.h\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/15/16.\n//  Copyright © 2016 Shaw (https://github"
  },
  {
    "path": "Sources/UIView+PBSnapshot.m",
    "chars": 2064,
    "preview": "//\n//  UIView+PBSnapshot.m\n//  PhotoBrowser\n//\n//  Created by Shaw on 5/15/16.\n//  Copyright © 2016 Shaw (https://github"
  }
]

About this extraction

This page contains the full source code of the cuzv/PhotoBrowser GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (154.3 KB), approximately 43.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!