From b07a29d42f987dbd6bfcab722f83674512fe1118 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 17 Jul 2024 18:27:35 +0800 Subject: [PATCH] feat(ios): add HippyFontChangeTriggerNotification for native font update --- ios/sdk/base/HippyFont.h | 10 ++- ios/sdk/base/HippyFont.mm | 24 +++--- ios/sdk/base/HippyRootView.mm | 12 --- .../component/textinput/HippyBaseTextInput.h | 10 +++ .../component/textinput/HippyBaseTextInput.m | 54 +++++++++++++ .../component/textinput/HippyShadowTextView.h | 11 ++- .../textinput/HippyShadowTextView.mm | 76 +++++++++++++++++++ .../textinput/HippyTextViewManager.mm | 66 +++------------- ios/sdk/module/uimanager/HippyUIManager.h | 19 +++-- ios/sdk/module/uimanager/HippyUIManager.mm | 55 +++++++++----- 10 files changed, 221 insertions(+), 116 deletions(-) diff --git a/ios/sdk/base/HippyFont.h b/ios/sdk/base/HippyFont.h index 60683010a47..9d2ae10b925 100644 --- a/ios/sdk/base/HippyFont.h +++ b/ios/sdk/base/HippyFont.h @@ -39,10 +39,12 @@ variant:(NSArray *)variant scaleMultiplier:(CGFloat)scaleMultiplier; -+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family; -+ (UIFont *)updateFont:(UIFont *)font withSize:(NSNumber *)size; -+ (UIFont *)updateFont:(UIFont *)font withWeight:(NSString *)weight; -+ (UIFont *)updateFont:(UIFont *)font withStyle:(NSString *)style; +/// Get the +/// JS side usually pass a `fontName` instead of `fontFamily` +/// If not match, the original value is returned. +/// +/// - Parameter fontFamily: NSString * ++ (NSString *)familyNameWithCSSNameMatching:(NSString *)fontName; @end diff --git a/ios/sdk/base/HippyFont.mm b/ios/sdk/base/HippyFont.mm index cfa09fd7e6b..50a65c2911c 100644 --- a/ios/sdk/base/HippyFont.mm +++ b/ios/sdk/base/HippyFont.mm @@ -307,20 +307,16 @@ + (UIFont *)updateFont:(UIFont *)font return font; } -+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family { - return [self updateFont:font withFamily:family size:nil weight:nil style:nil variant:nil scaleMultiplier:1]; -} - -+ (UIFont *)updateFont:(UIFont *)font withSize:(NSNumber *)size { - return [self updateFont:font withFamily:nil size:size weight:nil style:nil variant:nil scaleMultiplier:1]; -} - -+ (UIFont *)updateFont:(UIFont *)font withWeight:(NSString *)weight { - return [self updateFont:font withFamily:nil size:nil weight:weight style:nil variant:nil scaleMultiplier:1]; -} - -+ (UIFont *)updateFont:(UIFont *)font withStyle:(NSString *)style { - return [self updateFont:font withFamily:nil size:nil weight:nil style:style variant:nil scaleMultiplier:1]; ++ (NSString *)familyNameWithCSSNameMatching:(NSString *)fontName { + NSString *familyName = fontName; + if (fontName && ![[UIFont familyNames] containsObject:fontName]) { + // Not a real FamilyName + // Using CSS name matching semantics. + // fontSize here is just a placeholder for getting font. + UIFont *cssFont = [UIFont fontWithName:fontName size:14.0]; + familyName = cssFont.familyName; + } + return familyName; } @end diff --git a/ios/sdk/base/HippyRootView.mm b/ios/sdk/base/HippyRootView.mm index 744fd26e3c7..6fcdb409baf 100644 --- a/ios/sdk/base/HippyRootView.mm +++ b/ios/sdk/base/HippyRootView.mm @@ -436,7 +436,6 @@ - (NSNumber *)allocateRootTag @implementation HippyRootContentView { __weak HippyBridge *_bridge; - UIColor *_backgroundColor; } - (instancetype)initWithFrame:(CGRect)frame @@ -484,17 +483,6 @@ - (void)setFrame:(CGRect)frame { } } -- (void)setBackgroundColor:(UIColor *)backgroundColor { - _backgroundColor = backgroundColor; - if (self.hippyTag && _bridge.isValid) { - [_bridge.uiManager setBackgroundColor:backgroundColor forView:self]; - } -} - -- (UIColor *)backgroundColor { - return _backgroundColor; -} - - (void)invalidate { if (self.userInteractionEnabled) { self.userInteractionEnabled = NO; diff --git a/ios/sdk/component/textinput/HippyBaseTextInput.h b/ios/sdk/component/textinput/HippyBaseTextInput.h index 1d770a24977..31c3b78d2a5 100644 --- a/ios/sdk/component/textinput/HippyBaseTextInput.h +++ b/ios/sdk/component/textinput/HippyBaseTextInput.h @@ -23,6 +23,16 @@ #import "HippyView.h" @interface HippyBaseTextInput : HippyView + +/// Font property - FontSize +@property (nonatomic, strong) NSNumber *fontSize; +/// Font property - FontWeight +@property (nonatomic, strong) NSString *fontWeight; +/// Font property - FontStyle +@property (nonatomic, strong) NSString *fontStyle; +/// Font property - FontFamily +@property (nonatomic, strong) NSString *fontFamily; + @property (nonatomic, strong) UIFont *font; @property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, copy) NSString *value; diff --git a/ios/sdk/component/textinput/HippyBaseTextInput.m b/ios/sdk/component/textinput/HippyBaseTextInput.m index bca70a752f3..988d0863d77 100644 --- a/ios/sdk/component/textinput/HippyBaseTextInput.m +++ b/ios/sdk/component/textinput/HippyBaseTextInput.m @@ -21,6 +21,7 @@ */ #import "HippyBaseTextInput.h" +#import "HippyFont.h" @implementation HippyBaseTextInput - (void)focus { @@ -42,4 +43,57 @@ - (void)keyboardHeightChanged:(NSNotification *)aNotification { // base method, should be override } + +#pragma mark - Hippy Update Callback + +- (void)hippyBridgeDidFinishTransaction { + // Use this opportunity to update font if needed. + [self layoutIfNeeded]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [self rebuildAndUpdateFont]; +} + + +#pragma mark - Font Related + +- (void)setFontSize:(NSNumber *)fontSize { + _fontSize = fontSize; + [self setNeedsLayout]; +} + +- (void)setFontStyle:(NSString *)fontStyle { + _fontStyle = fontStyle; + [self setNeedsLayout]; +} + +- (void)setFontWeight:(NSString *)fontWeight { + _fontWeight = fontWeight; + [self setNeedsLayout]; +} + +- (void)setFontFamily:(NSString *)fontFamily { + _fontFamily = fontFamily; + [self setNeedsLayout]; +} + +- (void)rebuildAndUpdateFont { + // Convert fontName to fontFamily if needed + CGFloat scaleMultiplier = 1.0; // scale not supported + NSString *familyName = [HippyFont familyNameWithCSSNameMatching:self.fontFamily]; + UIFont *font = [HippyFont updateFont:self.font + withFamily:familyName + size:self.fontSize + weight:self.fontWeight + style:self.fontStyle + variant:nil + scaleMultiplier:scaleMultiplier]; + if (self.font != font) { + self.font = font; + } +} + + @end diff --git a/ios/sdk/component/textinput/HippyShadowTextView.h b/ios/sdk/component/textinput/HippyShadowTextView.h index 7956bae9eda..c14929c629c 100644 --- a/ios/sdk/component/textinput/HippyShadowTextView.h +++ b/ios/sdk/component/textinput/HippyShadowTextView.h @@ -26,8 +26,6 @@ @property (nonatomic, copy) NSString *text; @property (nonatomic, copy) NSString *placeholder; -@property (nonatomic, strong) UIFont *font; - /// ParagraphStyles - lineHeight @property (nonatomic, strong) NSNumber *lineHeight; /// ParagraphStyles - lineSpacing @@ -35,4 +33,13 @@ /// ParagraphStyles - lineHeightMultiple @property (nonatomic, strong) NSNumber *lineHeightMultiple; +/// Font property - FontSize +@property (nonatomic, strong) NSNumber *fontSize; +/// Font property - FontWeight +@property (nonatomic, strong) NSString *fontWeight; +/// Font property - FontStyle +@property (nonatomic, strong) NSString *fontStyle; +/// Font property - FontFamily +@property (nonatomic, strong) NSString *fontFamily; + @end diff --git a/ios/sdk/component/textinput/HippyShadowTextView.mm b/ios/sdk/component/textinput/HippyShadowTextView.mm index 90d02387673..2576f268d71 100644 --- a/ios/sdk/component/textinput/HippyShadowTextView.mm +++ b/ios/sdk/component/textinput/HippyShadowTextView.mm @@ -24,9 +24,21 @@ #import "MTTLayout.h" #import "x5LayoutUtil.h" #import "HippyShadowView+MTTLayout.h" +#import "HippyFont.h" + @interface HippyShadowTextView () + +/// Cached font +@property (nonatomic, strong) UIFont *font; +/// Whether font needs to be updated. +@property (nonatomic, assign) BOOL isFontDirty; +/// Cached attributes @property (nonatomic, strong) NSDictionary *dicAttributes; + +/// rebuild and update the font property +- (void)rebuildAndUpdateFont; + @end static MTTSize x5MeasureFunc( @@ -34,6 +46,11 @@ static MTTSize x5MeasureFunc( HippyShadowTextView *shadowText = (__bridge HippyShadowTextView *)MTTNodeGetContext(node); NSString *text = shadowText.text ?: shadowText.placeholder; if (nil == shadowText.dicAttributes) { + if (shadowText.isFontDirty) { + [shadowText rebuildAndUpdateFont]; + shadowText.isFontDirty = NO; + } + // Keep this historical code, default fontSize 16. if (shadowText.font == nil) { shadowText.font = [UIFont systemFontOfSize:16]; } @@ -95,4 +112,63 @@ - (NSDictionary *)mergeProps:(NSDictionary *)props { return newProps; } +- (void)dirtyText { + [super dirtyText]; + self.isFontDirty = YES; + self.dicAttributes = nil; +} + +- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks + virtualApplierBlocks:(NSMutableSet *)virtualApplierBlocks + parentProperties:(NSDictionary *)parentProperties { + [super collectUpdatedProperties:applierBlocks + virtualApplierBlocks:virtualApplierBlocks + parentProperties:parentProperties]; + + // Set needs layout for font change event, etc. + NSNumber *currentTag = self.hippyTag; + [applierBlocks addObject:^(NSDictionary *viewRegistry){ + UIView *view = viewRegistry[currentTag]; + [view setNeedsLayout]; + }]; +} + + +#pragma mark - Font Related + +- (void)setFontSize:(NSNumber *)fontSize { + _fontSize = fontSize; + self.isFontDirty = YES; +} + +- (void)setFontStyle:(NSString *)fontStyle { + _fontStyle = fontStyle; + self.isFontDirty = YES; +} + +- (void)setFontWeight:(NSString *)fontWeight { + _fontWeight = fontWeight; + self.isFontDirty = YES; +} + +- (void)setFontFamily:(NSString *)fontFamily { + _fontFamily = fontFamily; + self.isFontDirty = YES; +} + +- (void)rebuildAndUpdateFont { + // Convert fontName to fontFamily if needed + NSString *familyName = [HippyFont familyNameWithCSSNameMatching:self.fontFamily]; + UIFont *font = [HippyFont updateFont:self.font + withFamily:familyName + size:self.fontSize + weight:self.fontWeight + style:self.fontStyle + variant:nil + scaleMultiplier:1.0]; + if (self.font != font) { + self.font = font; + } +} + @end diff --git a/ios/sdk/component/textinput/HippyTextViewManager.mm b/ios/sdk/component/textinput/HippyTextViewManager.mm index 8481ab9d8f2..70478e91d51 100644 --- a/ios/sdk/component/textinput/HippyTextViewManager.mm +++ b/ios/sdk/component/textinput/HippyTextViewManager.mm @@ -168,6 +168,11 @@ - (HippyShadowView *)shadowView { HIPPY_EXPORT_SHADOW_PROPERTY(lineSpacing, NSNumber) HIPPY_EXPORT_SHADOW_PROPERTY(lineHeightMultiple, NSNumber) +HIPPY_EXPORT_SHADOW_PROPERTY(fontSize, NSNumber) +HIPPY_EXPORT_SHADOW_PROPERTY(fontWeight, NSString) +HIPPY_EXPORT_SHADOW_PROPERTY(fontStyle, NSString) +HIPPY_EXPORT_SHADOW_PROPERTY(fontFamily, NSString) + HIPPY_EXPORT_VIEW_PROPERTY(lineHeight, NSNumber) HIPPY_EXPORT_VIEW_PROPERTY(lineSpacing, NSNumber) HIPPY_EXPORT_VIEW_PROPERTY(lineHeightMultiple, NSNumber) @@ -195,44 +200,10 @@ - (HippyShadowView *)shadowView { HIPPY_EXPORT_VIEW_PROPERTY(selection, HippyTextSelection) HIPPY_EXPORT_VIEW_PROPERTY(text, NSString) -HIPPY_CUSTOM_SHADOW_PROPERTY(fontSize, NSNumber, HippyShadowTextView) { - view.font = [HippyFont updateFont:view.font withSize:json]; -} - -HIPPY_CUSTOM_SHADOW_PROPERTY(fontWeight, NSString, HippyShadowTextView) { - view.font = [HippyFont updateFont:view.font withWeight:json]; -} - -HIPPY_CUSTOM_SHADOW_PROPERTY(fontStyle, NSString, HippyShadowTextView) { - view.font = [HippyFont updateFont:view.font withStyle:json]; // defaults to normal -} - -HIPPY_CUSTOM_SHADOW_PROPERTY(fontFamily, NSString, HippyShadowTextView) { - // Convert fontName to fontFamily if needed - NSString *familyName = [self familyNameWithCSSNameMatching:json]; - view.font = [HippyFont updateFont:view.font withFamily:familyName]; -} - -HIPPY_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, HippyBaseTextInput) { - UIFont *theFont = [HippyFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; - view.font = theFont; -} - -HIPPY_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused HippyBaseTextInput) { - UIFont *theFont = [HippyFont updateFont:view.font withWeight:json]; // defaults to normal - view.font = theFont; -} - -HIPPY_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused HippyBaseTextInput) { - UIFont *theFont = [HippyFont updateFont:view.font withStyle:json]; - view.font = theFont; // defaults to normal -} - -HIPPY_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, HippyBaseTextInput) { - // Convert fontName to fontFamily if needed - NSString *familyName = [self familyNameWithCSSNameMatching:json]; - view.font = [HippyFont updateFont:view.font withFamily:familyName ?: defaultView.font.familyName]; -} +HIPPY_EXPORT_VIEW_PROPERTY(fontSize, NSNumber) +HIPPY_EXPORT_VIEW_PROPERTY(fontWeight, NSString) +HIPPY_EXPORT_VIEW_PROPERTY(fontStyle, NSString) +HIPPY_EXPORT_VIEW_PROPERTY(fontFamily, NSString) - (HippyViewManagerUIBlock)uiBlockToAmendWithShadowView:(HippyShadowView *)shadowView { NSNumber *hippyTag = shadowView.hippyTag; @@ -242,23 +213,4 @@ - (HippyViewManagerUIBlock)uiBlockToAmendWithShadowView:(HippyShadowView *)shado }; } - -#pragma mark - Private - -/// Get the -/// JS side usually pass a `fontName` instead of `fontFamily` -/// - Parameter json: id -- (NSString *)familyNameWithCSSNameMatching:(id)json { - NSString *familyName = json; - if (json && ![[UIFont familyNames] containsObject:json]) { - // Not a real FamilyName - // Using CSS name matching semantics. - // fontSize here is just a placeholder for getting font. - UIFont *cssFont = [UIFont fontWithName:json size:14.0]; - familyName = cssFont.familyName; - } - return familyName; -} - - @end diff --git a/ios/sdk/module/uimanager/HippyUIManager.h b/ios/sdk/module/uimanager/HippyUIManager.h index d83ab548e26..1ff686d5c6e 100644 --- a/ios/sdk/module/uimanager/HippyUIManager.h +++ b/ios/sdk/module/uimanager/HippyUIManager.h @@ -71,6 +71,18 @@ HIPPY_EXTERN NSString *const HippyUIManagerRootViewKey; */ HIPPY_EXTERN NSString *const HippyUIManagerDidEndBatchNotification; +/** + * This notification can be sent when the font is registered or modified on the native side + * and hippy needs to be refreshed. + * + * `notification.object` can carry rootTag to filter the RootView that needs to be refreshed. + * Default value nil indicating that a refresh is required. + */ +HIPPY_EXTERN NSString *const HippyFontChangeTriggerNotification; + + +#pragma mark - + @protocol HippyScrollableProtocol; /** @@ -110,13 +122,6 @@ HIPPY_EXTERN NSString *const HippyUIManagerDidEndBatchNotification; */ - (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view; -/** - * Update the background color of a view. The source of truth for - * backgroundColor is the shadow view, so if to update backgroundColor from - * native code you will need to call this method. - */ -- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view; - /** * Schedule a block to be executed on the UI thread. Useful if you need to execute * view logic after all currently queued view updates have completed. diff --git a/ios/sdk/module/uimanager/HippyUIManager.mm b/ios/sdk/module/uimanager/HippyUIManager.mm index e7158440f36..2d0758e8d5a 100644 --- a/ios/sdk/module/uimanager/HippyUIManager.mm +++ b/ios/sdk/module/uimanager/HippyUIManager.mm @@ -49,6 +49,9 @@ #import "HippyMemoryOpt.h" #import "HippyDeviceBaseInfo.h" #import "HippyVirtualList.h" +#import "HippyShadowText.h" +#import "HippyShadowTextView.h" +#import "HippyShadowView+MTTLayout.h" @protocol HippyBaseListViewProtocol; @@ -70,7 +73,7 @@ static void HippyTraverseViewNodes(id view, void (^block)(id_rootViewTags containsObject:targetRootTag]) { + // do compare if notification has target RootView. + return; + } + NSArray *shadowViews = strongSelf->_shadowViewRegistry.allValues; + Class shadowTextClass = [HippyShadowText class]; + Class shadowTextViewClass = [HippyShadowTextView class]; + for (HippyShadowView *shadowView in shadowViews) { + if ([shadowView isKindOfClass:shadowTextClass]) { + [shadowView dirtyText]; + [shadowView dirtyPropagation]; + } else if ([shadowView isKindOfClass:shadowTextViewClass]) { + [shadowView dirtyText]; + [shadowView dirtyPropagation]; + MTTNodeMarkDirty(shadowView.nodeRef); + } + } + [strongSelf setNeedsLayout]; + }); +} + #pragma mark - @@ -397,25 +431,6 @@ - (void)setIntrinsicContentSize:(CGSize)size forView:(UIView *)view { }); } -- (void)setBackgroundColor:(UIColor *)color forView:(UIView *)view { - HippyAssertMainQueue(); - /* - NSNumber *hippyTag = view.hippyTag; - dispatch_async(HippyGetUIManagerQueue(), ^{ - if (!self->_viewRegistry) { - return; - } - - HippyShadowView *shadowView = self->_shadowViewRegistry[hippyTag]; - HippyAssert(shadowView != nil, @"Could not locate root view with tag #%@", hippyTag); - shadowView.backgroundColor = color; - [self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:shadowView]; - [self flushVirtualNodeBlocks]; - [self flushUIBlocks]; - }); - */ -} - /** * Unregisters views from registries */