Skip to content

fix(iOS): Images don't render on iOS 14 with RN < 0.63.2 #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 17, 2020
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
16 changes: 8 additions & 8 deletions ios/ReactTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
objects = {

/* Begin PBXBuildFile section */
1914199A234B2DD800D856AE /* RCTDevSupport+UIScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 19141999234B2DD800D856AE /* RCTDevSupport+UIScene.m */; };
19ECD0D6232ED425003D8557 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0D5232ED425003D8557 /* AppDelegate.swift */; };
19ECD0DA232ED425003D8557 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0D9232ED425003D8557 /* ContentView.swift */; };
192DD201240FCAF5004E9CEB /* Manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 192DD200240FCAF5004E9CEB /* Manifest.swift */; };
196C22622490CB7600449D3C /* React+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 196C22602490CB7600449D3C /* React+Compatibility.m */; };
196C7215232F1788006556ED /* ReactInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196C7214232F1788006556ED /* ReactInstance.swift */; };
196C724123319A85006556ED /* QRCodeReaderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196C724023319A85006556ED /* QRCodeReaderDelegate.swift */; };
1988284524105BEC005057FF /* UIViewController+ReactTestApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 1988284424105BEC005057FF /* UIViewController+ReactTestApp.m */; };
19ECD0D6232ED425003D8557 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0D5232ED425003D8557 /* AppDelegate.swift */; };
196C7215232F1788006556ED /* ReactInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196C7214232F1788006556ED /* ReactInstance.swift */; };
19ECD0D8232ED425003D8557 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0D7232ED425003D8557 /* SceneDelegate.swift */; };
19ECD0DA232ED425003D8557 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0D9232ED425003D8557 /* ContentView.swift */; };
1914199A234B2DD800D856AE /* RCTDevSupport+UIScene.m in Sources */ = {isa = PBXBuildFile; fileRef = 19141999234B2DD800D856AE /* RCTDevSupport+UIScene.m */; };
196C22622490CB7600449D3C /* React+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 196C22602490CB7600449D3C /* React+Compatibility.m */; };
1988284524105BEC005057FF /* UIViewController+ReactTestApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 1988284424105BEC005057FF /* UIViewController+ReactTestApp.m */; };
19ECD0DC232ED427003D8557 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 19ECD0DB232ED427003D8557 /* Assets.xcassets */; };
19ECD0E2232ED427003D8557 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 19ECD0E0232ED427003D8557 /* LaunchScreen.storyboard */; };
19ECD0ED232ED428003D8557 /* ReactTestAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19ECD0EC232ED428003D8557 /* ReactTestAppTests.swift */; };
Expand Down Expand Up @@ -220,8 +220,8 @@
19ECD0CA232ED425003D8557 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1100;
LastUpgradeCheck = 1220;
LastSwiftUpdateCheck = 1230;
LastUpgradeCheck = 1230;
ORGANIZATIONNAME = Microsoft;
TargetAttributes = {
19ECD0D1232ED425003D8557 = {
Expand Down
37 changes: 10 additions & 27 deletions ios/ReactTestApp/RCTDevSupport+UIScene.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,9 @@
#import <React/RCTUtils.h>
#import <React/RCTVersion.h>

#if DEBUG
#import "React+Compatibility.h"

void swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector)
{
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
#if DEBUG

// MARK: - RCTRedBoxWindow

Expand All @@ -56,11 +39,11 @@ + (void)load
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
swizzleSelector(class,
@selector(showErrorMessage:withStack:isUpdate:),
@selector(rta_showErrorMessage:withStack:isUpdate:));
swizzleSelector(class, @selector(dismiss), @selector(rta_dismiss));
swizzleSelector(class, @selector(makeKeyAndVisible), @selector(rta_makeKeyAndVisible));
RTASwizzleSelector(class,
@selector(showErrorMessage:withStack:isUpdate:),
@selector(rta_showErrorMessage:withStack:isUpdate:));
RTASwizzleSelector(class, @selector(dismiss), @selector(rta_dismiss));
RTASwizzleSelector(class, @selector(makeKeyAndVisible), @selector(rta_makeKeyAndVisible));
});
}

Expand Down Expand Up @@ -103,9 +86,9 @@ + (void)initialize
}

if (@available(iOS 13.0, *)) {
swizzleSelector([self class],
@selector(showMessage:color:backgroundColor:),
@selector(rta_showMessage:color:backgroundColor:));
RTASwizzleSelector([self class],
@selector(showMessage:color:backgroundColor:),
@selector(rta_showMessage:color:backgroundColor:));
}
}

Expand Down
2 changes: 2 additions & 0 deletions ios/ReactTestApp/React+Compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@

@class RCTBridge;

IMP RTASwizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector);

void RTATriggerReloadCommand(RCTBridge *, NSString *reason);
86 changes: 86 additions & 0 deletions ios/ReactTestApp/React+Compatibility.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,33 @@

#include <TargetConditionals.h>

#import <objc/runtime.h>

#import <React/RCTBridge.h>

