Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions apps/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import Orientation from './src/screens/Orientation';
import SearchBar from './src/screens/SearchBar';
import Events from './src/screens/Events';
import Gestures from './src/screens/Gestures';
// import BarButtonItems from './src/screens/BarButtonItems';
import BarButtonItems from './src/screens/BarButtonItems';

import { GestureDetectorProvider } from 'react-native-screens/gesture-handler';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
Expand Down Expand Up @@ -128,11 +128,11 @@ const SCREENS: Record<
component: Gestures,
type: 'playground',
},
// BarButtonItems: {
// title: 'Bar Button Items',
// component: BarButtonItems,
// type: 'playground',
// },
BarButtonItems: {
title: 'Bar Button Items',
component: BarButtonItems,
type: 'playground',
},
};

if (isTestSectionEnabled()) {
Expand Down
113 changes: 76 additions & 37 deletions ios/RNSBarButtonItem.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#import <React/RCTConvert.h>
#import <React/RCTFont.h>
#import <React/RCTImageSource.h>
#import <objc/runtime.h>
#import "RCTImageSource+AccessHiddenMembers.h"
#import "RNSDefines.h"

@implementation RNSBarButtonItem {
Expand All @@ -20,48 +20,36 @@ - (instancetype)initWithConfig:(NSDictionary<NSString *, id> *)dict
return self;
}

self.title = dict[@"title"];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you resign from setting title here and postponed it for later? Was there a particular reason here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to set the title before remote images had been downloaded

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But shouldn't be needed anymore since from what I understand also remote images will be loaded synchronously in production mode?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only assets stored on the device will be loaded synchronously. If you need to download header button images from some remote source during runtime (is this a valid use case?) it'll be done asynchronously. Packager (metro) assets are treated like device-local assets, so they're exception to that rule & will be loaded synchronously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't think its a valid use case. Just something to be aware of that the title will be shown until the image is loaded then if the download is done asynchronously.


NSString *title = dict[@"title"];
NSDictionary *imageSourceObj = dict[@"imageSource"];
NSDictionary *templateSourceObj = dict[@"templateSource"];

RCTImageSource *imageSource = nil;
if (imageSourceObj) {
imageSource = [RCTConvert RCTImageSource:imageSourceObj];
} else if (templateSourceObj) {
imageSource = [RCTConvert RCTImageSource:templateSourceObj];
}

if (imageSource) {
[imageLoader loadImageWithURLRequest:imageSource.request
size:imageSource.size
scale:imageSource.scale
clipped:true
resizeMode:RCTResizeModeContain
progressBlock:^(int64_t progress, int64_t total) {
}
partialLoadBlock:^(UIImage *_Nonnull image) {
}
completionBlock:^(NSError *_Nullable error, UIImage *_Nullable image) {
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *imageWithRenderingMode = nil;
if (imageSourceObj) {
imageWithRenderingMode = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
} else if (templateSourceObj) {
imageWithRenderingMode = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
self.image = imageWithRenderingMode;
});
}];
}
NSString *sfSymbolName = dict[@"sfSymbolName"];
if (sfSymbolName) {

if (imageSourceObj != nil) {
void (^completionAction)(NSError *_Nullable, UIImage *_Nullable) =
^(NSError *_Nullable error, UIImage *_Nullable image) {
self.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
};
[self loadImageSyncFromImageSourceJson:imageSourceObj withImageLoader:imageLoader completionBlock:completionAction];
} else if (templateSourceObj != nil) {
void (^completionAction)(NSError *_Nullable, UIImage *_Nullable) =
^(NSError *_Nullable error, UIImage *_Nullable image) {
self.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
};
[self loadImageSyncFromImageSourceJson:templateSourceObj
withImageLoader:imageLoader
completionBlock:completionAction];
} else if (sfSymbolName != nil) {
self.image = [UIImage systemImageNamed:sfSymbolName];
}

NSDictionary *titleStyle = dict[@"titleStyle"];
if (titleStyle) {
[self setTitleStyleFromConfig:titleStyle];
if (title != nil) {
self.title = title;

NSDictionary *titleStyle = dict[@"titleStyle"];
if (titleStyle != nil) {
[self setTitleStyleFromConfig:titleStyle];
}
}

id tintColorObj = dict[@"tintColor"];
Expand Down Expand Up @@ -339,4 +327,55 @@ - (void)setBadgeFromConfig:(NSDictionary *)badgeObj
}
#endif

/**
* Should be called from UI thread only. If done so, the method **tries** to load the image synchronously.
* There is no guarantee, because in release mode we rely on `RCTImageLoader` implementation details.
* No matter how the image is loaded, `completionBlock` is executed on main queue.
*/
- (void)loadImageSyncFromImageSourceJson:(nonnull NSDictionary *)imageSourceJson
withImageLoader:(nullable RCTImageLoader *)imageLoader
completionBlock:
(void (^_Nonnull)(NSError *_Nullable error, UIImage *_Nullable image))completion
{
RCTAssert(RCTIsMainQueue(), @"[RNScreens] Expected to run on main queue");

RCTImageSource *imageSource = [RCTConvert RCTImageSource:imageSourceJson];
RCTAssert(imageSource != nil, @"[RNScreens] Expected nonnill image source");

// We use `+ [RCTConvert UIImage:]` only in debug mode, because it is deprecated, however
// I haven't found different way to load image synchronously in debug other than
// writing the code manually.

#if !defined(NDEBUG) // We're in debug mode here
if (!imageSource.packagerAsset) {
// This is rather unexpected. In debug mode local asset should be sourced from packager.
RCTLogWarn(@"[RNScreens] Unexpected case during image load: loading not a packager asset");
}

// Try to load anyway.
UIImage *loadedImage = [RCTConvert UIImage:imageSourceJson];
completion(nil, loadedImage);
return;
#else // We're in release mode here
[imageLoader loadImageWithURLRequest:imageSource.request
size:imageSource.size
scale:imageSource.scale
clipped:true
resizeMode:RCTResizeModeContain
progressBlock:^(int64_t progress, int64_t total) {
}
partialLoadBlock:^(UIImage *_Nonnull image) {
}
completionBlock:^(NSError *_Nullable error, UIImage *_Nullable image) {
if (RCTIsMainQueue()) {
completion(error, image);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
completion(error, image);
});
}
}];
#endif
}

@end
9 changes: 9 additions & 0 deletions ios/utils/extensions/RCTImageSource+AccessHiddenMembers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

// This field should exist in extension in `RCTImageSource.m`

@interface RCTImageSource (AccessHiddenMembers)

@property (nonatomic, assign) BOOL packagerAsset;

@end
2 changes: 1 addition & 1 deletion react-navigation
Submodule react-navigation updated 295 files
6 changes: 3 additions & 3 deletions src/gesture-handler/fabricUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import { View } from "react-native";
import { View } from 'react-native';

/* eslint-disable */

Expand All @@ -24,11 +24,11 @@ export function getShadowNodeWrapperAndTagFromRef(ref: View | null): {
return {
shadowNodeWrapper: null,
tag: -1,
}
};
}
const internalRef = ref as unknown as HostInstance;
return {
shadowNodeWrapper: internalRef.__internalInstanceHandle.stateNode.node,
tag: internalRef.__nativeTag,
}
};
}
Loading