diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 27751eeef..c986d4af3 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -81,7 +81,7 @@ 352DCFF01D19F4C20056D623 /* RCTI18nUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DCFEF1D19F4C20056D623 /* RCTI18nUtil.m */; }; 391E86A41C623EC800009732 /* RCTTouchEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 391E86A21C623EC800009732 /* RCTTouchEvent.m */; }; 3D1E68DB1CABD13900DD7465 /* RCTDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D1E68D91CABD13900DD7465 /* RCTDisplayLink.m */; }; - 3D37B5821D522B190042D5B5 /* RCTFont.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D37B5811D522B190042D5B5 /* RCTFont.m */; }; + 3D37B5821D522B190042D5B5 /* RCTFont.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D37B5811D522B190042D5B5 /* RCTFont.mm */; }; 3EDCA8A51D3591E700450C31 /* RCTErrorInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCA8A41D3591E700450C31 /* RCTErrorInfo.m */; }; 58114A161AAE854800E7D092 /* RCTPicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A131AAE854800E7D092 /* RCTPicker.m */; }; 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A151AAE854800E7D092 /* RCTPickerManager.m */; }; @@ -279,7 +279,7 @@ 3D1E68D81CABD13900DD7465 /* RCTDisplayLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDisplayLink.h; sourceTree = ""; }; 3D1E68D91CABD13900DD7465 /* RCTDisplayLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDisplayLink.m; sourceTree = ""; }; 3D37B5801D522B190042D5B5 /* RCTFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFont.h; sourceTree = ""; }; - 3D37B5811D522B190042D5B5 /* RCTFont.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFont.m; sourceTree = ""; }; + 3D37B5811D522B190042D5B5 /* RCTFont.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTFont.mm; sourceTree = ""; }; 3DB910701C74B21600838BBE /* RCTWebSocketProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketProxy.h; sourceTree = ""; }; 3DB910711C74B21600838BBE /* RCTWebSocketProxyDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebSocketProxyDelegate.h; sourceTree = ""; }; 3EDCA8A21D3591E700450C31 /* RCTErrorCustomizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTErrorCustomizer.h; sourceTree = ""; }; @@ -435,7 +435,7 @@ 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */, 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */, 3D37B5801D522B190042D5B5 /* RCTFont.h */, - 3D37B5811D522B190042D5B5 /* RCTFont.m */, + 3D37B5811D522B190042D5B5 /* RCTFont.mm */, 14435CE11AAC4AE100FC20F4 /* RCTMap.h */, 14435CE21AAC4AE100FC20F4 /* RCTMap.m */, 13B202021BFB948C00C07393 /* RCTMapAnnotation.h */, @@ -757,7 +757,7 @@ 14F7A0EC1BDA3B3C003C6C10 /* RCTPerfMonitor.m in Sources */, 1450FF881BCFF28A00208362 /* RCTProfileTrampoline-arm64.S in Sources */, 13E41EEB1C05CA0B00CD8DAC /* RCTProfileTrampoline-i386.S in Sources */, - 3D37B5821D522B190042D5B5 /* RCTFont.m in Sources */, + 3D37B5821D522B190042D5B5 /* RCTFont.mm in Sources */, 13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */, 14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */, 137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */, diff --git a/React/Views/RCTFont.m b/React/Views/RCTFont.mm similarity index 87% rename from React/Views/RCTFont.m rename to React/Views/RCTFont.mm index 32f8b7739..f023a09c7 100644 --- a/React/Views/RCTFont.m +++ b/React/Views/RCTFont.mm @@ -9,6 +9,8 @@ #import "RCTFont.h" +#import + typedef CGFloat RCTFontWeight; static RCTFontWeight weightOfFont(UIFont *font) { @@ -56,6 +58,46 @@ static BOOL isCondensedFont(UIFont *font) return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0; } +static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight) +{ + static NSCache *fontCache; + static std::mutex fontCacheMutex; + + NSString *cacheKey = [NSString stringWithFormat:@"%.1f/%.2f", size, weight]; + UIFont *font; + { + std::lock_guard lock(fontCacheMutex); + if (!fontCache) { + fontCache = [NSCache new]; + } + font = [fontCache objectForKey:cacheKey]; + } + + if (!font) { + // Only supported on iOS8.2 and above + if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { + font = [UIFont systemFontOfSize:size weight:weight]; + } else { + if (weight >= UIFontWeightBold) { + font = [UIFont boldSystemFontOfSize:size]; + } else if (weight >= UIFontWeightMedium) { + font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:size]; + } else if (weight <= UIFontWeightLight) { + font = [UIFont fontWithName:@"HelveticaNeue-Light" size:size]; + } else { + font = [UIFont systemFontOfSize:size]; + } + } + + { + std::lock_guard lock(fontCacheMutex); + [fontCache setObject:font forKey:cacheKey]; + } + } + + return font; +} + @implementation RCTConvert (RCTFont) + (UIFont *)UIFont:(id)json @@ -137,8 +179,8 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{ // Handle system font as special case. This ensures that we preserve // the specific metrics of the standard system font as closely as possible. if ([familyName isEqual:defaultFontFamily] || [familyName isEqualToString:@"System"]) { - if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) { - font = [UIFont systemFontOfSize:fontSize weight:fontWeight]; + font = cachedSystemFont(fontSize, fontWeight); + if (font) { if (isItalic || isCondensed) { UIFontDescriptor *fontDescriptor = [font fontDescriptor]; UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;