22#import < React/RCTConvert.h>
33#import < React/RCTFont.h>
44#import < React/RCTImageSource.h>
5- #import < objc/runtime.h >
5+ #import " RCTImageSource+AccessHiddenMembers.h "
66#import " RNSDefines.h"
77
88@implementation RNSBarButtonItem {
@@ -20,48 +20,36 @@ - (instancetype)initWithConfig:(NSDictionary<NSString *, id> *)dict
2020 return self;
2121 }
2222
23- self.title = dict[@" title" ];
24-
23+ NSString *title = dict[@" title" ];
2524 NSDictionary *imageSourceObj = dict[@" imageSource" ];
2625 NSDictionary *templateSourceObj = dict[@" templateSource" ];
27-
28- RCTImageSource *imageSource = nil ;
29- if (imageSourceObj) {
30- imageSource = [RCTConvert RCTImageSource: imageSourceObj];
31- } else if (templateSourceObj) {
32- imageSource = [RCTConvert RCTImageSource: templateSourceObj];
33- }
34-
35- if (imageSource) {
36- [imageLoader loadImageWithURLRequest: imageSource.request
37- size: imageSource.size
38- scale: imageSource.scale
39- clipped: true
40- resizeMode: RCTResizeModeContain
41- progressBlock: ^(int64_t progress, int64_t total) {
42- }
43- partialLoadBlock: ^(UIImage *_Nonnull image) {
44- }
45- completionBlock: ^(NSError *_Nullable error, UIImage *_Nullable image) {
46- dispatch_async (dispatch_get_main_queue (), ^{
47- UIImage *imageWithRenderingMode = nil ;
48- if (imageSourceObj) {
49- imageWithRenderingMode = [image imageWithRenderingMode: UIImageRenderingModeAlwaysOriginal];
50- } else if (templateSourceObj) {
51- imageWithRenderingMode = [image imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate];
52- }
53- self.image = imageWithRenderingMode;
54- });
55- }];
56- }
5726 NSString *sfSymbolName = dict[@" sfSymbolName" ];
58- if (sfSymbolName) {
27+
28+ if (imageSourceObj != nil ) {
29+ void (^completionAction)(NSError *_Nullable, UIImage *_Nullable) =
30+ ^(NSError *_Nullable error, UIImage *_Nullable image) {
31+ self.image = [image imageWithRenderingMode: UIImageRenderingModeAlwaysOriginal];
32+ };
33+ [self loadImageSyncFromImageSourceJson: imageSourceObj withImageLoader: imageLoader completionBlock: completionAction];
34+ } else if (templateSourceObj != nil ) {
35+ void (^completionAction)(NSError *_Nullable, UIImage *_Nullable) =
36+ ^(NSError *_Nullable error, UIImage *_Nullable image) {
37+ self.image = [image imageWithRenderingMode: UIImageRenderingModeAlwaysTemplate];
38+ };
39+ [self loadImageSyncFromImageSourceJson: templateSourceObj
40+ withImageLoader: imageLoader
41+ completionBlock: completionAction];
42+ } else if (sfSymbolName != nil ) {
5943 self.image = [UIImage systemImageNamed: sfSymbolName];
6044 }
6145
62- NSDictionary *titleStyle = dict[@" titleStyle" ];
63- if (titleStyle) {
64- [self setTitleStyleFromConfig: titleStyle];
46+ if (title != nil ) {
47+ self.title = title;
48+
49+ NSDictionary *titleStyle = dict[@" titleStyle" ];
50+ if (titleStyle != nil ) {
51+ [self setTitleStyleFromConfig: titleStyle];
52+ }
6553 }
6654
6755 id tintColorObj = dict[@" tintColor" ];
@@ -339,4 +327,55 @@ - (void)setBadgeFromConfig:(NSDictionary *)badgeObj
339327}
340328#endif
341329
330+ /* *
331+ * Should be called from UI thread only. If done so, the method **tries** to load the image synchronously.
332+ * There is no guarantee, because in release mode we rely on `RCTImageLoader` implementation details.
333+ * No matter how the image is loaded, `completionBlock` is executed on main queue.
334+ */
335+ - (void )loadImageSyncFromImageSourceJson : (nonnull NSDictionary *)imageSourceJson
336+ withImageLoader : (nullable RCTImageLoader *)imageLoader
337+ completionBlock :
338+ (void (^_Nonnull)(NSError *_Nullable error, UIImage *_Nullable image))completion
339+ {
340+ RCTAssert (RCTIsMainQueue (), @" [RNScreens] Expected to run on main queue" );
341+
342+ RCTImageSource *imageSource = [RCTConvert RCTImageSource: imageSourceJson];
343+ RCTAssert (imageSource != nil , @" [RNScreens] Expected nonnill image source" );
344+
345+ // We use `+ [RCTConvert UIImage:]` only in debug mode, because it is deprecated, however
346+ // I haven't found different way to load image synchronously in debug other than
347+ // writing the code manually.
348+
349+ #if !defined(NDEBUG) // We're in debug mode here
350+ if (!imageSource.packagerAsset ) {
351+ // This is rather unexpected. In debug mode local asset should be sourced from packager.
352+ RCTLogWarn (@" [RNScreens] Unexpected case during image load: loading not a packager asset" );
353+ }
354+
355+ // Try to load anyway.
356+ UIImage *loadedImage = [RCTConvert UIImage: imageSourceJson];
357+ completion (nil , loadedImage);
358+ return ;
359+ #else // We're in release mode here
360+ [imageLoader loadImageWithURLRequest: imageSource.request
361+ size: imageSource.size
362+ scale: imageSource.scale
363+ clipped: true
364+ resizeMode: RCTResizeModeContain
365+ progressBlock: ^(int64_t progress, int64_t total) {
366+ }
367+ partialLoadBlock: ^(UIImage *_Nonnull image) {
368+ }
369+ completionBlock: ^(NSError *_Nullable error, UIImage *_Nullable image) {
370+ if (RCTIsMainQueue ()) {
371+ completion (error, image);
372+ } else {
373+ dispatch_async (dispatch_get_main_queue (), ^{
374+ completion (error, image);
375+ });
376+ }
377+ }];
378+ #endif
379+ }
380+
342381@end
0 commit comments