Move the watchOS wrapper hack into another file, make it easy to distinguish from other code to maintain
This commit is contained in:
parent
a2a54ec329
commit
e01fa4047b
|
@ -31,6 +31,14 @@
|
|||
326E480B23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480C23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
326E480D23431C0F00C633E9 /* ImageViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */; };
|
||||
3276EB00237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3276EB01237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3276EB02237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3276EB03237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
3276EB04237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */; };
|
||||
3276EB05237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */; };
|
||||
3276EB06237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */; };
|
||||
3276EB07237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */; };
|
||||
32B933E523659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; };
|
||||
32B933E623659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; };
|
||||
32B933E723659A1900BB7CAD /* Transition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B933E423659A1900BB7CAD /* Transition.swift */; };
|
||||
|
@ -119,6 +127,8 @@
|
|||
326B8486236335110011BDFB /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
|
||||
326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = "<group>"; };
|
||||
326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = "<group>"; };
|
||||
3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDAnimatedImageInterfaceWrapper.h; sourceTree = "<group>"; };
|
||||
3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageInterfaceWrapper.m; sourceTree = "<group>"; };
|
||||
32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = "<group>"; };
|
||||
32C43DCC22FD540D00BE87F5 /* SDWebImageSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
32C43DDC22FD54C600BE87F5 /* ImageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = "<group>"; };
|
||||
|
@ -177,6 +187,8 @@
|
|||
children = (
|
||||
324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */,
|
||||
324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */,
|
||||
3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */,
|
||||
3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */,
|
||||
);
|
||||
path = ObjC;
|
||||
sourceTree = "<group>";
|
||||
|
@ -262,6 +274,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB00237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */,
|
||||
324F61C7235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43DE622FD54CD00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
|
@ -271,6 +284,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB01237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */,
|
||||
324F61C8235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2222FD583A00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
|
@ -280,6 +294,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB02237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */,
|
||||
324F61C9235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2322FD583B00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
|
@ -289,6 +304,7 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB03237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h in Headers */,
|
||||
324F61CA235E07EC003973B8 /* SDAnimatedImageInterface.h in Headers */,
|
||||
32C43E2422FD583C00BE87F5 /* SDWebImageSwiftUI.h in Headers */,
|
||||
);
|
||||
|
@ -457,6 +473,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB04237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */,
|
||||
32B933E523659A1900BB7CAD /* Transition.swift in Sources */,
|
||||
32C43E1722FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
326B848C236335400011BDFB /* ProgressIndicator.swift in Sources */,
|
||||
|
@ -474,6 +491,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB05237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */,
|
||||
32B933E623659A1900BB7CAD /* Transition.swift in Sources */,
|
||||
32C43E1A22FD583700BE87F5 /* WebImage.swift in Sources */,
|
||||
326B848D236335400011BDFB /* ProgressIndicator.swift in Sources */,
|
||||
|
@ -491,6 +509,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB06237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */,
|
||||
32B933E723659A1900BB7CAD /* Transition.swift in Sources */,
|
||||
32C43E1D22FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
326B848E236335400011BDFB /* ProgressIndicator.swift in Sources */,
|
||||
|
@ -508,6 +527,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3276EB07237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m in Sources */,
|
||||
32B933E823659A1900BB7CAD /* Transition.swift in Sources */,
|
||||
32C43E2022FD583800BE87F5 /* WebImage.swift in Sources */,
|
||||
326B848F236335400011BDFB /* ProgressIndicator.swift in Sources */,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Do not use this class directly in WatchKit or Storyboard. This class is implementation detail and will be removed in the future.
|
||||
/// This is not public API at all.
|
||||
@interface SDAnimatedImageInterface : WKInterfaceImage
|
||||
|
||||
@property (nonatomic, assign, getter=isAnimating, readonly) BOOL animating;
|
||||
|
@ -29,13 +30,5 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@end
|
||||
|
||||
@interface SDAnimatedImageInterfaceWrapper : WKInterfaceGroup
|
||||
|
||||
@property (nonatomic, strong, nonnull) SDAnimatedImageInterface *wrapped;
|
||||
|
||||
- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
#import "SDAnimatedImageInterface.h"
|
||||
#if SD_WATCH
|
||||
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
#pragma mark - SPI
|
||||
|
||||
@protocol CALayerProtocol <NSObject>
|
||||
|
@ -31,16 +28,10 @@
|
|||
@property (nonatomic) CGRect frame;
|
||||
@property (nonatomic) CGRect bounds;
|
||||
@property (nonatomic) CGPoint center;
|
||||
@property (nonatomic) BOOL clipsToBounds;
|
||||
@property (nonatomic, readonly) CGSize intrinsicContentSize;
|
||||
@property(nonatomic) NSInteger tag;
|
||||
|
||||
- (void)invalidateIntrinsicContentSize;
|
||||
- (void)drawRect:(CGRect)rect;
|
||||
- (void)setNeedsDisplay;
|
||||
- (void)setNeedsDisplayInRect:(CGRect)rect;
|
||||
- (void)addSubview:(id<UIViewProtocol>)view;
|
||||
- (void)removeFromSuperview;
|
||||
- (void)layoutSubviews;
|
||||
- (CGSize)sizeThatFits:(CGSize)size;
|
||||
- (void)sizeToFit;
|
||||
|
@ -60,6 +51,7 @@
|
|||
|
||||
// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
|
||||
- (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)property;
|
||||
- (NSDictionary *)interfaceDescriptionForDynamicCreation;
|
||||
// This is remote UIView
|
||||
@property (nonatomic, strong, readwrite) id<UIViewProtocol> _interfaceView;
|
||||
|
||||
|
@ -258,140 +250,4 @@
|
|||
|
||||
@end
|
||||
|
||||
|
||||
#define SDAnimatedImageInterfaceWrapperTag 123456789
|
||||
#define SDAnimatedImageInterfaceWrapperSEL_layoutSubviews @"SDAnimatedImageInterfaceWrapper_layoutSubviews"
|
||||
#define SDAnimatedImageInterfaceWrapperSEL_sizeThatFits @" SDAnimatedImageInterfaceWrapper_sizeThatFits:"
|
||||
|
||||
// This using hook to implements the same logic like AnimatedImageViewWrapper.swift
|
||||
static CGSize intrinsicContentSizeIMP(id<UIViewProtocol> self, SEL _cmd) {
|
||||
struct objc_super superClass = {
|
||||
self,
|
||||
[self superclass]
|
||||
};
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
return ((CGSize(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&superClass), _cmd);
|
||||
}
|
||||
CGSize size = interfaceView.intrinsicContentSize;
|
||||
if (size.width > 0 && size.height > 0) {
|
||||
CGFloat aspectRatio = size.height / size.width;
|
||||
return CGSizeMake(1, 1 * aspectRatio);
|
||||
} else {
|
||||
return CGSizeMake(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void layoutSubviewsIMP(id<UIViewProtocol> self, SEL _cmd) {
|
||||
struct objc_super superClass = {
|
||||
self,
|
||||
[self superclass]
|
||||
};
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
((void(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_layoutSubviews));
|
||||
return;
|
||||
}
|
||||
((void(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&superClass), _cmd);
|
||||
interfaceView.frame = self.bounds;
|
||||
}
|
||||
|
||||
// This is suck that SwiftUI on watchOS will call extra sizeThatFits, we should always input size (already calculated with aspectRatio)
|
||||
// iOS's wrapper don't need this
|
||||
static CGSize sizeThatFitsIMP(id<UIViewProtocol> self, SEL _cmd, CGSize size) {
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
return ((CGSize(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_sizeThatFits));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@implementation SDAnimatedImageInterfaceWrapper
|
||||
|
||||
+ (void)load {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class class = NSClassFromString(@"SPInterfaceGroupView");
|
||||
// Implements `intrinsicContentSize`
|
||||
SEL selector = @selector(intrinsicContentSize);
|
||||
Method method = class_getInstanceMethod(class, selector);
|
||||
|
||||
BOOL didAddMethod =
|
||||
class_addMethod(class,
|
||||
selector,
|
||||
(IMP)intrinsicContentSizeIMP,
|
||||
method_getTypeEncoding(method));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
}
|
||||
|
||||
// Override `layoutSubviews`
|
||||
SEL originalSelector = @selector(layoutSubviews);
|
||||
SEL swizzledSelector = NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_layoutSubviews);
|
||||
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
||||
|
||||
didAddMethod =
|
||||
class_addMethod(class,
|
||||
swizzledSelector,
|
||||
(IMP)layoutSubviewsIMP,
|
||||
method_getTypeEncoding(originalMethod));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
} else {
|
||||
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
}
|
||||
|
||||
// Override `sizeThatFits:`
|
||||
originalSelector = @selector(sizeThatFits:);
|
||||
swizzledSelector = NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_sizeThatFits);
|
||||
originalMethod = class_getInstanceMethod(class, originalSelector);
|
||||
|
||||
didAddMethod =
|
||||
class_addMethod(class,
|
||||
swizzledSelector,
|
||||
(IMP)sizeThatFitsIMP,
|
||||
method_getTypeEncoding(originalMethod));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
} else {
|
||||
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
Class cls = [self class];
|
||||
NSString *UUID = [NSUUID UUID].UUIDString;
|
||||
NSString *property = [NSString stringWithFormat:@"%@_%@", cls, UUID];
|
||||
self = [self _initForDynamicCreationWithInterfaceProperty:property];
|
||||
if (self) {
|
||||
self.wrapped = [[SDAnimatedImageInterface alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)interfaceDescriptionForDynamicCreation {
|
||||
// This is called by WatchKit to provide default value
|
||||
return @{
|
||||
@"type" : @"group",
|
||||
@"property" : self.interfaceProperty,
|
||||
@"radius" : @(0),
|
||||
@"items": @[self.wrapped.interfaceDescriptionForDynamicCreation], // This will create the native view and added to subview
|
||||
};
|
||||
}
|
||||
|
||||
- (void)set_interfaceView:(id<UIViewProtocol>)interfaceView {
|
||||
// This is called by WatchKit when native view created
|
||||
[super set_interfaceView:interfaceView];
|
||||
// Bind the interface object and native view
|
||||
interfaceView.tag = SDAnimatedImageInterfaceWrapperTag;
|
||||
self.wrapped._interfaceView = interfaceView.subviews.firstObject;
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDAnimatedImageInterface.h"
|
||||
|
||||
#if SD_WATCH
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Do not use this class directly in WatchKit or Storyboard. This class is implementation detail and will be removed in the future.
|
||||
/// This is not public API at all.
|
||||
@interface SDAnimatedImageInterfaceWrapper : WKInterfaceGroup
|
||||
|
||||
@property (nonatomic, strong, nonnull) SDAnimatedImageInterface *wrapped;
|
||||
|
||||
- (instancetype)init WK_AVAILABLE_WATCHOS_ONLY(6.0);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) DreamPiggy <lizhuoli1126@126.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "SDAnimatedImageInterfaceWrapper.h"
|
||||
#if SD_WATCH
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
|
||||
#pragma mark - SPI
|
||||
|
||||
@protocol CALayerProtocol <NSObject>
|
||||
@property (nullable, strong) id contents;
|
||||
@property CGFloat contentsScale;
|
||||
@end
|
||||
|
||||
@protocol UIViewProtocol <NSObject>
|
||||
@property (nonatomic, strong, readonly) id<CALayerProtocol> layer;
|
||||
@property (nonatomic, assign) SDImageScaleMode contentMode;
|
||||
@property (nonatomic, readonly) id<UIViewProtocol> superview;
|
||||
@property (nonatomic, readonly, copy) NSArray<id<UIViewProtocol>> *subviews;
|
||||
@property (nonatomic, readonly) id window;
|
||||
@property (nonatomic) CGFloat alpha;
|
||||
@property (nonatomic, getter=isHidden) BOOL hidden;
|
||||
@property (nonatomic, getter=isOpaque) BOOL opaque;
|
||||
@property (nonatomic) CGRect frame;
|
||||
@property (nonatomic) CGRect bounds;
|
||||
@property (nonatomic) CGPoint center;
|
||||
@property (nonatomic, readonly) CGSize intrinsicContentSize;
|
||||
@property(nonatomic) NSInteger tag;
|
||||
|
||||
- (void)invalidateIntrinsicContentSize;
|
||||
- (void)layoutSubviews;
|
||||
- (CGSize)sizeThatFits:(CGSize)size;
|
||||
- (void)sizeToFit;
|
||||
|
||||
@end
|
||||
|
||||
@protocol UIImageViewProtocol <UIViewProtocol>
|
||||
|
||||
@property (nullable, nonatomic, strong) UIImage *image;
|
||||
- (void)startAnimating;
|
||||
- (void)stopAnimating;
|
||||
@property (nonatomic, readonly, getter=isAnimating) BOOL animating;
|
||||
|
||||
@end
|
||||
|
||||
@interface WKInterfaceObject ()
|
||||
|
||||
// This is needed for dynamic created WKInterfaceObject, like `WKInterfaceMap`
|
||||
- (instancetype)_initForDynamicCreationWithInterfaceProperty:(NSString *)property;
|
||||
- (NSDictionary *)interfaceDescriptionForDynamicCreation;
|
||||
// This is remote UIView
|
||||
@property (nonatomic, strong, readwrite) id<UIViewProtocol> _interfaceView;
|
||||
|
||||
@end
|
||||
|
||||
#define SDAnimatedImageInterfaceWrapperTag 123456789
|
||||
#define SDAnimatedImageInterfaceWrapperSEL_layoutSubviews @"SDAnimatedImageInterfaceWrapper_layoutSubviews"
|
||||
#define SDAnimatedImageInterfaceWrapperSEL_sizeThatFits @" SDAnimatedImageInterfaceWrapper_sizeThatFits:"
|
||||
|
||||
// This using hook to implements the same logic like AnimatedImageViewWrapper.swift
|
||||
static CGSize intrinsicContentSizeIMP(id<UIViewProtocol> self, SEL _cmd) {
|
||||
struct objc_super superClass = {
|
||||
self,
|
||||
[self superclass]
|
||||
};
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
return ((CGSize(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&superClass), _cmd);
|
||||
}
|
||||
CGSize size = interfaceView.intrinsicContentSize;
|
||||
if (size.width > 0 && size.height > 0) {
|
||||
CGFloat aspectRatio = size.height / size.width;
|
||||
return CGSizeMake(1, 1 * aspectRatio);
|
||||
} else {
|
||||
return CGSizeMake(-1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void layoutSubviewsIMP(id<UIViewProtocol> self, SEL _cmd) {
|
||||
struct objc_super superClass = {
|
||||
self,
|
||||
[self superclass]
|
||||
};
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
((void(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_layoutSubviews));
|
||||
return;
|
||||
}
|
||||
((void(*)(id, SEL))objc_msgSendSuper)((__bridge id)(&superClass), _cmd);
|
||||
interfaceView.frame = self.bounds;
|
||||
}
|
||||
|
||||
// This is suck that SwiftUI on watchOS will call extra sizeThatFits, we should always return input size (already calculated with aspectRatio)
|
||||
// iOS's wrapper don't need this. Apple should provide the public API on View protocol to specify `intrinsicContentSize` or `intrinsicAspectRatio`
|
||||
static CGSize sizeThatFitsIMP(id<UIViewProtocol> self, SEL _cmd, CGSize size) {
|
||||
NSUInteger tag = self.tag;
|
||||
id<UIViewProtocol> interfaceView = self.subviews.firstObject;
|
||||
if (tag != SDAnimatedImageInterfaceWrapperTag || !interfaceView) {
|
||||
return ((CGSize(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_sizeThatFits));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@implementation SDAnimatedImageInterfaceWrapper
|
||||
|
||||
/// Use wrapper to solve tne watchOS `WKInterfaceImage` frame size become image size issue, as well as aspect ratio issue (SwiftUI's Bug)
|
||||
+ (void)load {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Class class = NSClassFromString(@"SPInterfaceGroupView");
|
||||
// Implements `intrinsicContentSize`
|
||||
SEL selector = @selector(intrinsicContentSize);
|
||||
Method method = class_getInstanceMethod(class, selector);
|
||||
|
||||
BOOL didAddMethod =
|
||||
class_addMethod(class,
|
||||
selector,
|
||||
(IMP)intrinsicContentSizeIMP,
|
||||
method_getTypeEncoding(method));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
}
|
||||
|
||||
// Override `layoutSubviews`
|
||||
SEL originalSelector = @selector(layoutSubviews);
|
||||
SEL swizzledSelector = NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_layoutSubviews);
|
||||
Method originalMethod = class_getInstanceMethod(class, originalSelector);
|
||||
|
||||
didAddMethod =
|
||||
class_addMethod(class,
|
||||
swizzledSelector,
|
||||
(IMP)layoutSubviewsIMP,
|
||||
method_getTypeEncoding(originalMethod));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
} else {
|
||||
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
}
|
||||
|
||||
// Override `sizeThatFits:`
|
||||
originalSelector = @selector(sizeThatFits:);
|
||||
swizzledSelector = NSSelectorFromString(SDAnimatedImageInterfaceWrapperSEL_sizeThatFits);
|
||||
originalMethod = class_getInstanceMethod(class, originalSelector);
|
||||
|
||||
didAddMethod =
|
||||
class_addMethod(class,
|
||||
swizzledSelector,
|
||||
(IMP)sizeThatFitsIMP,
|
||||
method_getTypeEncoding(originalMethod));
|
||||
if (!didAddMethod) {
|
||||
NSAssert(NO, @"SDAnimatedImageInterfaceWrapper will not work as expected.");
|
||||
} else {
|
||||
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
Class cls = [self class];
|
||||
NSString *UUID = [NSUUID UUID].UUIDString;
|
||||
NSString *property = [NSString stringWithFormat:@"%@_%@", cls, UUID];
|
||||
self = [self _initForDynamicCreationWithInterfaceProperty:property];
|
||||
if (self) {
|
||||
self.wrapped = [[SDAnimatedImageInterface alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)interfaceDescriptionForDynamicCreation {
|
||||
// This is called by WatchKit to provide default value
|
||||
return @{
|
||||
@"type" : @"group",
|
||||
@"property" : self.interfaceProperty,
|
||||
@"radius" : @(0),
|
||||
@"items": @[self.wrapped.interfaceDescriptionForDynamicCreation], // This will create the native view and added to subview
|
||||
};
|
||||
}
|
||||
|
||||
- (void)set_interfaceView:(id<UIViewProtocol>)interfaceView {
|
||||
// This is called by WatchKit when native view created
|
||||
[super set_interfaceView:interfaceView];
|
||||
// Bind the interface object and native view
|
||||
interfaceView.tag = SDAnimatedImageInterfaceWrapperTag;
|
||||
self.wrapped._interfaceView = interfaceView.subviews.firstObject;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SDWebImageSwiftUI/SDAnimatedImageInterface.h>
|
||||
#import <SDWebImageSwiftUI/SDAnimatedImageInterfaceWrapper.h>
|
||||
|
||||
//! Project version number for SDWebImageSwiftUI.
|
||||
FOUNDATION_EXPORT double SDWebImageSwiftUIVersionNumber;
|
||||
|
|
Loading…
Reference in New Issue