cxfeng1 closed pull request #1799: [iOS] Add richtext component.
URL: https://github.com/apache/incubator-weex/pull/1799
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj 
b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
index 11eb08fa2d..d6aca1de3f 100644
--- a/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
+++ b/ios/sdk/WeexSDK.xcodeproj/project.pbxproj
@@ -285,6 +285,10 @@
                74EF31C31DE6935600667A07 /* WXURLRewriteTests.m in Sources */ = 
{isa = PBXBuildFile; fileRef = 74EF31C21DE6935600667A07 /* WXURLRewriteTests.m 
*/; };
                74F7BFF51DC782EC004D0871 /* testRootView.js in Resources */ = 
{isa = PBXBuildFile; fileRef = 74F7BFF41DC782EC004D0871 /* testRootView.js */; 
};
                74FD6E041C7C0E9600DBEB6D /* WXScrollerProtocol.h in Headers */ 
= {isa = PBXBuildFile; fileRef = 74FD6E031C7C0E9600DBEB6D /* 
WXScrollerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
+               7715EB6221A69DD9001F1108 /* WXRichText.h in Headers */ = {isa = 
PBXBuildFile; fileRef = 7715EB6021A69DD8001F1108 /* WXRichText.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
+               7715EB6321A69DD9001F1108 /* WXRichText.h in Headers */ = {isa = 
PBXBuildFile; fileRef = 7715EB6021A69DD8001F1108 /* WXRichText.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
+               7715EB6421A69DD9001F1108 /* WXRichText.mm in Sources */ = {isa 
= PBXBuildFile; fileRef = 7715EB6121A69DD9001F1108 /* WXRichText.mm */; };
+               7715EB6521A69DD9001F1108 /* WXRichText.mm in Sources */ = {isa 
= PBXBuildFile; fileRef = 7715EB6121A69DD9001F1108 /* WXRichText.mm */; };
                775BEE4E1C16F993008D1629 /* WXDefine.h in Headers */ = {isa = 
PBXBuildFile; fileRef = 775BEE4D1C16F993008D1629 /* WXDefine.h */; settings = 
{ATTRIBUTES = (Public, ); }; };
                775BEE6E1C1BD8F4008D1629 /* WXImgLoaderProtocol.h in Headers */ 
= {isa = PBXBuildFile; fileRef = 775BEE6C1C1BD8F4008D1629 /* 
WXImgLoaderProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
                775BEE711C1BD977008D1629 /* WXModuleProtocol.h in Headers */ = 
{isa = PBXBuildFile; fileRef = 775BEE701C1BD977008D1629 /* WXModuleProtocol.h 
*/; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1287,6 +1291,8 @@
                74EF31C21DE6935600667A07 /* WXURLRewriteTests.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= WXURLRewriteTests.m; sourceTree = "<group>"; };
                74F7BFF41DC782EC004D0871 /* testRootView.js */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; 
path = testRootView.js; sourceTree = "<group>"; };
                74FD6E031C7C0E9600DBEB6D /* WXScrollerProtocol.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WXScrollerProtocol.h; sourceTree = "<group>"; };
+               7715EB6021A69DD8001F1108 /* WXRichText.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WXRichText.h; sourceTree = "<group>"; };
+               7715EB6121A69DD9001F1108 /* WXRichText.mm */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; 
path = WXRichText.mm; sourceTree = "<group>"; };
                775BEE4D1C16F993008D1629 /* WXDefine.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WXDefine.h; sourceTree = "<group>"; };
                775BEE6C1C1BD8F4008D1629 /* WXImgLoaderProtocol.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WXImgLoaderProtocol.h; sourceTree = "<group>"; };
                775BEE701C1BD977008D1629 /* WXModuleProtocol.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
WXModuleProtocol.h; sourceTree = "<group>"; };
@@ -2159,12 +2165,14 @@
                77E65A0A1C155E6E008B8775 /* Component */ = {
                        isa = PBXGroup;
                        children = (
-                               C4B3D6D21E6954300013F38D /* WXEditComponent.h 
*/,
-                               C4B3D6D31E6954300013F38D /* WXEditComponent.mm 
*/,
                                74CFDD361F45937D007A1A66 /* RecycleList */,
                                74D8DB401E4825920078B667 /* Recycler */,
                                2A837AAC1CD9DE9200AEDF03 /* 
WXLoadingComponent.h */,
                                2A837AAD1CD9DE9200AEDF03 /* 
WXLoadingComponent.mm */,
+                               7715EB6021A69DD8001F1108 /* WXRichText.h */,
+                               7715EB6121A69DD9001F1108 /* WXRichText.mm */,
+                               C4B3D6D21E6954300013F38D /* WXEditComponent.h 
*/,
+                               C4B3D6D31E6954300013F38D /* WXEditComponent.mm 
*/,
                                2A837AAE1CD9DE9200AEDF03 /* 
WXLoadingIndicator.h */,
                                DCC77C111D770AE300CE7288 /* 
WXSliderNeighborComponent.mm */,
                                DCC77C121D770AE300CE7288 /* 
WXSliderNeighborComponent.h */,
@@ -2689,6 +2697,7 @@
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               7715EB6221A69DD9001F1108 /* WXRichText.h in 
Headers */,
                                4532670A213FC84A00DAA620 /* 
WXDisplayLinkManager.h in Headers */,
                                B8D66C1B21255730003960BD /* style.h in Headers 
*/,
                                B8D66C2321255730003960BD /* layout.h in Headers 
*/,
@@ -2977,6 +2986,7 @@
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               7715EB6321A69DD9001F1108 /* WXRichText.h in 
Headers */,
                                4532670C213FCF2300DAA620 /* 
WXDisplayLinkManager.h in Headers */,
                                B8D66C1C21255730003960BD /* style.h in Headers 
*/,
                                B8D66C2421255730003960BD /* layout.h in Headers 
*/,
@@ -3587,6 +3597,7 @@
                                B8D66BF32125572F003960BD /* object.cc in 
Sources */,
                                2AC750251C7565690041D390 /* 
WXIndicatorComponent.m in Sources */,
                                591DD3311D23AD5800BE8709 /* WXErrorView.m in 
Sources */,
+                               7715EB6421A69DD9001F1108 /* WXRichText.mm in 
Sources */,
                                B8D66C1121255730003960BD /* 
core_side_in_script.cpp in Sources */,
                                B8D66C032125572F003960BD /* 
css_value_getter.cpp in Sources */,
                                B8394F3921468AF100CA1EFF /* 
render_action_trigger_vsync.cpp in Sources */,
@@ -3807,6 +3818,7 @@
                                DCA4454C1EFA55B300D0CFA8 /* WXComponent.mm in 
Sources */,
                                B8D66C4821255730003960BD /* 
render_action_add_event.cpp in Sources */,
                                B8D66BAE2125572F003960BD /* code_generator.cc 
in Sources */,
+                               7715EB6521A69DD9001F1108 /* WXRichText.mm in 
Sources */,
                                DCA4454D1EFA55B300D0CFA8 /* WXDivComponent.m in 
Sources */,
                                DCA4454E1EFA55B300D0CFA8 /* WXImageComponent.m 
in Sources */,
                                B8D66BB02125572F003960BD /* ast_factory.cc in 
Sources */,
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRichText.h 
b/ios/sdk/WeexSDK/Sources/Component/WXRichText.h
new file mode 100644
index 0000000000..b0c768cc15
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRichText.h
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "WXComponent.h"
+
+@interface WXRichText : WXComponent<UITextViewDelegate>
+
+@end
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm 
b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
new file mode 100644
index 0000000000..833c7d48af
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Component/WXRichText.mm
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import "WXRichText.h"
+#import "WXSDKManager.h"
+#import "WXSDKEngine.h"
+#import "WXConvert.h"
+#import "WXSDKInstance.h"
+#import "WXComponent+Layout.h"
+#import "WXNavigationProtocol.h"
+#import "WXImgLoaderProtocol.h"
+#include <pthread/pthread.h>
+
+@interface WXRichNode : NSObject
+
+@property (nonatomic, strong) NSString  *type;
+@property (nonatomic, strong) NSString  *text;
+@property (nonatomic, strong) UIColor   *color;
+@property (nonatomic, strong) UIColor   *backgroundColor;
+@property (nonatomic, strong) NSString  *fontFamily;
+@property (nonatomic, assign) CGFloat   fontSize;
+@property (nonatomic, assign) CGFloat   fontWeight;
+@property (nonatomic, assign) WXTextStyle  fontStyle;
+@property (nonatomic, assign) WXTextDecoration textDecoration;
+@property (nonatomic, strong) NSString  *pseudoRef;
+@property (nonatomic, assign) CGFloat width;
+@property (nonatomic, assign) CGFloat height;
+@property (nonatomic, strong) NSURL *href;
+@property (nonatomic, strong) NSURL *src;
+@property (nonatomic, assign) NSRange range;
+
+@end
+
+@implementation WXRichNode
+
+@end
+
+@interface WXRichTextView : UITextView
+
+@end
+
+@implementation WXRichTextView
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+    if ((self = [super initWithFrame:frame])) {
+        self.isAccessibilityElement = YES;
+        self.accessibilityTraits |= UIAccessibilityTraitStaticText;
+        self.opaque = NO;
+        self.editable = NO;
+        self.selectable = YES;
+        self.contentMode = UIViewContentModeRedraw;
+        self.textContainerInset = UIEdgeInsetsZero;
+        self.textContainer.lineFragmentPadding = 0.0f;
+        self.textContainer.lineBreakMode = NSLineBreakByClipping;
+    }
+    return self;
+}
+
+@end
+
+#define WX_STYLE_FILL_RICHTEXT(key, type)\
+do {\
+    id value = styles[@#key]; \
+    if (value) { \
+        node.key = [WXConvert type:value];\
+    } else if (!([@#key isEqualToString:@"backgroundColor"] || [@#key 
isEqualToString:@"textDecoration"]) && superNode.key ) { \
+        node.key = superNode.key; \
+    } \
+} while(0);
+
+#define WX_STYLE_FILL_RICHTEXT_PIXEL(key)\
+do {\
+    id value = styles[@#key];\
+    if (value) {\
+        node.key = [WXConvert WXPixelType:value 
scaleFactor:self.weexInstance.pixelScaleFactor];\
+    } else if (superNode.key ) { \
+        node.key = superNode.key; \
+    } \
+} while(0);
+
+
+@implementation WXRichText
+{
+    WXRichTextView *textView;
+    NSMutableArray *_richNodes;
+    NSMutableDictionary *_nodeRanges;
+    NSMutableDictionary *_styles;
+    NSMutableDictionary *_attributes;
+    UIEdgeInsets _padding;
+    NSTextAlignment _textAlign;
+    UIColor *_backgroundColor;
+    NSMutableAttributedString *_attributedString;
+    pthread_mutex_t _attributedStringMutex;
+    pthread_mutexattr_t _propertMutexAttr;
+    CGFloat _lineHeight;
+}
+
+- (void)dealloc
+{
+    pthread_mutex_destroy(&_attributedStringMutex);
+    pthread_mutexattr_destroy(&_propertMutexAttr);
+}
+
+- (WXRichTextView *)textView
+{
+    if (!textView) {
+        textView = [[WXRichTextView alloc]init];
+        textView.delegate = self;
+        textView.scrollEnabled = NO;
+    }
+    return textView;
+}
+
+- (instancetype)initWithRef:(NSString *)ref
+                       type:(NSString *)type
+                     styles:(NSDictionary *)styles
+                 attributes:(NSDictionary *)attributes
+                     events:(NSArray *)events
+               weexInstance:(WXSDKInstance *)weexInstance
+{
+    self = [super initWithRef:ref type:type styles:styles 
attributes:attributes events:events weexInstance:weexInstance];
+    if (self) {
+        _richNodes = [NSMutableArray new];
+        _nodeRanges = [NSMutableDictionary new];
+        _styles = [NSMutableDictionary dictionaryWithDictionary:styles];
+        _attributes = [NSMutableDictionary 
dictionaryWithDictionary:attributes];
+        _textAlign = styles[@"textAlign"] ? [WXConvert 
NSTextAlignment:styles[@"textAlign"]] : NSTextAlignmentLeft;
+        _lineHeight = styles[@"lineHeight"] ? [WXConvert 
CGFloat:styles[@"lineHeight"]] / 2: 0;
+        pthread_mutexattr_init(&(_propertMutexAttr));
+        pthread_mutexattr_settype(&(_propertMutexAttr), 
PTHREAD_MUTEX_RECURSIVE);
+        pthread_mutex_init(&(_attributedStringMutex), &(_propertMutexAttr));
+    }
+    return self;
+}
+
+- (void)fillAttributes:(NSDictionary *)attributes
+{
+    id value = attributes[@"value"];
+    if ([value isKindOfClass: [NSArray class]]) {
+        [_richNodes removeAllObjects];
+        
+        WXRichNode *rootNode = [[WXRichNode alloc]init];
+        [_richNodes addObject:rootNode];
+        
+        //记录richtext根节点styles,仅用于子节点的样式继承
+        rootNode.type = @"root";
+        if (_styles) {
+            [self fillCSSStyles:_styles toNode:rootNode superNode:nil];
+        }
+        
+        for (NSDictionary *dict in value) {
+            [self recursivelyAddChildNode:dict toSuperNode:rootNode];
+        }
+        
+        _backgroundColor = rootNode.backgroundColor?:[UIColor whiteColor];
+    }
+}
+
+- (void)fillCSSStyles:(NSDictionary *)styles toNode:(WXRichNode *)node 
superNode:(WXRichNode *)superNode
+{
+    WX_STYLE_FILL_RICHTEXT(color, UIColor)
+    WX_STYLE_FILL_RICHTEXT(backgroundColor, UIColor)
+    WX_STYLE_FILL_RICHTEXT(fontFamily, NSString)
+    WX_STYLE_FILL_RICHTEXT_PIXEL(fontSize)
+    WX_STYLE_FILL_RICHTEXT(fontWeight, WXTextWeight)
+    WX_STYLE_FILL_RICHTEXT(fontStyle, WXTextStyle)
+    WX_STYLE_FILL_RICHTEXT(textDecoration, WXTextDecoration)
+    WX_STYLE_FILL_RICHTEXT_PIXEL(width)
+    WX_STYLE_FILL_RICHTEXT_PIXEL(height)
+}
+
+- (void)fillAttributes:(NSDictionary *)attributes toNode:(WXRichNode *)node 
superNode:(WXRichNode *)superNode
+{
+    if (attributes[@"pseudoRef"]) {
+        node.pseudoRef = attributes[@"pseudoRef"];
+        node.href = [NSURL URLWithString:@"click://"];
+    }
+    
+    if (attributes[@"href"]) {
+        node.href = [NSURL URLWithString:attributes[@"href"]];
+    }
+    else if (superNode.href) {
+        node.href = superNode.href;
+    }
+    
+    if (attributes[@"src"]) {
+        node.src = [NSURL URLWithString:attributes[@"src"]];
+    }
+    
+    if (attributes[@"value"] ) {
+        id value = attributes[@"value"];
+        if ([value isKindOfClass:[NSString class]]) {
+            node.text = (NSString *)value;
+        }
+    }
+}
+
+- (void)recursivelyAddChildNode:(NSDictionary *)nodeValue 
toSuperNode:(WXRichNode *)superNode
+{
+    WXRichNode *node = [[WXRichNode alloc]init];
+    [_richNodes addObject:node];
+    
+    node.type = nodeValue[@"type"];
+
+    [self fillCSSStyles:nodeValue[@"style"] toNode:node superNode:superNode];
+
+    if (nodeValue[@"attr"]) {
+        [self fillAttributes:nodeValue[@"attr"] toNode:node 
superNode:superNode];
+    }
+    
+    if (nodeValue[@"children"]) {
+        id value = nodeValue[@"children"];
+        if ([value isKindOfClass:[NSArray class]]) {
+            NSArray *children = (NSArray *)value;
+            for(NSDictionary *childValue in children){
+                [self recursivelyAddChildNode:childValue toSuperNode:node];
+            }
+        }
+    }
+}
+
+#pragma mark - Subclass
+
+- (UIView *)loadView
+{
+    return  [self textView];
+}
+
+- (void)viewDidUnload
+{
+    textView = nil;
+}
+
+- (void)viewDidLoad
+{
+    [self innerLayout];
+}
+
+- (void)layoutDidFinish
+{
+    [self innerLayout];
+}
+
+- (void)innerLayout
+{
+    if (self.flexCssNode == nullptr) {
+        return;
+    }
+    UIEdgeInsets padding = {
+            
WXFloorPixelValue(self.flexCssNode->getPaddingTop()+self.flexCssNode->getBorderWidthTop()),
+            
WXFloorPixelValue(self.flexCssNode->getPaddingLeft()+self.flexCssNode->getBorderWidthLeft()),
+            
WXFloorPixelValue(self.flexCssNode->getPaddingBottom()+self.flexCssNode->getBorderWidthBottom()),
+            
WXFloorPixelValue(self.flexCssNode->getPaddingRight()+self.flexCssNode->getBorderWidthRight())
+        };
+
+    
+    _padding = padding;
+    
+    _attributedString = [self buildAttributeString];
+
+    [self textView].attributedText = _attributedString;
+    [self textView].textContainerInset = _padding;
+    [self textView].backgroundColor = [UIColor clearColor];
+}
+
+- (CGSize (^)(CGSize))measureBlock
+{
+    __weak typeof(self) weakSelf = self;
+    
+    return ^CGSize (CGSize constrainedSize) {
+        
+        NSMutableAttributedString *attributedString = [weakSelf 
buildAttributeString];
+        
+        CGFloat width = constrainedSize.width;
+        if (isnan(width)) {
+            width = CGFLOAT_MAX;
+        }
+        
+        CGRect rect = [attributedString boundingRectWithSize:CGSizeMake(width, 
CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | 
NSStringDrawingUsesFontLeading context:nil];
+        CGSize computedSize = rect.size;
+        if(weakSelf.flexCssNode != nullptr){
+            if (!isnan(weakSelf.flexCssNode->getMinWidth())) {
+                computedSize.width = MAX(computedSize.width, 
weakSelf.flexCssNode->getMinWidth());
+            }
+            if (!isnan(weakSelf.flexCssNode->getMaxWidth())) {
+                computedSize.width = MIN(computedSize.width, 
weakSelf.flexCssNode->getMaxWidth());
+            }
+            if (!isnan(weakSelf.flexCssNode->getMinHeight())) {
+                computedSize.width = MAX(computedSize.height, 
weakSelf.flexCssNode->getMinHeight());
+            }
+            if (!isnan(weakSelf.flexCssNode->getMaxHeight())) {
+                computedSize.width = MIN(computedSize.height, 
weakSelf.flexCssNode->getMaxHeight());
+            }
+        }
+        return (CGSize) {
+            WXCeilPixelValue(computedSize.width),
+            WXCeilPixelValue(computedSize.height)
+        };
+    };
+}
+
+#pragma mark Text Building
+
+- (NSMutableAttributedString *)buildAttributeString
+{
+    pthread_mutex_lock(&(_attributedStringMutex));
+    [self fillAttributes:_attributes];
+    NSMutableArray *array = [NSMutableArray arrayWithArray:_richNodes];
+    pthread_mutex_unlock(&(_attributedStringMutex));
+    
+    NSMutableDictionary *nodeRange = [NSMutableDictionary dictionary];
+    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] 
init];
+    NSUInteger location;
+    
+    __weak typeof(self) weakSelf = self;
+    for (WXRichNode *node in array) {
+        location = attrStr.length;
+        
+        if ([node.type isEqualToString:@"span"]) {
+            if (node.text && [node.text length] > 0) {
+                NSString *text = node.text;
+                [attrStr.mutableString appendString:text];
+                
+                NSRange range = NSMakeRange(location, text.length);
+                [attrStr addAttribute:NSForegroundColorAttributeName 
value:node.color ?: [UIColor blackColor] range:range];
+                [attrStr addAttribute:NSBackgroundColorAttributeName 
value:node.backgroundColor ?: _backgroundColor range:range];
+                
+                UIFont *font = [WXUtility fontWithSize:node.fontSize 
textWeight:node.fontWeight textStyle:WXTextStyleNormal 
fontFamily:node.fontFamily scaleFactor:self.weexInstance.pixelScaleFactor];
+                if (font) {
+                    [attrStr addAttribute:NSFontAttributeName value:font 
range:range];
+                }
+                if (node.fontStyle == WXTextStyleItalic) {
+                    [attrStr addAttribute:NSObliquenessAttributeName 
value:@0.3 range:range];
+                }
+                else
+                {
+                    [attrStr addAttribute:NSObliquenessAttributeName value:@0 
range:range];
+                }
+                [attrStr addAttribute:NSUnderlineStyleAttributeName 
value:[NSNumber numberWithInteger:WXTextDecorationNone] range:range];
+                [attrStr addAttribute:NSStrikethroughStyleAttributeName 
value:[NSNumber numberWithInteger:WXTextDecorationNone] range:range];
+                
+                if (node.textDecoration == WXTextDecorationUnderline) {
+                    [attrStr addAttribute:NSUnderlineStyleAttributeName 
value:[NSNumber numberWithInteger:WXTextDecorationUnderline] range:range];
+                }
+                else if (node.textDecoration == WXTextDecorationLineThrough) {
+                    [attrStr addAttribute:NSStrikethroughStyleAttributeName 
value:[NSNumber numberWithInteger:WXTextDecorationLineThrough] range:range];
+                }
+                
+                if (node.href) {
+                    [attrStr addAttribute:NSLinkAttributeName value:node.href 
range:range];
+                }
+                else {
+                    [attrStr removeAttribute:NSLinkAttributeName range:range];
+                }
+                
+                NSMutableParagraphStyle *paragraphStyle = 
[[NSMutableParagraphStyle alloc] init];
+                paragraphStyle.alignment = _textAlign;
+                if(_lineHeight != 0 )
+                {
+                    paragraphStyle.minimumLineHeight = _lineHeight;
+                    paragraphStyle.maximumLineHeight = _lineHeight;
+                    [attrStr addAttribute:NSBaselineOffsetAttributeName 
value:@((_lineHeight - font.lineHeight)/2) range:range];
+                }
+                [attrStr addAttribute:NSParagraphStyleAttributeName 
value:paragraphStyle range:range];
+                
+                [nodeRange setObject:node forKey:NSStringFromRange(range)];
+            }
+        }
+        else if ([node.type isEqualToString:@"image"]) {
+            NSTextAttachment *imgAttachment = [[NSTextAttachment alloc]init];
+            imgAttachment.bounds = CGRectMake(0, 0, node.width, node.height);
+            
+            NSAttributedString *attachAttriStr = [NSAttributedString 
attributedStringWithAttachment:imgAttachment];
+            [attrStr appendAttributedString:attachAttriStr];
+            
+            NSRange range = NSMakeRange(location, attachAttriStr.length);
+            [attrStr addAttribute:NSFontAttributeName value: [UIFont 
systemFontOfSize:node.height] range:range];
+            
+            if (node.href) {
+                [attrStr addAttribute:NSLinkAttributeName value:node.href 
range:range];
+            }
+            else {
+                [attrStr removeAttribute:NSLinkAttributeName range:range];
+            }
+            
+            [nodeRange setObject:node forKey:NSStringFromRange(range)];
+            
+            if (node.src) {
+                [[self imageLoader] 
downloadImageWithURL:node.src.absoluteString imageFrame:imgAttachment.bounds 
userInfo:nil completed:^(UIImage *image, NSError *error, BOOL finished) {
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        imgAttachment.image = image;
+                        [[weakSelf textView].layoutManager 
invalidateDisplayForCharacterRange:range];
+                    });
+                }];
+            }
+        }
+    }
+    
+    pthread_mutex_lock(&(_attributedStringMutex));
+    [_nodeRanges removeAllObjects];
+    _nodeRanges = [NSMutableDictionary dictionaryWithDictionary:nodeRange];
+    pthread_mutex_unlock(&(_attributedStringMutex));
+    
+    return attrStr;
+}
+
+- (void)updateStyles:(NSDictionary *)styles {
+    
+    if (styles[@"textAlign"]) {
+        _textAlign = [WXConvert NSTextAlignment:styles[@"textAlign"]];
+    }
+    if (styles[@"lineHeight"]) {
+        _lineHeight = [WXConvert CGFloat:styles[@"lineHeight"]] / 2;
+    }
+    [_styles addEntriesFromDictionary:styles];
+    [self syncTextStorageForView];
+}
+
+- (void)updateAttributes:(NSDictionary *)attributes {
+    _attributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
+    [self syncTextStorageForView];
+}
+
+- (void)syncTextStorageForView {
+    pthread_mutex_lock(&(_attributedStringMutex));
+    [self fillAttributes:_attributes];
+    pthread_mutex_unlock(&(_attributedStringMutex));
+    
+    if (_styles[@"height"]) {
+       [self innerLayout];
+    }
+    else {
+       [self setNeedsLayout];
+    }
+}
+
+#pragma mark - UITextView Delegate
+
+- (BOOL)textView:(UITextView *)textView 
shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment 
inRange:(NSRange)characterRange {
+    return NO;
+}
+
+- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL 
inRange:(NSRange)characterRange {
+    if (!URL) {
+        return NO;
+    }
+    
+    NSString *rangeStr = NSStringFromRange(characterRange);
+    WXRichNode *node = [_nodeRanges objectForKey:rangeStr];
+    
+    if (![[node.href absoluteString] isEqualToString:@"click://"]) {
+        id<WXNavigationProtocol> navigationHandler = [self navigationHandler];
+        if ([navigationHandler 
respondsToSelector:@selector(pushViewControllerWithParam:
+                                                            completion:
+                                                            withContainer:)]) {
+            [navigationHandler 
pushViewControllerWithParam:@{@"url":URL.absoluteString} completion:^(NSString 
*code, NSDictionary *responseData) {
+            } withContainer:self.weexInstance.viewController];
+        } else {
+            WXLogError(@"Event handler of class %@ does not respond to 
pushViewControllerWithParam", NSStringFromClass([navigationHandler class]));
+        }
+    }
+    else if (node.pseudoRef) {
+        NSMutableDictionary *params = [NSMutableDictionary new];
+        [params setObject:node.pseudoRef forKey:@"pseudoRef"];
+        [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId 
ref:self.ref type:@"itemclick" params:params domChanges:nil];
+    }
+    
+    return NO;
+}
+
+# pragma mark - imageLoader
+
+- (id<WXImgLoaderProtocol>)imageLoader {
+    static id<WXImgLoaderProtocol> imageLoader;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        imageLoader = [WXSDKEngine 
handlerForProtocol:@protocol(WXImgLoaderProtocol)];
+    });
+    return imageLoader;
+}
+
+- (id<WXNavigationProtocol>)navigationHandler {
+    static id<WXNavigationProtocol> navigationHandler;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        navigationHandler = [WXSDKEngine 
handlerForProtocol:@protocol(WXNavigationProtocol)];
+    });
+    return navigationHandler;
+}
+
+@end
diff --git a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m 
b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
index 26ca8b7714..980d4fd8b4 100644
--- a/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
+++ b/ios/sdk/WeexSDK/Sources/Engine/WXSDKEngine.m
@@ -100,6 +100,8 @@ + (void)_registerDefaultComponents
     [self registerComponent:@"div" withClass:NSClassFromString(@"WXComponent") 
withProperties:nil];
     [self registerComponent:@"text" 
withClass:NSClassFromString(@"WXTextComponent") withProperties:nil];
     [self registerComponent:@"image" 
withClass:NSClassFromString(@"WXImageComponent") withProperties:nil];
+    [self registerComponent:@"richtext" 
withClass:NSClassFromString(@"WXRichText") withProperties:nil];
+    
     [self registerComponent:@"scroller" 
withClass:NSClassFromString(@"WXScrollerComponent") withProperties:nil];
     [self registerComponent:@"list" 
withClass:NSClassFromString(@"WXListComponent") withProperties:nil];
     [self registerComponent:@"recycler" 
withClass:NSClassFromString(@"WXRecyclerComponent") withProperties:nil];
diff --git a/ios/sdk/WeexSDK/Sources/WeexSDK.h 
b/ios/sdk/WeexSDK/Sources/WeexSDK.h
index 1f6791a392..a611390951 100644
--- a/ios/sdk/WeexSDK/Sources/WeexSDK.h
+++ b/ios/sdk/WeexSDK/Sources/WeexSDK.h
@@ -38,6 +38,7 @@
 #import "WXSDKError.h"
 #import "WXSDKEngine.h"
 #import "WXRootViewController.h"
+#import "WXRichText.h"
 #import "WXResourceResponse.h"
 #import "WXResourceRequestHandler.h"
 #import "WXResourceRequest.h"


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to