TwitterImagePipeline/TIPImageViewFetchHelper.h (141 lines of code) (raw):

// // TIPImageViewFetchHelper.h // TwitterImagePipeline // // Created on 4/18/16. // Copyright © 2020 Twitter. All rights reserved. // #import <TwitterImagePipeline/TIPImageFetchDelegate.h> #import <TwitterImagePipeline/TIPImageUtils.h> #import <UIKit/UIView.h> @class TIPImageFetchMetrics; @class TIPImagePipeline; @protocol TIPImageFetchable; @protocol TIPImageFetchRequest; @protocol TIPImageViewFetchHelperDelegate; @protocol TIPImageViewFetchHelperDataSource; NS_ASSUME_NONNULL_BEGIN /** enum of disappearnace behaviors */ typedef NS_ENUM(NSInteger, TIPImageViewDisappearanceBehavior) { /** do nothing on disappear */ TIPImageViewDisappearanceBehaviorNone = 0, /** cancel the fetch on disappear */ TIPImageViewDisappearanceBehaviorCancelImageFetch, /** lower priority of fetch on disappear */ TIPImageViewDisappearanceBehaviorLowerImageFetchPriority, /** unload the image on disappear (and cancel any outstanding fetch) */ TIPImageViewDisappearanceBehaviorUnload, /** replace the image on disappear with a placeholder */ TIPImageViewDisappearanceBehaviorReplaceWithPlaceholder }; /** Helper object for loading a fetch request on behalf of a target `UIView` adopting `TIPImageFetchable`. Offers lots of dynamic features via subclassing and/or use of the `delegate` and/or `dataSource`. `TIPImageViewFetchHelper` has built in support for an information overlay too that is helpful for debugging (see `TIPImageViewHelper(Debugging)`) */ @interface TIPImageViewFetchHelper : NSObject #pragma mark Properties / State /** behavior on "disappear", default == `TIPImageViewDisappearanceBehaviorUnload` */ @property (nonatomic) TIPImageViewDisappearanceBehavior disappearanceBehavior; /** should treat application backgrounding as the view disappearing?, default == `YES` */ @property (nonatomic) BOOL shouldTreatApplicationBackgroundAsViewDisappearance; /** associated `UIView` that conforms to `TIPImageFetchable` */ @property (nonatomic, nullable, weak) UIView<TIPImageFetchable> *fetchView; /** request to fetch */ @property (nonatomic, readonly, nullable) id<TIPImageFetchRequest> fetchRequest; /** is the helper loading the fetch */ @property (nonatomic, readonly, getter=isLoading) BOOL loading; /** progress of the fetch, `0.f` to `1.f` */ @property (nonatomic, readonly) float fetchProgress; /** error of fetch, if encountered */ @property (nonatomic, readonly, nullable) NSError *fetchError; /** metrics of fetch */ @property (nonatomic, readonly, nullable) TIPImageFetchMetrics *fetchMetrics; /** dimensions of the fetched image */ @property (nonatomic, readonly) CGSize fetchResultDimensions; /** source of fetch */ @property (nonatomic, readonly) TIPImageLoadSource fetchSource; /** is the fetched image treated as a placeholder? */ @property (nonatomic, readonly) BOOL fetchedImageTreatedAsPlaceholder; /** is the fetched image a preview? */ @property (nonatomic, readonly) BOOL fetchedImageIsPreview; /** is the fetched image a preview that's being treated as final? */ @property (nonatomic, readonly) BOOL fetchedImageIsScaledPreviewAsFinal; /** is the fetched image a progressive scan/frame? */ @property (nonatomic, readonly) BOOL fetchedImageIsProgressiveFrame; /** is the fetched image the full load? */ @property (nonatomic, readonly) BOOL fetchedImageIsFullLoad; /** is the fetched image loaded at all? */ @property (nonatomic, readonly) BOOL didLoadAny; /** the fetched image, not necessarily the final image URL */ @property (nonatomic, readonly, nullable) NSURL *fetchedImageURL; #pragma mark Dynamic Behavior Support /** the delegate for this fetch helper */ @property (nonatomic, weak, nullable) id<TIPImageViewFetchHelperDelegate> delegate; /** the data source for this fetch helper */ @property (nonatomic, weak, nullable) id<TIPImageViewFetchHelperDataSource> dataSource; #pragma mark Initializer /** initializer (convenience) */ - (instancetype)init; /** initializer with delegate & data source (designated) */ - (instancetype)initWithDelegate:(nullable id<TIPImageViewFetchHelperDelegate>)delegate dataSource:(nullable id<TIPImageViewFetchHelperDataSource>)dataSource NS_DESIGNATED_INITIALIZER; #pragma mark Primary Actions /** reload the fetch */ - (void)reload; /** cancel in flight fetch */ - (void)cancelFetchRequest; /** clear the fetched image */ - (void)clearImage; #pragma mark Override Actions /** set the image, as if it was loaded from a fetch */ - (void)setImageAsIfLoaded:(UIImage *)image; /** set the image container, as if it was loaded from a fetch */ - (void)setImageContainerAsIfLoaded:(TIPImageContainer *)imageContainer; /** mark the image as loaded from a fetch */ - (void)markAsIfLoaded; /** set the image, as if it was a placeholder image */ - (void)setImageAsIfPlaceholder:(UIImage *)image; /** set the image container, as if it was a placeholder image */ - (void)setImageContainerAsIfPlaceholder:(TIPImageContainer *)imageContainer; /** mark the image as a placeholder */ - (void)markAsIfPlaceholder; #pragma mark Triggers - Do NOT override /** call when view will disappear */ - (void)triggerViewWillDisappear NS_REQUIRES_SUPER; /** call when view did disappear */ - (void)triggerViewDidDisappear NS_REQUIRES_SUPER; /** call when view will appear */ - (void)triggerViewWillAppear NS_REQUIRES_SUPER; /** call when view did appear */ - (void)triggerViewDidAppear NS_REQUIRES_SUPER; /** call when view is laying out subviews */ - (void)triggerViewLayingOutSubviews NS_REQUIRES_SUPER; /** call when view will move to window _newWindow_ (can be `nil` when removed from all windows) */ - (void)triggerViewWillMoveToWindow:(nullable UIWindow *)newWindow NS_REQUIRES_SUPER; /** call when view did move to window (or `nil`) */ - (void)triggerViewDidMoveToWindow NS_REQUIRES_SUPER; /** call when application did enter the background */ - (void)triggerApplicationDidEnterBackground; /** call when application will enter the foreground */ - (void)triggerApplicationWillEnterForeground; #pragma mark Transition a UIView between fetch helpers /** call to transition view from one fetch helper to a new fetch helper */ + (void)transitionView:(UIView<TIPImageFetchable> *)fetchableView fromFetchHelper:(nullable TIPImageViewFetchHelper *)fromHelper toFetchHelper:(nullable TIPImageViewFetchHelper *)toHelper; #pragma mark Global Methods /** Call this method to trigger all `TIPImageFetchHelpers` that failed to load an image to retry. For example, when the network conditions change, calling this method could yield a successful load with the network returning. */ + (void)notifyAllFetchHelpersToRetryFailedLoads; #pragma mark Deprecated /** Deprecated */ @property (nonatomic) TIPImageViewDisappearanceBehavior fetchDisappearanceBehavior __attribute__((deprecated("`fetchDisappearanceBehavior` is deprecated. Use `disappearanceBehavior` isntead. This API will be removed in the future."))); @end //! Notification that the debug info visibility for `TIPImageView` changed FOUNDATION_EXTERN NSString * const TIPImageViewDidUpdateDebugInfoVisibilityNotification; //! User info key of visibility, `NSNumber` wrapping a `BOOL` FOUNDATION_EXTERN NSString * const TIPImageViewDidUpdateDebugInfoVisibilityNotificationKeyVisible; /** Category for debugging features on `TIPImageView` */ @interface TIPImageViewFetchHelper (Debugging) /** make debug info visible or not */ @property (class, nonatomic, getter=isDebugInfoVisible) BOOL debugInfoVisible; /** color for image highlight when debug info visible */ @property (nonatomic, nullable) UIColor *debugImageHighlightColor; /** color for text of debug info when visible */ @property (nonatomic, nullable) UIColor *debugInfoTextColor; /** Returns a mutable array of debug info strings for debug info view to display. Subclasses can override and modify returned array. */ - (NSMutableArray<NSString *> *)debugInfoStrings NS_REQUIRES_SUPER; /** trigger that the debug info needs updating */ - (void)setDebugInfoNeedsUpdate; @end /** Data source protocol for `TIPImageViewFetchHelper` Selection order: 1. TIPImageContainer 2. UIImage 3. TIPImageFetchRequest 4. NSURL */ @protocol TIPImageViewFetchHelperDataSource <NSObject> #pragma mark Image Load Source @optional // Chosen in order /** load from a static `TIPImageContainer` */ - (nullable TIPImageContainer *)tip_imageContainerForFetchHelper:(TIPImageViewFetchHelper *)helper; /** load from a static `UIImage` */ - (nullable UIImage *)tip_imageForFetchHelper:(TIPImageViewFetchHelper *)helper; /** load from a specific `NSURL` */ - (nullable NSURL *)tip_imageURLForFetchHelper:(TIPImageViewFetchHelper *)helper; /** load from a `TIPImageFetchRequest` */ - (nullable id<TIPImageFetchRequest>)tip_imageFetchRequestForFetchHelper:(TIPImageViewFetchHelper *)helper; // Pipeline /** always called after a `TIPImageFetchRequest` or `NSURL` is loaded. Failing to implement this method or returning `nil` will end the fetch. */ - (nullable TIPImagePipeline *)tip_imagePipelineForFetchHelper:(TIPImageViewFetchHelper *)helper; #pragma mark Image Load Behavior @optional /** can inspect the fetchImageView and the fetchRequest to make a decision. Default == `NO` */ - (BOOL)tip_shouldRefetchOnTargetSizingChangeForFetchHelper:(TIPImageViewFetchHelper *)helper; /** the priority for the fetch, default == `NSOperationQueuePriorityNormal` */ - (NSOperationQueuePriority)tip_fetchOperationPriorityForFetchHelper:(TIPImageViewFetchHelper *)helper; #pragma mark Debug @optional /** Can extend the debug info of a debug info overlay */ - (nullable NSArray<NSString *> *)tip_additionalDebugInfoStringsForFetchHelper:(TIPImageViewFetchHelper *)helper; @end /** Delegate protocol for `TIPImageViewFetchHelper` */ @protocol TIPImageViewFetchHelperDelegate <NSObject> #pragma mark Deciders @optional /** should update image with preview image? default == `NO` */ - (BOOL)tip_fetchHelper:(TIPImageViewFetchHelper *)helper shouldUpdateImageWithPreviewImageResult:(id<TIPImageFetchResult>)previewImageResult; /** should continue to load after fetching preview? Default behavior: - If preview result is a placeholder, `YES`. - If fetching request is placeholder, `NO`. - If preview is larger or equal to target sizing, `NO`. - Otherwise, `YES`. */ - (BOOL)tip_fetchHelper:(TIPImageViewFetchHelper *)helper shouldContinueLoadingAfterFetchingPreviewImageResult:(id<TIPImageFetchResult>)previewImageResult; /** should load progressively? default == `NO` Called via a background thread synchronously. */ - (BOOL)tip_fetchHelper:(TIPImageViewFetchHelper *)helper shouldLoadProgressivelyWithIdentifier:(NSString *)identifier URL:(NSURL *)URL imageType:(NSString *)imageType originalDimensions:(CGSize)originalDimensions; /** should reload after a different fetch completed? Has automatic/default behavior, call super to utilize auto behavior */ - (BOOL)tip_fetchHelper:(TIPImageViewFetchHelper *)helper shouldReloadAfterDifferentFetchCompletedWithImageContainer:(TIPImageContainer *)imageContainer dimensions:(CGSize)dimensions identifier:(NSString *)identifier URL:(NSURL *)URL treatedAsPlaceholder:(BOOL)placeholder manuallyStored:(BOOL)manuallyStored; #pragma mark Events @optional /** fetch did start loading */ - (void)tip_fetchHelperDidStartLoading:(TIPImageViewFetchHelper *)helper; /** fetch did update progress */ - (void)tip_fetchHelper:(TIPImageViewFetchHelper *)helper didUpdateProgress:(float)progress; /** fetch did update displayed image with a `TIPImageContainer` */ - (void)tip_fetchHelper:(TIPImageViewFetchHelper *)helper didUpdateDisplayedImageContainer:(TIPImageContainer *)imageContainer fromSourceDimensions:(CGSize)size isFinal:(BOOL)isFinal; /** fetch did load final image */ - (void)tip_fetchHelper:(TIPImageViewFetchHelper *)helper didLoadFinalImageFromSource:(TIPImageLoadSource)source; /** fetch did fail */ - (void)tip_fetchHelper:(TIPImageViewFetchHelper *)helper didFailToLoadFinalImage:(NSError *)error; /** fetch did reset */ - (void)tip_fetchHelperDidReset:(TIPImageViewFetchHelper *)helper; /** fetch did start loading from the network */ - (void)tip_fetchHelperDidStartLoadingFromNetwork:(TIPImageViewFetchHelper *)helper; #pragma mark Deprecated /** should reload after a different fetch completed? Has automatic/default behavior, call super to utilize auto behavior @warning deprecated callback, implement `tip_fetchHelper:shouldReloadAfterDifferentFetchCompletedWithImage:dimensions:identifier:URL:treatedAsPlaceholder:manuallyStored:` instead */ - (BOOL)tip_fetchHelper:(TIPImageViewFetchHelper *)helper shouldReloadAfterDifferentFetchCompletedWithImage:(UIImage *)image dimensions:(CGSize)dimensions identifier:(NSString *)identifier URL:(NSURL *)URL treatedAsPlaceholder:(BOOL)placeholder manuallyStored:(BOOL)manuallyStored __attribute__((deprecated("implement `tip_fetchHelper:shouldReloadAfterDifferentFetchCompletedWithImage:dimensions:identifier:URL:treatedAsPlaceholder:manuallyStored:` instead"))); /** fetch did update displayed image @warning deprecated callback, implement `tip_fetchHelper:didUpdateDisplayedImageContainer:fromSourceDimensions:isFinal:` instead */ - (void)tip_fetchHelper:(TIPImageViewFetchHelper *)helper didUpdateDisplayedImage:(UIImage *)image fromSourceDimensions:(CGSize)size isFinal:(BOOL)isFinal __attribute__((deprecated("implement `tip_fetchHelper:didUpdateDisplayedImageContainer:fromSourceDimensions:isFinal:` instead"))); @end NS_ASSUME_NONNULL_END