diff --git a/SDWebImageSwiftUI.xcodeproj/project.pbxproj b/SDWebImageSwiftUI.xcodeproj/project.pbxproj index 916f00d..3781f14 100644 --- a/SDWebImageSwiftUI.xcodeproj/project.pbxproj +++ b/SDWebImageSwiftUI.xcodeproj/project.pbxproj @@ -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 = ""; }; 326B848B236335400011BDFB /* ProgressIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; }; 326E480923431C0F00C633E9 /* ImageViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewWrapper.swift; sourceTree = ""; }; + 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDAnimatedImageInterfaceWrapper.h; sourceTree = ""; }; + 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDAnimatedImageInterfaceWrapper.m; sourceTree = ""; }; 32B933E423659A1900BB7CAD /* Transition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transition.swift; sourceTree = ""; }; 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 = ""; }; @@ -177,6 +187,8 @@ children = ( 324F61C5235E07EC003973B8 /* SDAnimatedImageInterface.h */, 324F61C6235E07EC003973B8 /* SDAnimatedImageInterface.m */, + 3276EAFE237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.h */, + 3276EAFF237760D800B385D4 /* SDAnimatedImageInterfaceWrapper.m */, ); path = ObjC; sourceTree = ""; @@ -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 */, diff --git a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h index d47c49b..49d43bb 100644 --- a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h +++ b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.h @@ -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 diff --git a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m index c4d95b1..23c2e2c 100644 --- a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m +++ b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterface.m @@ -9,9 +9,6 @@ #import "SDAnimatedImageInterface.h" #if SD_WATCH -#import -#import - #pragma mark - SPI @protocol CALayerProtocol @@ -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)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 _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 self, SEL _cmd) { - struct objc_super superClass = { - self, - [self superclass] - }; - NSUInteger tag = self.tag; - id 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 self, SEL _cmd) { - struct objc_super superClass = { - self, - [self superclass] - }; - NSUInteger tag = self.tag; - id 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 self, SEL _cmd, CGSize size) { - NSUInteger tag = self.tag; - id 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)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 diff --git a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.h b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.h new file mode 100644 index 0000000..01cad8e --- /dev/null +++ b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.h @@ -0,0 +1,25 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* 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 diff --git a/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.m b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.m new file mode 100644 index 0000000..58eb739 --- /dev/null +++ b/SDWebImageSwiftUI/Classes/ObjC/SDAnimatedImageInterfaceWrapper.m @@ -0,0 +1,199 @@ +/* +* This file is part of the SDWebImage package. +* (c) DreamPiggy +* +* 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 +#import + +#pragma mark - SPI + +@protocol CALayerProtocol +@property (nullable, strong) id contents; +@property CGFloat contentsScale; +@end + +@protocol UIViewProtocol +@property (nonatomic, strong, readonly) id layer; +@property (nonatomic, assign) SDImageScaleMode contentMode; +@property (nonatomic, readonly) id superview; +@property (nonatomic, readonly, copy) NSArray> *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 + +@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 _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 self, SEL _cmd) { + struct objc_super superClass = { + self, + [self superclass] + }; + NSUInteger tag = self.tag; + id 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 self, SEL _cmd) { + struct objc_super superClass = { + self, + [self superclass] + }; + NSUInteger tag = self.tag; + id 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 self, SEL _cmd, CGSize size) { + NSUInteger tag = self.tag; + id 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)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 diff --git a/SDWebImageSwiftUI/Module/SDWebImageSwiftUI.h b/SDWebImageSwiftUI/Module/SDWebImageSwiftUI.h index 958fe3e..4fc701b 100644 --- a/SDWebImageSwiftUI/Module/SDWebImageSwiftUI.h +++ b/SDWebImageSwiftUI/Module/SDWebImageSwiftUI.h @@ -8,6 +8,7 @@ #import #import +#import //! Project version number for SDWebImageSwiftUI. FOUNDATION_EXPORT double SDWebImageSwiftUIVersionNumber;