Skip to content
3 changes: 2 additions & 1 deletion TZImagePickerController/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ - (void)useNavControllerAsRoot {
- (void)pushTZImagePickerController {
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:9 columnNumber:4 delegate:nil pushPhotoPickerVc:YES];
imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
UINavigationController *nav = (UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController;
UIWindow *keyWindow = [TZCommonTools currentKeyWindow];
UINavigationController *nav =(UINavigationController *)keyWindow.rootViewController;
[nav.topViewController presentViewController:imagePickerVc animated:YES completion:nil];
}

Expand Down
5 changes: 2 additions & 3 deletions TZImagePickerController/FLAnimatedImage/FLAnimatedImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) 2013-2015 Flipboard. All rights reserved.
// Copyright (c) Flipboard. All rights reserved.
//


Expand All @@ -12,7 +12,6 @@
// Allow user classes conveniently just importing one header.
#import "FLAnimatedImageView.h"


#ifndef NS_DESIGNATED_INITIALIZER
#if __has_attribute(objc_designated_initializer)
#define NS_DESIGNATED_INITIALIZER __attribute((objc_designated_initializer))
Expand All @@ -34,7 +33,7 @@ extern const NSTimeInterval kFLAnimatedImageDelayTimeIntervalMinimum;
@property (nonatomic, strong, readonly) UIImage *posterImage; // Guaranteed to be loaded; usually equivalent to `-imageLazilyCachedAtIndex:0`
@property (nonatomic, assign, readonly) CGSize size; // The `.posterImage`'s `.size`

@property (nonatomic, assign, readonly) NSUInteger loopCount; // 0 means repeating the animation indefinitely
@property (nonatomic, assign, readonly) NSUInteger loopCount; // "The number of times to repeat an animated sequence." according to ImageIO (note the slightly different definition to Netscape 2.0 Loop Extension); 0 means repeating the animation forever
@property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes; // Of type `NSTimeInterval` boxed in `NSNumber`s
@property (nonatomic, assign, readonly) NSUInteger frameCount; // Number of valid frames; equal to `[.delayTimes count]`

Expand Down
115 changes: 69 additions & 46 deletions TZImagePickerController/FLAnimatedImage/FLAnimatedImage.m

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions TZImagePickerController/FLAnimatedImage/FLAnimatedImageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) 2013-2015 Flipboard. All rights reserved.
// Copyright (c) Flipboard. All rights reserved.
//


Expand Down Expand Up @@ -31,6 +31,6 @@

// The animation runloop mode. Enables playback during scrolling by allowing timer events (i.e. animation) with NSRunLoopCommonModes.
// To keep scrolling smooth on single-core devices such as iPhone 3GS/4 and iPod Touch 4th gen, the default run loop mode is NSDefaultRunLoopMode. Otherwise, the default is NSDefaultRunLoopMode.
@property (nonatomic, copy) NSString *runLoopMode;
@property (nonatomic, copy) NSRunLoopMode runLoopMode;

@end
60 changes: 39 additions & 21 deletions TZImagePickerController/FLAnimatedImage/FLAnimatedImageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Flipboard
//
// Created by Raphael Schaad on 7/8/13.
// Copyright (c) 2013-2015 Flipboard. All rights reserved.
// Copyright (c) Flipboard. All rights reserved.
//


Expand Down Expand Up @@ -101,8 +101,16 @@ - (void)setAnimatedImage:(FLAnimatedImage *)animatedImage
{
if (![_animatedImage isEqual:animatedImage]) {
if (animatedImage) {
// Clear out the image.
super.image = nil;
if (super.image) {
// UIImageView's `setImage:` will internally call its layer's `setContentsTransform:` based on the `image.imageOrientation`.
// The `contentsTransform` will affect layer rendering rotation because the CGImage's bitmap buffer does not actually take rotation.
// However, when calling `setImage:nil`, this `contentsTransform` will not be reset to identity.
// Further animation frame will be rendered as rotated. So we must set it to the poster image to clear the previous state.
// See more here: https://github.com/Flipboard/FLAnimatedImage/issues/100
super.image = animatedImage.posterImage;
// Clear out the image.
super.image = nil;
}
// Ensure disabled highlighting; it's not supported (see `-setHighlighted:`).
super.highlighted = NO;
// UIImageView seems to bypass some accessors when calculating its intrinsic content size, so this ensures its intrinsic content size comes from the animated image.
Expand Down Expand Up @@ -213,7 +221,6 @@ - (CGSize)intrinsicContentSize
return intrinsicContentSize;
}

#pragma mark Smart Invert Colors

#pragma mark - UIImageView Method Overrides
#pragma mark Image Data
Expand Down Expand Up @@ -249,7 +256,7 @@ - (NSTimeInterval)frameDelayGreatestCommonDivisor
// Presision is set to half of the `kFLAnimatedImageDelayTimeIntervalMinimum` in order to minimize frame dropping.
const NSTimeInterval kGreatestCommonDivisorPrecision = 2.0 / kFLAnimatedImageDelayTimeIntervalMinimum;

NSArray *delays = self.animatedImage.delayTimesForIndexes.allValues;
NSArray *const delays = self.animatedImage.delayTimesForIndexes.allValues;

// Scales the frame delays by `kGreatestCommonDivisorPrecision`
// then converts it to an UInteger for in order to calculate the GCD.
Expand All @@ -259,7 +266,7 @@ - (NSTimeInterval)frameDelayGreatestCommonDivisor
}

// Reverse to scale to get the value back into seconds.
return scaledGCD / kGreatestCommonDivisorPrecision;
return (double)scaledGCD / kGreatestCommonDivisorPrecision;
}