IMP RTASwizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector)
{
Method originalMethod = class_getInstanceMethod(class, originalSelector);
IMP originalImpl = method_getImplementation(originalMethod);

Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
const char *type = method_getTypeEncoding(originalMethod);
class_replaceMethod(class, swizzledSelector, originalImpl, type);
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}

return originalImpl;
}

// MARK: - [0.62] RCTTriggerReloadCommandListeners replaces -[RCTBridge reload]

// `RCTReloadCommand.h` is excluded from `react-native-macos`
// See https://github.com/microsoft/react-native-macos/blob/v0.61.39/React-Core.podspec#L66
#if REACT_NATIVE_VERSION >= 6200
Expand All @@ -25,3 +50,64 @@ void RTATriggerReloadCommand(RCTBridge *bridge, NSString *reason)
RCTTriggerReloadCommandListeners(reason);
#endif
}

// MARK: - [0.63.2] Images do not render on iOS 14
// See https://github.com/facebook/react-native/pull/29420

#if !TARGET_OS_OSX && REACT_NATIVE_VERSION < 6302

#import <React/RCTUIImageViewAnimated.h>

@implementation RCTUIImageViewAnimated (ReactTestApp)

static void (*orig_displayLayer)(id, SEL, CALayer *);

+ (void)initialize
{
if ([self class] != [RCTUIImageViewAnimated class]) {
return;
}

if (@available(iOS 14.0, *)) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
IMP impl = RTASwizzleSelector(
[self class], @selector(displayLayer:), @selector(rta_displayLayer:));
orig_displayLayer = (void (*)(id, SEL, CALayer *))impl;
});
}
}

- (void)rta_displayLayer:(CALayer *)layer
{
/* The fix for images not rendering is to let UIImageView handle it when we
* are not animating. The following change was made in react-native#29420:
*
* diff --git a/Libraries/Image/RCTUIImageViewAnimated.m b/Libraries/Image/RCTUIImageViewAnimated.m
* index 93c6a2f02f5..f6fb5bc60cc 100644
* --- a/Libraries/Image/RCTUIImageViewAnimated.m
* +++ b/Libraries/Image/RCTUIImageViewAnimated.m
* @@ -285,6 +285,8 @@ static NSUInteger RCTDeviceFreeMemory() {
* if (_currentFrame) {
* layer.contentsScale = self.animatedImageScale;
* layer.contents = (__bridge id)_currentFrame.CGImage;
* + } else {
* + [super displayLayer:layer];
* }
* }
*
* The patch calls `super` when `_currentFrame` is `nil`. For our monkey
* patch, we'll invert the logic to let the original method handle the case
* where `_currentFrame` is missing.
*/
if ([self respondsToSelector:@selector(currentFrame)] &&
[self performSelector:@selector(currentFrame)] == nil) {
[super displayLayer:layer];
} else {
orig_displayLayer(self, @selector(displayLayer:), layer);
}
}

@end

#endif
12 changes: 6 additions & 6 deletions macos/ReactTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

/* Begin PBXBuildFile section */
193EF063247A736200BE8C79 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF062247A736200BE8C79 /* AppDelegate.swift */; };
193EF065247A736200BE8C79 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF064247A736200BE8C79 /* ViewController.swift */; };
193EF067247A736300BE8C79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 193EF066247A736300BE8C79 /* Assets.xcassets */; };
193EF06A247A736300BE8C79 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 193EF068247A736300BE8C79 /* Main.storyboard */; };
193EF08F247A799D00BE8C79 /* Manifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF08E247A799D00BE8C79 /* Manifest.swift */; };
193EF093247A830200BE8C79 /* UIViewController+ReactTestApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 193EF091247A830200BE8C79 /* UIViewController+ReactTestApp.m */; };
193EF098247B130700BE8C79 /* ReactInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF097247B130700BE8C79 /* ReactInstance.swift */; };
193EF065247A736200BE8C79 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193EF064247A736200BE8C79 /* ViewController.swift */; };
196C22652490CBAB00449D3C /* React+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 196C22632490CBAB00449D3C /* React+Compatibility.m */; };
193EF093247A830200BE8C79 /* UIViewController+ReactTestApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 193EF091247A830200BE8C79 /* UIViewController+ReactTestApp.m */; };
193EF067247A736300BE8C79 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 193EF066247A736300BE8C79 /* Assets.xcassets */; };
193EF06A247A736300BE8C79 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 193EF068247A736300BE8C79 /* Main.storyboard */; };
19E791C024B08E1400FA6468 /* ReactTestAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E791BF24B08E1400FA6468 /* ReactTestAppTests.swift */; };
19E791C324B08E4D00FA6468 /* ReactTestAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E791C224B08E4D00FA6468 /* ReactTestAppUITests.swift */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -211,8 +211,8 @@
193EF057247A736100BE8C79 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1150;
LastUpgradeCheck = 1220;
LastSwiftUpdateCheck = 1230;
LastUpgradeCheck = 1230;
ORGANIZATIONNAME = Microsoft;
TargetAttributes = {
193EF05E247A736100BE8C79 = {
Expand Down