Expand All @@ -273,7 +280,7 @@ static NSUInteger gcd(NSUInteger a, NSUInteger b)
}

while (true) {
NSUInteger remainder = a % b;
const NSUInteger remainder = a % b;
if (remainder == 0) {
return b;
}
Expand All @@ -298,18 +305,21 @@ - (void)startAnimating
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:self.runLoopMode];
}

// Note: The display link's `.frameInterval` value of 1 (default) means getting callbacks at the refresh rate of the display (~60Hz).
// Setting it to 2 divides the frame rate by 2 and hence calls back at every other display refresh.
const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz
self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);

if (@available(iOS 10.0, *)) {
// Adjusting preferredFramesPerSecond allows us to skip unnecessary calls to displayDidRefresh: when showing GIFs
// that don't animate quickly. Use ceil to err on the side of too many FPS so we don't miss a frame transition moment.
self.displayLink.preferredFramesPerSecond = ceil(1.0 / [self frameDelayGreatestCommonDivisor]);
} else {
const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz
self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);
}
self.displayLink.paused = NO;
} else {
[super startAnimating];
}
}

- (void)setRunLoopMode:(NSString *)runLoopMode
- (void)setRunLoopMode:(NSRunLoopMode)runLoopMode
{
if (![@[NSDefaultRunLoopMode, NSRunLoopCommonModes] containsObject:runLoopMode]) {
NSAssert(NO, @"Invalid run loop mode: %@", runLoopMode);
Expand Down Expand Up @@ -359,7 +369,7 @@ - (void)setHighlighted:(BOOL)highlighted
// Just update our cached value whenever the animated image or visibility (window, superview, hidden, alpha) is changed.
- (void)updateShouldAnimate
{
BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0;
const BOOL isVisible = self.window && self.superview && ![self isHidden] && self.alpha > 0.0;
self.shouldAnimate = self.animatedImage && isVisible;
}

Expand All @@ -373,12 +383,12 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
return;
}

NSNumber *delayTimeNumber = [self.animatedImage.delayTimesForIndexes objectForKey:@(self.currentFrameIndex)];
NSNumber *_Nullable const delayTimeNumber = [self.animatedImage.delayTimesForIndexes objectForKey:@(self.currentFrameIndex)];
// If we don't have a frame delay (e.g. corrupt frame), don't update the view but skip the playhead to the next frame (in else-block).
if (delayTimeNumber) {
NSTimeInterval delayTime = [delayTimeNumber floatValue];
if (delayTimeNumber != nil) {
const NSTimeInterval delayTime = [delayTimeNumber floatValue];
// If we have a nil image (e.g. waiting for frame), don't update the view nor playhead.
UIImage *image = [self.animatedImage imageLazilyCachedAtIndex:self.currentFrameIndex];
UIImage *_Nullable const image = [self.animatedImage imageLazilyCachedAtIndex:self.currentFrameIndex];
if (image) {
FLLog(FLLogLevelVerbose, @"Showing frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
self.currentFrame = image;
Expand All @@ -387,7 +397,11 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
self.needsDisplayWhenImageBecomesAvailable = NO;
}

self.accumulator += displayLink.duration * displayLink.frameInterval;
if (@available(iOS 10.0, *)) {
self.accumulator += displayLink.targetTimestamp - CACurrentMediaTime();
} else {
self.accumulator += displayLink.duration * (NSTimeInterval)displayLink.frameInterval;
}

// While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m
while (self.accumulator >= delayTime) {
Expand All @@ -414,7 +428,11 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
FLLog(FLLogLevelDebug, @"Waiting for frame %lu for animated image: %@", (unsigned long)self.currentFrameIndex, self.animatedImage);
#if defined(DEBUG) && DEBUG
if ([self.debug_delegate respondsToSelector:@selector(debug_animatedImageView:waitingForFrame:duration:)]) {
[self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:(NSTimeInterval)displayLink.duration * displayLink.frameInterval];
if (@available(iOS 10.0, *)) {
[self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:displayLink.targetTimestamp - CACurrentMediaTime()];
} else {
[self.debug_delegate debug_animatedImageView:self waitingForFrame:self.currentFrameIndex duration:displayLink.duration * (NSTimeInterval)displayLink.frameInterval];
}
}
#endif
}
Expand All @@ -423,7 +441,7 @@ - (void)displayDidRefresh:(CADisplayLink *)displayLink
}
}

+ (NSString *)defaultRunLoopMode
+ (NSRunLoopMode)defaultRunLoopMode
{
// Key off `activeProcessorCount` (as opposed to `processorCount`) since the system could shut down cores in certain situations.
return [NSProcessInfo processInfo].activeProcessorCount > 1 ? NSRunLoopCommonModes : NSDefaultRunLoopMode;
Expand Down
7 changes: 7 additions & 0 deletions TZImagePickerController/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict/>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>en_US</string>
<key>CFBundleExecutable</key>
Expand Down
6 changes: 5 additions & 1 deletion TZImagePickerController/LxGridViewFlowLayout.m
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,11 @@ - (void)longPressGestureRecognizerTriggerd:(UILongPressGestureRecognizer *)longP
{
if (_displayLink == nil) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkTriggered:)];
_displayLink.frameInterval = 6;
if (@available(iOS 10.0, *)) {
_displayLink.preferredFramesPerSecond = 6;
} else {
_displayLink.frameInterval = 6;
}
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

_remainSecondsToBeginEditing = MIN_PRESS_TO_BEGIN_EDITING_DURATION;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ @implementation TZGifPhotoPreviewController

- (void)viewDidLoad {
[super viewDidLoad];
self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden;
self.needShowStatusBar = ![TZCommonTools currentStatusBarHidden];
self.view.backgroundColor = [UIColor blackColor];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (tzImagePickerVc) {
Expand All @@ -44,16 +44,11 @@ - (void)viewDidLoad {

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
_originStatusBarStyle = [TZCommonTools currentStatusBarStyle];
}

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.needShowStatusBar) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
[UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle;
}

- (void)configPreviewView {
Expand Down Expand Up @@ -101,6 +96,10 @@ - (void)configBottomToolBar {
}
}

- (BOOL)prefersStatusBarHidden {
return !self.needShowStatusBar;
}

- (UIStatusBarStyle)preferredStatusBarStyle {
TZImagePickerController *tzImagePicker = (TZImagePickerController *)self.navigationController;
if (tzImagePicker && [tzImagePicker isKindOfClass:[TZImagePickerController class]]) {
Expand Down Expand Up @@ -132,12 +131,6 @@ - (void)viewDidLayoutSubviews {
- (void)signleTapAction {
_toolBar.hidden = !_toolBar.isHidden;
[self.navigationController setNavigationBarHidden:_toolBar.isHidden];
TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController;
if (_toolBar.isHidden) {
[UIApplication sharedApplication].statusBarHidden = YES;
} else if (tzImagePickerVc.needShowStatusBar) {
[UIApplication sharedApplication].statusBarHidden = NO;
}
}

- (void)doneButtonClick {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
- (PHImageRequestID)getPhotoWithAsset:(PHAsset *)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
- (PHImageRequestID)requestImageDataForAsset:(PHAsset *)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler;
//适配iOS13
- (PHImageRequestID)requestImageDataFitSystemForAsset:(PHAsset *)asset options:(nullable PHImageRequestOptions *)options completion:(void (^_Nonnull)(NSData *_Nullable imageData, NSString *_Nullable dataUTI, UIImageOrientation orientation, NSDictionary *_Nullable info))completion;

/// Get full Image 获取原图
/// 如下两个方法completion一般会调多次,一般会先返回缩略图,再返回原图(详见方法内部使用的系统API的说明),如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。
Expand Down
Loading