├── .gitignore ├── CompileTool ├── buildTemplate.sh ├── compiler.jar ├── config.properties ├── copyOutFile.sh ├── template │ ├── container.xml │ ├── dot9image.xml │ ├── framelayout.xml │ ├── grid.xml │ ├── griditem.xml │ ├── gridlayout.xml │ ├── nframelayout.xml │ ├── ngridlayout.xml │ ├── nimage.xml │ ├── nline.xml │ ├── nratiolayout.xml │ ├── ntext.xml │ ├── nvh2layout.xml │ ├── nvhlayout.xml │ ├── page.xml │ ├── pageitem.xml │ ├── ratiolayout.xml │ ├── textcell.xml │ ├── vh2layout.xml │ └── vhlayout.xml └── templatelist.properties ├── LICENSE ├── Podfile ├── README.md ├── README ├── compile_tools_script.png └── feature.png ├── RealtimePreview ├── RealtimePreview.xcodeproj │ └── project.pbxproj └── RealtimePreview │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── ic_toys.imageset │ │ ├── Contents.json │ │ └── ic_toys.png │ ├── scan.imageset │ │ ├── Contents.json │ │ └── scan@3x.png │ └── settings.imageset │ │ ├── Contents.json │ │ └── settings@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Classes │ ├── HotReloadService.h │ ├── HotReloadService.m │ ├── MainViewController.h │ ├── MainViewController.m │ ├── ScanViewController.h │ ├── ScanViewController.m │ ├── SettingsViewController.h │ ├── SettingsViewController.m │ ├── TestViewController.h │ └── TestViewController.m │ ├── Info.plist │ └── main.m ├── VirtualView.podspec ├── VirtualView ├── Core │ ├── VVBaseNode.h │ ├── VVBaseNode.m │ ├── VVBinaryStringMapper.h │ ├── VVBinaryStringMapper.m │ ├── VVConfig.h │ ├── VVConfig.m │ ├── VVDefines.h │ ├── VVErrors.h │ ├── VVErrors.m │ ├── VVNodeClassMapper.h │ ├── VVNodeClassMapper.m │ ├── VVSystemKey.h │ ├── VVSystemKey.m │ ├── VVViewContainer.h │ └── VVViewContainer.m ├── Creater │ ├── Setter │ │ ├── Expression │ │ │ ├── VVExpression.h │ │ │ ├── VVExpression.m │ │ │ ├── VVPropertyExpressionSetter.h │ │ │ └── VVPropertyExpressionSetter.m │ │ ├── VVPropertyFloatSetter.h │ │ ├── VVPropertyFloatSetter.m │ │ ├── VVPropertyIntSetter.h │ │ ├── VVPropertyIntSetter.m │ │ ├── VVPropertySetter.h │ │ ├── VVPropertySetter.m │ │ ├── VVPropertyStringSetter.h │ │ └── VVPropertyStringSetter.m │ ├── VVNodeCreater.h │ └── VVNodeCreater.m ├── Loader │ ├── VVBinaryLoader.h │ ├── VVBinaryLoader.m │ ├── VVTemplateBinaryLoader.h │ ├── VVTemplateBinaryLoader.m │ ├── VVTemplateLoader.h │ ├── VVTemplateLoader.m │ ├── VVTemplateManager.h │ ├── VVTemplateManager.m │ ├── VVVersionModel.h │ ├── VVVersionModel.m │ ├── VVViewFactory.h │ └── VVViewFactory.m ├── Node │ ├── Layer │ │ ├── VVLayer.h │ │ ├── VVLayer.m │ │ ├── VVLineLayer.h │ │ └── VVLineLayer.m │ ├── Layout │ │ ├── NVFrameLayout.h │ │ ├── NVFrameLayout.m │ │ ├── NVGridLayout.h │ │ ├── NVGridLayout.m │ │ ├── NVRatioLayout.h │ │ ├── NVRatioLayout.m │ │ ├── NVVH2Layout.h │ │ ├── NVVH2Layout.m │ │ ├── NVVHLayout.h │ │ ├── NVVHLayout.m │ │ ├── VVFrameLayout.h │ │ ├── VVFrameLayout.m │ │ ├── VVGridLayout.h │ │ ├── VVGridLayout.m │ │ ├── VVLayout.h │ │ ├── VVLayout.m │ │ ├── VVRatioLayout.h │ │ ├── VVRatioLayout.m │ │ ├── VVVH2Layout.h │ │ ├── VVVH2Layout.m │ │ ├── VVVHLayout.h │ │ └── VVVHLayout.m │ ├── Magic │ │ ├── VVGridView.h │ │ ├── VVGridView.m │ │ ├── VVLoopingScrollView.h │ │ ├── VVLoopingScrollView.m │ │ ├── VVPageView.h │ │ └── VVPageView.m │ └── Widget │ │ ├── NVImageView.h │ │ ├── NVImageView.m │ │ ├── NVLineView.h │ │ ├── NVLineView.m │ │ ├── NVTextView.h │ │ └── NVTextView.m ├── Observer │ ├── NSObject+VVObserver.h │ ├── NSObject+VVObserver.m │ ├── VVObserver.h │ └── VVObserver.m └── Utils │ ├── UIColor+VirtualView.h │ ├── UIColor+VirtualView.m │ ├── UIView+VirtualView.h │ └── UIView+VirtualView.m ├── VirtualViewDemo ├── README ├── VirtualViewDemo.xcodeproj │ └── project.pbxproj └── VirtualViewDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── ContainerViewController.h │ ├── ContainerViewController.m │ ├── Dot9ImageView.h │ ├── Dot9ImageView.m │ ├── GridViewController.h │ ├── GridViewController.m │ ├── Info.plist │ ├── MainViewController.h │ ├── MainViewController.m │ ├── PageViewController.h │ ├── PageViewController.m │ ├── Resources │ ├── Container.out │ ├── Dot9Image.out │ ├── FrameLayout.out │ ├── Grid.out │ ├── GridItem.out │ ├── GridLayout.out │ ├── Media.xcassets │ │ ├── Contents.json │ │ ├── right_arrow.imageset │ │ │ ├── Contents.json │ │ │ ├── right_arrow@2x.png │ │ │ └── right_arrow@3x.png │ │ └── right_bubble.imageset │ │ │ ├── Contents.json │ │ │ ├── right_bubble@2x.png │ │ │ └── right_bubble@3x.png │ ├── NFrameLayout.out │ ├── NGridLayout.out │ ├── NImage.out │ ├── NLine.out │ ├── NRatioLayout.out │ ├── NText.out │ ├── NVH2Layout.out │ ├── NVHLayout.out │ ├── Page.out │ ├── PageItem.out │ ├── RatioLayout.out │ ├── TextCell.out │ ├── VH2Layout.out │ ├── VHLayout.out │ └── local_image.png │ ├── TableViewController.h │ ├── TableViewController.m │ ├── TestViewController.h │ ├── TestViewController.m │ └── main.m ├── VirtualViewTest ├── README ├── VirtualViewTest.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcbaselines │ │ └── 926B403F2004950B0003D88E.xcbaseline │ │ ├── 4547F22E-E920-471F-BD93-A11D61EF210A.plist │ │ └── Info.plist └── VirtualViewTest │ ├── BinaryLoaderTest.m │ ├── BinaryReaderTest.m │ ├── ColorUtilsTest.m │ ├── ExpressionTest.m │ ├── Info.plist │ ├── ObserverTest.m │ ├── Resources │ ├── FrameLayout.out │ ├── GridItem.out │ ├── NImage.out │ ├── NLine.out │ ├── NText.out │ ├── RatioLayout.out │ └── VHLayout.out │ ├── StringMapperTest.m │ └── TemplateManagerTest.m └── update_header.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | 3 | ## Build generated 4 | build/ 5 | DerivedData/ 6 | 7 | ## Various settings 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata/ 17 | 18 | ## Other 19 | *.moved-aside 20 | *.xccheckout 21 | *.xcscmblueprint 22 | 23 | ## Obj-C/Swift specific 24 | *.hmap 25 | *.ipa 26 | *.dSYM.zip 27 | *.dSYM 28 | 29 | # CocoaPods 30 | Pods/ 31 | Podfile.lock 32 | *.xcworkspace 33 | 34 | # IDEA 35 | .idea/ 36 | -------------------------------------------------------------------------------- /CompileTool/buildTemplate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! command -v java >/dev/null 2>&1 ; then 4 | echo "java is not installed" 5 | exit 0 6 | fi 7 | if ! java -version 2>&1 | grep -q 'version "1\.8\.' ; then 8 | echo "java version is not 1.8.x" 9 | exit 0 10 | fi 11 | 12 | cd `dirname $0` 13 | java -jar compiler.jar jarBuild 14 | -------------------------------------------------------------------------------- /CompileTool/compiler.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/CompileTool/compiler.jar -------------------------------------------------------------------------------- /CompileTool/config.properties: -------------------------------------------------------------------------------- 1 | VIEW_ID_FrameLayout=1 2 | VIEW_ID_VHLayout=2 3 | VIEW_ID_VH2Layout=3 4 | VIEW_ID_GridLayout=4 5 | VIEW_ID_FlexLayout=5 6 | VIEW_ID_RatioLayout=6 7 | VIEW_ID_NText=7 8 | VIEW_ID_VText=8 9 | VIEW_ID_NImage=9 10 | VIEW_ID_VImage=10 11 | VIEW_ID_NLine=13 12 | VIEW_ID_VLine=14 13 | VIEW_ID_Page=16 14 | VIEW_ID_Grid=17 15 | VIEW_ID_NFrameLayout=25 16 | VIEW_ID_NGridLayout=26 17 | VIEW_ID_NRatioLayout=27 18 | VIEW_ID_NVH2Layout=28 19 | VIEW_ID_NVHLayout=29 20 | VIEW_ID_Dot9Image=1001 21 | autoDimDirection=Enum 22 | autoDimX=Number 23 | autoDimY=Number 24 | autoSwitch=Int 25 | background=Color 26 | borderBottomLeftRadius=Float 27 | borderBottomRightRadius=Float 28 | borderColor=Color 29 | borderRadius=Float 30 | borderTopLeftRadius=Float 31 | borderTopRightRadius=Float 32 | borderWidth=Float 33 | canSlide=Int 34 | colCount=Int 35 | color=Color 36 | ellipsize=Enum 37 | flag=Flag 38 | gravity=Align 39 | id=Int 40 | itemHeight=Number 41 | itemHorizontalMargin=Number 42 | itemVerticalMargin=Number 43 | layoutDirection=Enum 44 | layoutGravity=Align 45 | layoutHeight=LayoutWidthHeight 46 | layoutMarginBottom=Number 47 | layoutMarginLeft=Number 48 | layoutMarginRight=Number 49 | layoutMarginTop=Number 50 | layoutOrientation=Enum 51 | layoutRatio=Number 52 | layoutWidth=LayoutWidthHeight 53 | lines=Int 54 | lineHeight=Float 55 | lineSpaceExtra=Float 56 | lineSpaceMultiplier=Float 57 | maxLines=Int 58 | minHeight=Int 59 | minWidth=Int 60 | orientation=Enum 61 | paddingBottom=Number 62 | paddingLeft=Number 63 | paddingRight=Number 64 | paddingTop=Number 65 | paintWidth=Number 66 | ratio=Float 67 | scaleType=Enum 68 | stayTime=Int 69 | style=Enum 70 | supportHTMLStyle=Int 71 | textColor=Color 72 | textSize=Number 73 | textStyle=TextStyle 74 | visibility=Visibility 75 | dot9Left=Number 76 | dot9Right=Number 77 | dot9Top=Number 78 | dot9Bottom=Number 79 | dot9Scale=Int 80 | -------------------------------------------------------------------------------- /CompileTool/copyOutFile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0` 4 | if [ -d "./build/out" ]; then 5 | cp ./build/out/*.out ${PROJECT_DIR}/${PROJECT_NAME}/Resources 6 | fi 7 | -------------------------------------------------------------------------------- /CompileTool/template/dot9image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 23 | 28 | 35 | 46 | 47 | 55 | 62 | 67 | 74 | 86 | -------------------------------------------------------------------------------- /CompileTool/template/framelayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 22 | 31 | 42 | 51 | 61 | 66 | 71 | 72 | 81 | 90 | 100 | 106 | -------------------------------------------------------------------------------- /CompileTool/template/grid.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 29 | 30 | -------------------------------------------------------------------------------- /CompileTool/template/griditem.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 20 | 26 | -------------------------------------------------------------------------------- /CompileTool/template/nframelayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 22 | 31 | 39 | 43 | 44 | 53 | 63 | 68 | 73 | 74 | 83 | 92 | 102 | 108 | -------------------------------------------------------------------------------- /CompileTool/template/nimage.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 22 | 23 | 31 | 35 | 36 | 44 | 50 | 51 | 59 | 64 | 65 | 73 | 78 | 79 | 87 | 92 | 93 | 101 | 110 | 111 | 119 | 129 | -------------------------------------------------------------------------------- /CompileTool/template/nline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 19 | 26 | 34 | 41 | 49 | 50 | 51 | 60 | 63 | 70 | 77 | 78 | 79 | 87 | 94 | 101 | 102 | 110 | 120 | 128 | 129 | -------------------------------------------------------------------------------- /CompileTool/template/page.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 22 | 23 | 31 | 38 | 39 | 47 | 52 | 53 | 61 | 68 | 69 | 77 | 84 | 85 | -------------------------------------------------------------------------------- /CompileTool/template/pageitem.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 20 | -------------------------------------------------------------------------------- /CompileTool/template/textcell.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 23 | -------------------------------------------------------------------------------- /CompileTool/templatelist.properties: -------------------------------------------------------------------------------- 1 | ntext=NText,1 2 | nline=NLine,1 3 | nimage=NImage,1 4 | framelayout=FrameLayout,1 5 | vhlayout=VHLayout,1 6 | vh2layout=VH2Layout,1 7 | gridlayout=GridLayout,1 8 | ratiolayout=RatioLayout,1 9 | nframelayout=NFrameLayout,1 10 | nvhlayout=NVHLayout,1 11 | nvh2layout=NVH2Layout,1 12 | ngridlayout=NGridLayout,1 13 | nratiolayout=NRatioLayout,1 14 | container=Container,1 15 | grid=Grid,1 16 | griditem=GridItem,1 17 | page=Page,1 18 | pageitem=PageItem,1 19 | textcell=TextCell,1 20 | dot9image=Dot9Image,1 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017-2018 Alibaba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source "https://github.com/CocoaPods/Specs.git" 2 | # source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git' 3 | 4 | platform :ios, '8.0' 5 | 6 | target 'VirtualViewDemo' do 7 | project 'VirtualViewDemo/VirtualViewDemo.xcodeproj' 8 | pod 'VirtualView', :path => './' 9 | end 10 | 11 | target 'RealtimePreview' do 12 | project 'RealtimePreview/RealtimePreview.xcodeproj' 13 | pod 'VirtualView', :path => './' 14 | pod 'Masonry' 15 | pod 'XLForm' 16 | end 17 | 18 | target 'VirtualViewTest' do 19 | project 'VirtualViewTest/VirtualViewTest.xcodeproj' 20 | pod 'VirtualView', :path => './' 21 | pod 'OCHamcrest' 22 | end 23 | 24 | workspace 'VirtualView' 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CocoaPods](https://img.shields.io/cocoapods/v/VirtualView.svg)]() [![CocoaPods](https://img.shields.io/cocoapods/p/VirtualView.svg)]() [![CocoaPods](https://img.shields.io/cocoapods/l/VirtualView.svg)]() 2 | 3 | # VirtualView 4 | 5 | A solution to create & release UI component dynamically. 6 | 7 | It a part of our [Tangram](https://github.com/alibaba/Tangram-iOS) solution. And it can be used as a standalone library. 8 | 9 | 这是一个动态化创建和发布 UI 组件的方案。 10 | 11 | 它是我们 [Tangram](https://github.com/alibaba/Tangram-iOS) 方案的一部分。当然它也可以独立使用。 12 | 13 | 中文介绍:[VirtualView iOS](http://pingguohe.net/2018/02/23/virtualview-ios-1.2.html) 14 | 15 | 中文文档:[VirtualView通用文档](http://tangram.pingguohe.net/docs/virtualview/about-virtualview),[VirtualView iOS文档](http://tangram.pingguohe.net/docs/ios/use-virtualview) 16 | 17 | ## Features 18 | 19 | ![feature](https://github.com/alibaba/VirtualView-iOS/raw/master/README/feature.png) 20 | 21 | 1. Write component via XML. 22 | 2. Compile XML to a .out (binary) file. 23 | 3. Load .out file in iOS application. 24 | 4. Create component from loaded template and bind data to it. 25 | 5. Show the component. 26 | 27 | 简单总结起来就是用 XML 描述一个组件,用我们提供的工具编译成 .out 二进制文件,在集成了 VirtualView 的 App 里直接加载 .out 文件就可以得到一个组件,然后像使用普通 UIView 一样使用它就好了。 28 | 29 | ## Install 30 | 31 | ### CocoaPods 32 | 33 | Use VirtualView alone: 34 | 35 | pod 'VirtualView' 36 | 37 | Use VirtualView with [Tangram](https://github.com/alibaba/Tangram-iOS): 38 | 39 | pod 'Tangram' 40 | 41 | CocoaPods will install VirtualView as a part of Tangram 2.x. 42 | 43 | ### Source codes 44 | 45 | Or you can download source codes from [releases page](https://github.com/alibaba/VirtualView-iOS/releases) and put them into your project. 46 | 47 | ## How to use 48 | 49 | 1. Load component template from .out file. 50 | 51 | ```objective-c 52 | if (![[VVTemplateManager sharedManager].loadedTypes containsObject:@"icon_type"]) { 53 | NSString *path = [[NSBundle mainBundle] pathForResource:@"icon_file" ofType:@"out"]; 54 | [[VVTemplateManager sharedManager] loadTemplateFile:path forType:@"type_alias"]; 55 | } 56 | ``` 57 | 58 | 2. Create component. 59 | 60 | ```objective-c 61 | self.viewContainer = [VVViewContainer viewContainerWithTemplateType:@"icon_type"]; 62 | [self.view addSubview:self.viewContainer]; 63 | ``` 64 | 65 | 3. Bind data and calc the layout (fixed size). 66 | 67 | ```objective-c 68 | self.viewContainer.frame = CGRectMake(0, 0, SCREEN_WIDTH, 1000); 69 | [self.viewContainer update:@{ 70 | @"type" : @"icon-type", 71 | @"imgUrl" : @"https://test.com/test.png" 72 | }]; 73 | ``` 74 | 75 | 4. If you want to clac size. 76 | 77 | ```objective-c 78 | [self.viewContainer updateData:@{ 79 | @"type" : @"icon-type", 80 | @"imgUrl" : @"https://test.com/test.png" 81 | }]; 82 | CGSize size = CGSizeMake(MAX_WIDTH, MAX_HEIGHT); 83 | size = [self.viewContainer estimatedSize:size]; 84 | self.viewContainer.frame = CGRectMake(0, 0, size.width, size.height); 85 | [self.viewContainer updateLayout]; 86 | ``` 87 | 88 | See more details in the demo project. 89 | 90 | ## XML Compile Tools 91 | 92 | An executable jar (need Java 1.8) is in the CompileTool path. In the demo project, we use bash script to sync XML template changes. You can find the script here: 93 | 94 | ![compile_tools_script](https://github.com/alibaba/VirtualView-iOS/raw/master/README/compile_tools_script.png) 95 | 96 | See more details [here](https://github.com/alibaba/virtualview_tools). 97 | -------------------------------------------------------------------------------- /README/compile_tools_script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/README/compile_tools_script.png -------------------------------------------------------------------------------- /README/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/README/feature.png -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // VVPlayground 4 | // 5 | // Created by isaced on 2018/2/6. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | - (void)reloadWindow; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // VVPlayground 4 | // 5 | // Created by isaced on 2018/2/6. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "MainViewController.h" 10 | #import "SettingsViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 17 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[MainViewController alloc] initWithStyle:UITableViewStyleGrouped]]; 18 | self.window.backgroundColor = [UIColor whiteColor]; 19 | [self.window makeKeyAndVisible]; 20 | 21 | // Nav 22 | [[UINavigationBar appearance] setTintColor:[UIColor darkGrayColor]]; 23 | 24 | return YES; 25 | } 26 | 27 | - (void)reloadWindow { 28 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[MainViewController alloc] initWithStyle:UITableViewStyleGrouped]]; 29 | } 30 | 31 | @end 32 | 33 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/ic_toys.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_toys.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/ic_toys.imageset/ic_toys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/RealtimePreview/RealtimePreview/Assets.xcassets/ic_toys.imageset/ic_toys.png -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/scan.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "scan@3x.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/scan.imageset/scan@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/RealtimePreview/RealtimePreview/Assets.xcassets/scan.imageset/scan@3x.png -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "settings@3x.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Assets.xcassets/settings.imageset/settings@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/RealtimePreview/RealtimePreview/Assets.xcassets/settings.imageset/settings@3x.png -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/HotReloadService.h: -------------------------------------------------------------------------------- 1 | // 2 | // HotReloadService.h 3 | // VirtualViewDemo 4 | // 5 | // Created by isaced on 2018/1/31. 6 | // 7 | 8 | #import 9 | 10 | typedef void(^HotReloadServiceDataBlock)(NSData * data); 11 | typedef void(^HotReloadServiceDictBlock)(NSDictionary * dict); 12 | typedef void(^HotReloadServiceArrayBlock)(NSArray * array); 13 | typedef void(^HotReloadServiceTemplateBlock)(NSArray *templates, NSDictionary *params); 14 | 15 | @interface HotReloadService : NSObject 16 | 17 | + (NSString *)hostIP; 18 | + (NSURL *)hostURL; 19 | + (NSString *)hostURLString; 20 | 21 | /** 22 | 根据模版名从 Host 加载 data.json 23 | */ 24 | + (void)fetchTemplateByName:(NSString *)templateName callback:(HotReloadServiceTemplateBlock)callback; 25 | 26 | /** 27 | 根据扫描来的 URL 加载模版 28 | */ 29 | + (void)fetchTemplateByDataJsonURL:(NSURL *)fetchTemplateByDataJsonURL callback:(HotReloadServiceTemplateBlock)callback; 30 | 31 | /** 32 | 获取 VVTool 模版名列表 33 | */ 34 | + (void)fetchTemplateNameListWithCallback:(HotReloadServiceArrayBlock)callback; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/HotReloadService.m: -------------------------------------------------------------------------------- 1 | // 2 | // HotReloadService.m 3 | // VirtualViewDemo 4 | // 5 | // Created by isaced on 2018/1/31. 6 | // 7 | 8 | #import "HotReloadService.h" 9 | 10 | static NSString *const kKeyInPlistHostIP = @"ASC_HOST_IP"; 11 | 12 | @implementation HotReloadService 13 | 14 | + (NSString *)hostIP { 15 | NSString *ip = [[NSBundle mainBundle] objectForInfoDictionaryKey:kKeyInPlistHostIP]; 16 | if (!ip) { 17 | ip = @"127.0.0.1"; 18 | } 19 | return ip; 20 | } 21 | 22 | + (NSURL *)hostURL { 23 | return [NSURL URLWithString:[self hostURLString]]; 24 | } 25 | 26 | + (NSString *)hostURLString { 27 | return [NSString stringWithFormat:@"http://%@:7788/", [self hostIP]]; 28 | } 29 | 30 | + (void)requestWithURL:(NSURL *)url completionHandler:(HotReloadServiceDataBlock)completionHandler { 31 | if (!url) { 32 | completionHandler(nil); 33 | return; 34 | } 35 | 36 | NSURLSessionConfiguration *conf = [NSURLSessionConfiguration defaultSessionConfiguration]; 37 | conf.requestCachePolicy = NSURLRequestReloadIgnoringCacheData; 38 | NSURLSession *session = [NSURLSession sessionWithConfiguration:conf]; 39 | [[session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { 40 | if (![NSThread isMainThread]) { 41 | dispatch_async(dispatch_get_main_queue(), ^{ 42 | completionHandler(data); 43 | }); 44 | }else{ 45 | completionHandler(data); 46 | } 47 | }] resume]; 48 | } 49 | 50 | + (void)fetchTemplateByName:(NSString *)templateName callback:(HotReloadServiceTemplateBlock)callback { 51 | if (![self hostIP] || ! templateName) { 52 | callback(nil, nil); 53 | return; 54 | } 55 | 56 | NSURL *url = [[[self hostURL] URLByAppendingPathComponent:templateName] URLByAppendingPathComponent:@"data.json"]; 57 | [self fetchTemplateByDataJsonURL:url callback:callback]; 58 | } 59 | 60 | + (void)fetchTemplateByDataJsonURL:(NSURL *)fetchTemplateByDataJsonURL callback:(HotReloadServiceTemplateBlock)callback { 61 | if (!fetchTemplateByDataJsonURL) { 62 | callback(nil, nil); 63 | return; 64 | } 65 | 66 | [self requestWithURL:fetchTemplateByDataJsonURL completionHandler:^(NSData *data) { 67 | NSDictionary *dict; 68 | if (data) { 69 | dict = [NSJSONSerialization JSONObjectWithData:data 70 | options:NSJSONReadingAllowFragments 71 | error:nil]; 72 | } 73 | 74 | // 模版 75 | NSMutableArray *templates = [NSMutableArray array]; 76 | if (dict) { 77 | for (NSString *templateBase64String in dict[@"templates"]) { 78 | NSData *templateData = [[NSData alloc] initWithBase64EncodedString:templateBase64String options:NSDataBase64DecodingIgnoreUnknownCharacters]; 79 | [templates addObject:templateData]; 80 | } 81 | } 82 | 83 | // 数据 84 | NSDictionary *params = dict[@"data"]; 85 | if (callback) { 86 | callback(templates, params); 87 | } 88 | }]; 89 | } 90 | 91 | + (void)fetchTemplateNameListWithCallback:(HotReloadServiceArrayBlock)callback { 92 | NSURL *url = [[self hostURL] URLByAppendingPathComponent:@".dir"]; 93 | [self requestWithURL:url completionHandler:^(NSData *data) { 94 | NSArray *array; 95 | if (data) { 96 | array = [NSJSONSerialization JSONObjectWithData:data 97 | options:NSJSONReadingAllowFragments 98 | error:nil]; 99 | } 100 | callback(array); 101 | }]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface MainViewController : UITableViewController 11 | @end 12 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/ScanViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ScanViewController.h 3 | // VVPlayground 4 | // 5 | // Created by isaced on 2018/2/7. 6 | // 7 | 8 | #import 9 | 10 | @protocol ScanViewControllerDelegate 11 | 12 | - (void)scanViewController:(UIViewController *)scanViewController didScanContent:(NSString *)content; 13 | 14 | @end 15 | 16 | @interface ScanViewController : UIViewController 17 | 18 | @property (nonatomic, weak) id delegate; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/SettingsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.h 3 | // VVPlayground 4 | // 5 | // Created by isaced on 2018/2/9. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | static NSString *const kSettingsRTLKey = @"kSettingsRTLKey"; 12 | static NSString *const kSettingsHotReloadKey = @"kSettingsHotReloadKey"; 13 | 14 | @interface SettingsViewController : XLFormViewController 15 | 16 | + (void)configSetValue:(NSObject *)value forKey:(NSString *)key; 17 | + (id)configValueForKey:(NSString *)key; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/SettingsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.m 3 | // VVPlayground 4 | // 5 | // Created by isaced on 2018/2/9. 6 | // 7 | 8 | #import "SettingsViewController.h" 9 | #import "XLForm.h" 10 | #import "AppDelegate.h" 11 | #import "HotReloadService.h" 12 | 13 | @interface SettingsViewController () 14 | 15 | @end 16 | 17 | @implementation SettingsViewController 18 | 19 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 20 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 21 | if (self){ 22 | [self initializeForm]; 23 | } 24 | return self; 25 | } 26 | 27 | - (id)initWithCoder:(NSCoder *)aDecoder { 28 | self = [super initWithCoder:aDecoder]; 29 | if (self){ 30 | [self initializeForm]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)initializeForm { 36 | XLFormDescriptor * form; 37 | XLFormSectionDescriptor * section; 38 | 39 | form = [XLFormDescriptor formDescriptorWithTitle:@"Settings"]; 40 | 41 | section = [XLFormSectionDescriptor formSection]; 42 | [form addFormSection:section]; 43 | 44 | XLFormRowDescriptor * hotReloadRTL = [XLFormRowDescriptor formRowDescriptorWithTag:@"HotReload" rowType:XLFormRowDescriptorTypeBooleanSwitch title:@"List HotReload"]; 45 | hotReloadRTL.value = [SettingsViewController configValueForKey:kSettingsHotReloadKey]; 46 | hotReloadRTL.onChangeBlock = ^(id _Nullable oldValue, id _Nullable newValue, XLFormRowDescriptor * _Nonnull rowDescriptor) { 47 | [SettingsViewController configSetValue:newValue forKey:kSettingsHotReloadKey]; 48 | }; 49 | [section addFormRow:hotReloadRTL]; 50 | 51 | 52 | XLFormSectionDescriptor *section2 = [XLFormSectionDescriptor formSection]; 53 | XLFormRowDescriptor *serverIPRow = [XLFormRowDescriptor formRowDescriptorWithTag:@"serverIP" rowType:XLFormRowDescriptorTypeInfo title:@"Server IP"]; 54 | serverIPRow.value = [HotReloadService hostIP]; 55 | [section2 addFormRow:serverIPRow]; 56 | 57 | XLFormRowDescriptor *versionRow = [XLFormRowDescriptor formRowDescriptorWithTag:@"appversion" rowType:XLFormRowDescriptorTypeInfo title:@"Version"]; 58 | NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"]; 59 | NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey]; 60 | NSString *versionString = [NSString stringWithFormat:@"%@ (%@)", version, build]; 61 | versionRow.value = versionString; 62 | [section2 addFormRow:versionRow]; 63 | [form addFormSection:section2]; 64 | 65 | self.form = form; 66 | } 67 | 68 | #pragma mark Userdefaults 69 | 70 | + (void)configSetValue:(NSObject *)value forKey:(NSString *)key { 71 | [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; 72 | } 73 | 74 | + (id)configValueForKey:(NSString *)key { 75 | return [[NSUserDefaults standardUserDefaults] objectForKey:key]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Classes/TestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface TestViewController : UIViewController 11 | 12 | @property (nonatomic, strong) NSDictionary *params; 13 | 14 | @property (nonatomic, assign) BOOL hotReload; 15 | 16 | 17 | /** 18 | 文件名 19 | */ 20 | - (instancetype)initWithFilename:(NSString *)filename; 21 | 22 | /** 23 | 文件名 24 | */ 25 | - (instancetype)initWithHotReloadTemplateName:(NSString *)templateName; 26 | 27 | /** 28 | URL 29 | */ 30 | - (instancetype)initWithTemplateBaseURL:(NSURL *)templateBaseURL; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /RealtimePreview/RealtimePreview/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RealtimePreview 4 | // 5 | // Created by isaced on 2018/4/16. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /VirtualView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "VirtualView" 4 | s.version = "1.2.7" 5 | s.summary = "A solution to create & release UI component dynamically." 6 | s.homepage = "https://github.com/alibaba/VirtualView-iOS" 7 | s.license = { :type => 'MIT' } 8 | s.author = { "HarrisonXi" => "gpra8764@gmail.com" } 9 | s.platform = :ios 10 | s.ios.deployment_target = '8.0' 11 | s.requires_arc = true 12 | s.source = { :git => "https://github.com/alibaba/VirtualView-iOS.git", :tag => '1.2.7' } 13 | s.source_files = 'VirtualView/**/*.{h,m}' 14 | s.prefix_header_contents = '#import "VVDefines.h"' 15 | 16 | s.dependency 'SDWebImage', '~> 4.2' 17 | 18 | end 19 | -------------------------------------------------------------------------------- /VirtualView/Core/VVBinaryStringMapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVBinaryStringMapper.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | // Internal supported string list. 11 | // Key is hash of string by default. 12 | // 13 | // @"action", @"actionParam", @"alignContent", @"alignItems", @"alignSelf", @"alpha", @"ancestor", 14 | // @"animatorStyle", @"animatorTime", @"autoDimDirection", @"autoDimX", @"autoDimY", 15 | // @"autoRefreshThreshold", @"autoSwitch", @"autoSwitchTime", @"background", @"backgroundImage", 16 | // @"blurRadius", @"borderBottomLeftRadius", @"borderBottomRightRadius", @"borderColor", 17 | // @"borderRadius", @"borderTopLeftRadius", @"borderTopRightRadius", @"borderWidth", @"canSlide", 18 | // @"children", @"ck", @"class", @"colCount", @"color", @"Component", @"Container", @"containerID", 19 | // @"dashEffect", @"data", @"dataMode", @"dataParam", @"dataTag", @"dataUrl", @"diameterX", 20 | // @"diameterY", @"disableCache", @"disablePlaceHolder", @"do", @"ellipsize", @"else", @"elseif", 21 | // @"filterWhiteBg", @"firstSpace", @"fixBy", @"flag", @"flexBasis", @"flexDirection", @"flexFlow", 22 | // @"flexGrow", @"FlexLayout", @"flexShrink", @"flexWrap", @"for", @"FrameLayout", @"gravity", 23 | // @"Grid", @"GridLayout", @"id", @"if", @"initValue", @"itemHeight", @"itemHorizontalMargin", 24 | // @"itemMargin", @"itemVerticalMargin", @"itemWidth", @"justifyContent", @"lastSpace", 25 | // @"layoutDirection", @"layoutGravity", @"layoutHeight", @"layoutMarginBottom", @"layoutMarginLeft", 26 | // @"layoutMarginRight", @"layoutMarginTop", @"layoutOrientation", @"layoutRatio", @"layoutWidth", 27 | // @"lines", @"lineSpace", @"lineSpaceExtra", @"lineSpaceMultiplier", @"List", @"maskColor", 28 | // @"maxLines", @"minHeight", @"minWidth", @"mode", @"module", @"name", @"NImage", @"NLine", @"NText", 29 | // @"onAfterDataLoad", @"onAutoRefresh", @"onBeforeDataLoad", @"onClick", @"onLongClick", 30 | // @"onPageFlip", @"onScroll", @"onSetData", @"order", @"orientation", @"paddingBottom", 31 | // @"paddingLeft", @"paddingRight", @"paddingTop", @"Page", @"paintStyle", @"paintWidth", @"parent", 32 | // @"pos", @"Progress", @"ratio", @"RatioLayout", @"scaleType", @"Scroller", @"self", @"siblings", 33 | // @"size", @"Slider", @"span", @"src", @"stayTime", @"style", @"supportHTMLStyle", @"supportSticky", 34 | // @"tag", @"text", @"textColor", @"textSize", @"textStyle", @"this", @"TMNImage", @"TMVImage", 35 | // @"type", @"typeface", @"uuid", @"var", @"VGraph", @"VH", @"VH2Layout", @"VHLayout", @"VImage", 36 | // @"visibility", @"VLine", @"vList", @"VText", @"VTime", @"waterfall", @"while" 37 | 38 | @interface VVBinaryStringMapper : NSObject 39 | 40 | + (nullable NSString *)stringForKey:(int)key; 41 | 42 | /** 43 | DO NOT use this method unless you know what you are doing. 44 | Please use "registerString:" to register new custom string. 45 | 46 | @param string The string to be resistered. 47 | @param key The specified key. 48 | */ 49 | + (void)registerString:(nonnull NSString *)string forKey:(int)key; 50 | 51 | /** 52 | Register a string, the key will be hash of the string. 53 | 54 | @param string The string to be resistered. 55 | */ 56 | + (void)registerString:(nonnull NSString *)string; 57 | 58 | + (int)hashOfString:(nonnull NSString *)string; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /VirtualView/Core/VVBinaryStringMapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVBinaryStringMapper.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVBinaryStringMapper.h" 9 | 10 | @interface VVBinaryStringMapper () 11 | 12 | @property (nonatomic, strong) NSMutableDictionary *mapperDict; 13 | 14 | @end 15 | 16 | @implementation VVBinaryStringMapper 17 | 18 | + (VVBinaryStringMapper *)sharedMapper 19 | { 20 | static VVBinaryStringMapper *_sharedMapper; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | _sharedMapper = [VVBinaryStringMapper new]; 24 | }); 25 | return _sharedMapper; 26 | } 27 | 28 | - (instancetype)init 29 | { 30 | if (self = [super init]) { 31 | NSArray *strings = @[ 32 | @"action", @"actionParam", @"alignContent", @"alignItems", @"alignSelf", @"alpha", @"ancestor", 33 | @"animatorStyle", @"animatorTime", @"autoDimDirection", @"autoDimX", @"autoDimY", 34 | @"autoRefreshThreshold", @"autoSwitch", @"autoSwitchTime", @"background", @"backgroundImage", 35 | @"blurRadius", @"borderBottomLeftRadius", @"borderBottomRightRadius", @"borderColor", 36 | @"borderRadius", @"borderTopLeftRadius", @"borderTopRightRadius", @"borderWidth", @"canSlide", 37 | @"children", @"ck", @"class", @"colCount", @"color", @"Component", @"Container", @"containerID", 38 | @"dashEffect", @"data", @"dataMode", @"dataParam", @"dataTag", @"dataUrl", @"diameterX", 39 | @"diameterY", @"disableCache", @"disablePlaceHolder", @"do", @"ellipsize", @"else", @"elseif", 40 | @"filterWhiteBg", @"firstSpace", @"fixBy", @"flag", @"flexBasis", @"flexDirection", @"flexFlow", 41 | @"flexGrow", @"FlexLayout", @"flexShrink", @"flexWrap", @"for", @"FrameLayout", @"gravity", 42 | @"Grid", @"GridLayout", @"id", @"if", @"initValue", @"itemHeight", @"itemHorizontalMargin", 43 | @"itemMargin", @"itemVerticalMargin", @"itemWidth", @"justifyContent", @"lastSpace", 44 | @"layoutDirection", @"layoutGravity", @"layoutHeight", @"layoutMarginBottom", @"layoutMarginLeft", 45 | @"layoutMarginRight", @"layoutMarginTop", @"layoutOrientation", @"layoutRatio", @"layoutWidth", 46 | @"lines", @"lineSpace", @"lineSpaceExtra", @"lineSpaceMultiplier", @"List", @"maskColor", 47 | @"maxLines", @"minHeight", @"minWidth", @"mode", @"module", @"name", @"NImage", @"NLine", @"NText", 48 | @"onAfterDataLoad", @"onAutoRefresh", @"onBeforeDataLoad", @"onClick", @"onLongClick", 49 | @"onPageFlip", @"onScroll", @"onSetData", @"order", @"orientation", @"paddingBottom", 50 | @"paddingLeft", @"paddingRight", @"paddingTop", @"Page", @"paintStyle", @"paintWidth", @"parent", 51 | @"pos", @"Progress", @"ratio", @"RatioLayout", @"scaleType", @"Scroller", @"self", @"siblings", 52 | @"size", @"Slider", @"span", @"src", @"stayTime", @"style", @"supportHTMLStyle", @"supportSticky", 53 | @"tag", @"text", @"textColor", @"textSize", @"textStyle", @"this", @"TMNImage", @"TMVImage", 54 | @"type", @"typeface", @"uuid", @"var", @"VGraph", @"VH", @"VH2Layout", @"VHLayout", @"VImage", 55 | @"visibility", @"VLine", @"vList", @"VText", @"VTime", @"waterfall", @"while" 56 | #ifdef VV_ALIBABA 57 | , @"height", @"marginBottom", @"marginLeft", @"marginRight", @"marginTop", @"videoInfo", 58 | @"VVNavtiveViewContainer", @"width" 59 | #endif 60 | ]; 61 | _mapperDict = [NSMutableDictionary dictionaryWithCapacity:strings.count]; 62 | for (NSString *string in strings) { 63 | [_mapperDict setObject:string forKey:@([VVBinaryStringMapper hashOfString:string])]; 64 | } 65 | #ifdef VV_ALIBABA 66 | [_mapperDict setObject:@"inMainThread" forKey:@(STR_ID_inMainThread)]; 67 | #endif 68 | } 69 | return self; 70 | } 71 | 72 | + (NSString *)stringForKey:(int)key 73 | { 74 | return [[VVBinaryStringMapper sharedMapper].mapperDict objectForKey:@(key)]; 75 | } 76 | 77 | + (void)registerString:(NSString *)string forKey:(int)key 78 | { 79 | if (string && string.length > 0) { 80 | [[VVBinaryStringMapper sharedMapper].mapperDict setObject:string forKey:@(key)]; 81 | } 82 | } 83 | 84 | + (void)registerString:(NSString *)string 85 | { 86 | if (string && string.length > 0) { 87 | [self registerString:string forKey:[self hashOfString:string]]; 88 | } 89 | } 90 | 91 | + (int)hashOfString:(NSString *)string 92 | { 93 | int hash = 0; 94 | if (string && string.length > 0) { 95 | const char *chars = [string UTF8String]; 96 | NSInteger length = strlen(chars); 97 | for (NSInteger index = 0; index < length; index++) { 98 | hash = (hash * 31) + chars[index]; 99 | } 100 | } 101 | return hash; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /VirtualView/Core/VVConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVConfig.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface VVConfig : NSObject 11 | 12 | /** 13 | RP is a length unit of UI design. 14 | If the size of UI design is 750 x 1334: 15 | 16 | pointRatio = [UIScreen mainScreen].bounds.size.width / 750 17 | 18 | Then you can use RP as a length in your xml template. 19 | For example, on iPhone SE: 20 | 21 | pointRatio = 320 / 750 = 0.4266667 22 | 75 RP = (75 x pointRatio) PT = 32 PT 23 | */ 24 | @property (nonatomic, assign, class) CGFloat pointRatio; 25 | 26 | /** 27 | Always resize and layout whole node tree. 28 | VVViewContainer will use this value as its default value. 29 | Default value is NO. 30 | */ 31 | @property (nonatomic, assign, class) BOOL alwaysRefresh; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /VirtualView/Core/VVConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVConfig.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVConfig.h" 9 | #ifdef VV_ALIBABA 10 | #import "Orange.h" 11 | #endif 12 | 13 | @interface VVConfig () 14 | 15 | @property (nonatomic, assign) CGFloat pointRatio; 16 | @property (nonatomic, assign) BOOL alwaysRefresh; 17 | 18 | @end 19 | 20 | @implementation VVConfig 21 | 22 | + (VVConfig *)sharedConfig 23 | { 24 | static VVConfig *_sharedConfig; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | _sharedConfig = [VVConfig new]; 28 | }); 29 | return _sharedConfig; 30 | } 31 | 32 | - (instancetype)init 33 | { 34 | if (self = [super init]) { 35 | _pointRatio = [UIScreen mainScreen].bounds.size.width / 750; 36 | #ifdef VV_ALIBABA 37 | NSString *alwaysRefreshConfig = [Orange getConfigByGroupName:@"tangram" key:@"vv_forceRefresh" defaultConfig:nil isDefault:nil]; 38 | _alwaysRefresh = [alwaysRefreshConfig integerValue] != 0; 39 | #endif 40 | } 41 | return self; 42 | } 43 | 44 | + (CGFloat)pointRatio 45 | { 46 | return [self sharedConfig].pointRatio; 47 | } 48 | 49 | + (void)setPointRatio:(CGFloat)pointRatio 50 | { 51 | [self sharedConfig].pointRatio = pointRatio; 52 | } 53 | 54 | + (BOOL)alwaysRefresh 55 | { 56 | return [self sharedConfig].alwaysRefresh; 57 | } 58 | 59 | + (void)setAlwaysRefresh:(BOOL)alwaysRefresh 60 | { 61 | [self sharedConfig].alwaysRefresh = alwaysRefresh; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /VirtualView/Core/VVErrors.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVErrors.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | FOUNDATION_EXTERN NSErrorDomain const VVErrorDomain; 11 | 12 | #if __IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_10_2 13 | NS_ENUM(NSInteger) { 14 | #else 15 | NS_ERROR_ENUM(VVErrorDomain) { 16 | #endif 17 | VVUnknownError = 0, 18 | VVNeedToBeOverridedError, 19 | VVWrongHeaderError, 20 | VVInvalidDataError 21 | }; 22 | 23 | #define VVMakeError(errorCode, errorDescription) [NSError errorWithDomain:VVErrorDomain code:errorCode userInfo:@{NSLocalizedDescriptionKey : [NSString stringWithFormat:@"%@ - %@: %@", self.class, NSStringFromSelector(_cmd), errorDescription]}] 24 | -------------------------------------------------------------------------------- /VirtualView/Core/VVErrors.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVErrors.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVErrors.h" 9 | 10 | NSErrorDomain const VVErrorDomain = @"VVErrorDomain"; 11 | -------------------------------------------------------------------------------- /VirtualView/Core/VVNodeClassMapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVNodeClassMapper.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | // Internal supported node list. 11 | #define VV_NODE_ID_FrameLayout 1 12 | #define VV_NODE_ID_VHLayout 2 13 | #define VV_NODE_ID_VH2Layout 3 14 | #define VV_NODE_ID_GridLayout 4 15 | #define VV_NODE_ID_RatioLayout 6 16 | #define VV_NODE_ID_NativeText 7 17 | #define VV_NODE_ID_VirtualText 8 18 | #define VV_NODE_ID_NativeImage 9 19 | #define VV_NODE_ID_VirtualImage 10 20 | #define VV_NODE_ID_NativeLine 13 21 | #define VV_NODE_ID_VirtualLine 14 22 | #define VV_NODE_ID_Page 16 23 | #define VV_NODE_ID_Grid 17 24 | #define VV_NODE_ID_NFrameLayout 25 25 | #define VV_NODE_ID_NGridLayout 26 26 | #define VV_NODE_ID_NRatioLayout 27 27 | #define VV_NODE_ID_NVH2Layout 28 28 | #define VV_NODE_ID_NVHLayout 29 29 | #define VV_NODE_TYPE_FrameLayout @"FrameLayout" 30 | #define VV_NODE_TYPE_VHLayout @"VHLayout" 31 | #define VV_NODE_TYPE_VH2Layout @"VH2Layout" 32 | #define VV_NODE_TYPE_GridLayout @"GridLayout" 33 | #define VV_NODE_TYPE_RatioLayout @"RatioLayout" 34 | #define VV_NODE_TYPE_NativeText @"NativeText" 35 | #define VV_NODE_TYPE_VirtualText @"VirtualText" 36 | #define VV_NODE_TYPE_NativeImage @"NativeImage" 37 | #define VV_NODE_TYPE_VirtualImage @"VirtualImage" 38 | #define VV_NODE_TYPE_NativeLine @"NativeLine" 39 | #define VV_NODE_TYPE_VirtualLine @"VirtualLine" 40 | #define VV_NODE_TYPE_Page @"Page" 41 | #define VV_NODE_TYPE_Grid @"Grid" 42 | #define VV_NODE_TYPE_NFrameLayout @"NFrameLayout" 43 | #define VV_NODE_TYPE_NGridLayout @"NGridLayout" 44 | #define VV_NODE_TYPE_NRatioLayout @"NRatioLayout" 45 | #define VV_NODE_TYPE_NVH2Layout @"NVH2Layout" 46 | #define VV_NODE_TYPE_NVHLayout @"NVHLayout" 47 | 48 | @interface VVNodeClassMapper : NSObject 49 | 50 | + (nullable NSString *)classNameForID:(short)nodeID; 51 | + (void)registerClassName:(nonnull NSString *)className forID:(short)nodeID; 52 | + (nullable NSString *)classNameForType:(nonnull NSString *)type; 53 | + (void)registerClassName:(nonnull NSString *)className forType:(nonnull NSString *)type; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /VirtualView/Core/VVSystemKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVSystemKey.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | This class will be deprecated. 12 | DO NOT use it. 13 | */ 14 | @interface VVSystemKey : NSObject 15 | 16 | + (nonnull VVSystemKey *)shareInstance __deprecated; 17 | 18 | //@property (nonatomic, strong, readonly, nonnull) NSDictionary *keyDictionary __deprecated_msg("use [VVBinaryStringMapper stringForKey:]"); 19 | 20 | @property (nonatomic, assign) CGFloat rate __deprecated_msg("use VVConfig.pointRatio"); 21 | 22 | //- (nullable NSString *)classNameForIndex:(short)index __deprecated_msg("use [VVNodeClassMapper classNameForID:]"); 23 | //- (void)registerWidget:(nonnull NSString *)className withIndex:(short)index __deprecated_msg("use [VVNodeClassMapper registerClassName:forID:]"); 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /VirtualView/Core/VVSystemKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVSystemKey.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVSystemKey.h" 9 | #import "VVConfig.h" 10 | 11 | @implementation VVSystemKey 12 | 13 | + (VVSystemKey *)shareInstance 14 | { 15 | static VVSystemKey* _shareInstance; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | _shareInstance = [[VVSystemKey alloc] init]; 19 | }); 20 | return _shareInstance; 21 | } 22 | 23 | - (void)setRate:(CGFloat)rate 24 | { 25 | VVConfig.pointRatio = rate; 26 | } 27 | 28 | - (CGFloat)rate 29 | { 30 | return VVConfig.pointRatio; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /VirtualView/Core/VVViewContainer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVViewContainer.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVBaseNode.h" 10 | 11 | @protocol VirtualViewDelegate 12 | 13 | @optional 14 | 15 | - (void)virtualViewClickedWithAction:(NSString *)action andValue:(NSString *)value; 16 | - (void)virtualView:(VVBaseNode *)node clickedWithAction:(NSString *)action andValue:(NSString *)value; 17 | - (void)virtualViewLongPressedWithAction:(NSString *)action andValue:(NSString *)value; 18 | - (void)virtualView:(VVBaseNode *)node longPressedWithAction:(NSString *)action andValue:(NSString *)value; 19 | 20 | @end 21 | 22 | @interface VVViewContainer : UIView 23 | 24 | @property (nonatomic, strong, readonly) VVBaseNode *rootNode; 25 | @property (nonatomic, weak) id delegate; 26 | @property (nonatomic, assign, readonly) BOOL alwaysRefresh; 27 | 28 | + (VVViewContainer *)viewContainerWithTemplateType:(NSString *)type; 29 | + (VVViewContainer *)viewContainerWithTemplateType:(NSString *)type alwaysRefresh:(BOOL)alwaysRefresh; 30 | - (id)initWithRootNode:(VVBaseNode *)rootNode; 31 | - (id)initWithRootNode:(VVBaseNode *)rootNode alwaysRefresh:(BOOL)alwaysRefresh; 32 | 33 | /** 34 | Get estimated size of VirtualView. 35 | Cannot get correct size if VirtualView will change size after binding data (wrap_content). 36 | If you want to calculate size via this method, try these steps: 37 | (will cost a lot of CPU resources & not tested adequately) 38 | 1. call "updateData:" 39 | 2. call "estimatedSize:" to calculate correct size 40 | 3. update the frame of VVViewContainer 41 | 4. call "updatelayout" 42 | */ 43 | - (CGSize)estimatedSize:(CGSize)maxSize; 44 | /** 45 | Get established size of VirtualView. 46 | Will get zero size if VirtualView will change size after binding data (wrap_content). 47 | */ 48 | - (CGSize)establishedSize:(CGSize)maxSize; 49 | /** 50 | Get established fixed size of VirtualView. 51 | Will get zero size if size of VirtualView is not fixed. 52 | */ 53 | - (CGSize)establishedSize; 54 | 55 | /** 56 | Bind new data to VirtualView and resize & layout it if it is necessary. 57 | */ 58 | - (void)update:(id)data; 59 | /** 60 | Bind new data to VirtualView. 61 | */ 62 | - (void)updateData:(id)data; 63 | /** 64 | Resize & layout VirtualView if it is necessary. 65 | */ 66 | - (void)updateLayout; 67 | 68 | - (VVBaseNode *)nodeWithID:(NSInteger)nodeID; 69 | 70 | + (NSArray *)variableNodes:(VVBaseNode *)rootNode; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/Expression/VVExpression.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVExpression.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface VVExpression : NSObject 11 | 12 | /** 13 | Return an expression if the expression string is valid. 14 | If you want to use this method of subclasses directly, please trim the string at first. 15 | 16 | @param string Expression string 17 | @return An expression or nil. 18 | */ 19 | + (nullable VVExpression *)expressionWithString:(nonnull NSString *)string; 20 | 21 | /** 22 | Get result with specified object. 23 | 24 | @param object An array or dictionary. 25 | @return Expression result. 26 | */ 27 | - (nullable id)resultWithObject:(nullable id)object; 28 | 29 | @end 30 | 31 | @interface VVConstExpression : VVExpression 32 | 33 | @end 34 | 35 | @interface VVVariableExpression : VVExpression 36 | 37 | @end 38 | 39 | @interface VVIifExpression : VVExpression 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/Expression/VVPropertyExpressionSetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyExpressionSetter.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertySetter.h" 9 | #import "VVExpression.h" 10 | 11 | @interface VVPropertyExpressionSetter : VVPropertySetter 12 | 13 | @property (nonatomic, strong, readonly, nullable) VVExpression *expression; 14 | 15 | @property (nonatomic, assign, readonly) int valueType; 16 | 17 | /** 18 | Return an expression setter if the expression string is valid. 19 | Will return nil is expression string is a const expression. 20 | 21 | @param key Property key. 22 | @param expressionString Expression string. 23 | @return An expression setter or nil. 24 | */ 25 | + (nullable VVPropertyExpressionSetter *)setterWithPropertyKey:(int)key expressionString:(nullable NSString *)expressionString; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyFloatSetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyFloatSetter.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertySetter.h" 9 | 10 | @interface VVPropertyFloatSetter : VVPropertySetter 11 | 12 | @property (nonatomic, assign, readonly) CGFloat value; 13 | 14 | + (nonnull VVPropertyFloatSetter *)setterWithPropertyKey:(int)key floatValue:(CGFloat)value; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyFloatSetter.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyFloatSetter.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertyFloatSetter.h" 9 | 10 | @interface VVPropertyFloatSetter () 11 | 12 | @property (nonatomic, assign, readwrite) CGFloat value; 13 | 14 | @end 15 | 16 | @implementation VVPropertyFloatSetter 17 | 18 | + (VVPropertyFloatSetter *)setterWithPropertyKey:(int)key floatValue:(CGFloat)value 19 | { 20 | VVPropertyFloatSetter *setter = [[self alloc] initWithPropertyKey:key]; 21 | setter.value = value; 22 | return setter; 23 | } 24 | 25 | - (NSString *)description 26 | { 27 | return [NSString stringWithFormat:@"<%@: %p; name = %@; value = %f>", self.class, self, self.name, self.value]; 28 | } 29 | 30 | - (void)applyToNode:(VVBaseNode *)node 31 | { 32 | BOOL handled; 33 | handled = [node setFloatValue:self.value forKey:self.key]; 34 | #ifdef VV_DEBUG 35 | NSAssert(handled == YES, @"Property is not handled."); 36 | #endif 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyIntSetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyIntSetter.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertySetter.h" 9 | 10 | @interface VVPropertyIntSetter : VVPropertySetter 11 | 12 | @property (nonatomic, assign, readonly) int value; 13 | 14 | + (nonnull VVPropertyIntSetter *)setterWithPropertyKey:(int)key intValue:(int)value; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyIntSetter.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyIntSetter.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertyIntSetter.h" 9 | 10 | @interface VVPropertyIntSetter () 11 | 12 | @property (nonatomic, assign, readwrite) int value; 13 | 14 | @end 15 | 16 | @implementation VVPropertyIntSetter 17 | 18 | + (VVPropertyIntSetter *)setterWithPropertyKey:(int)key intValue:(int)value 19 | { 20 | VVPropertyIntSetter *setter = [[self alloc] initWithPropertyKey:key]; 21 | setter.value = value; 22 | return setter; 23 | } 24 | 25 | - (NSString *)description 26 | { 27 | return [NSString stringWithFormat:@"<%@: %p; name = %@; value = %d>", self.class, self, self.name, self.value]; 28 | } 29 | 30 | - (void)applyToNode:(VVBaseNode *)node 31 | { 32 | BOOL handled; 33 | handled = [node setIntValue:self.value forKey:self.key]; 34 | #ifdef VV_DEBUG 35 | NSAssert(handled == YES, @"Property is not handled."); 36 | #endif 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertySetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertySetter.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVBaseNode.h" 10 | 11 | /** 12 | This is a base class for all PropertySetter. DO NOT use it directly. 13 | Please use the sub class of it. 14 | PropertySetter can save 'property => value' key-value-pair. 15 | It is used for setting a property with one call. 16 | 17 | 这是所有 PropertySetter 类的基类,请不要直接使用。请使用它的子类。 18 | PropertySetter 可以存储 'property => value' 的键值对。 19 | 它是用于一键设置对象属性的。 20 | */ 21 | @interface VVPropertySetter : NSObject 22 | 23 | @property (nonatomic, assign, readonly) int key; 24 | @property (nonatomic, strong, readonly, nullable) NSString *name; 25 | 26 | - (nonnull instancetype)initWithPropertyKey:(int)key; 27 | 28 | /** 29 | If this setter is an expression setter. 30 | For expression setter, you should implement and use "applyToNode:withDict:". 31 | Default value is NO, subclass must override this method to return YES. 32 | 33 | @return YES for expression setter. 34 | */ 35 | - (BOOL)isExpression; 36 | - (void)applyToNode:(nullable VVBaseNode *)node; 37 | - (void)applyToNode:(nullable VVBaseNode *)node withObject:(nullable NSDictionary *)object; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertySetter.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertySetter.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertySetter.h" 9 | #import "VVBinaryStringMapper.h" 10 | 11 | @implementation VVPropertySetter 12 | 13 | - (instancetype)initWithPropertyKey:(int)key 14 | { 15 | if (self = [super init]) { 16 | _key = key; 17 | _name = [VVBinaryStringMapper stringForKey:key]; 18 | if (!_name) { 19 | #ifdef VV_DEBUG 20 | NSLog(@"VVPropertySetter - Key does not match a string: %d", key); 21 | #endif 22 | _name = [NSString stringWithFormat:@"%d", key]; 23 | } 24 | } 25 | return self; 26 | } 27 | 28 | - (NSString *)description 29 | { 30 | return [NSString stringWithFormat:@"<%@: %p; name = %@>", self.class, self, self.name]; 31 | } 32 | 33 | - (BOOL)isExpression 34 | { 35 | return NO; 36 | } 37 | 38 | - (void)applyToNode:(VVBaseNode *)node 39 | { 40 | // override me 41 | if ([self isExpression] == YES) { 42 | [self applyToNode:node withObject:nil]; 43 | } 44 | } 45 | 46 | - (void)applyToNode:(VVBaseNode *)node withObject:(nullable NSDictionary *)object 47 | { 48 | // override me 49 | if ([self isExpression] == NO) { 50 | [self applyToNode:node]; 51 | } 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyStringSetter.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyStringSetter.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertySetter.h" 9 | 10 | @interface VVPropertyStringSetter : VVPropertySetter 11 | 12 | @property (nonatomic, copy, readonly, nullable) NSString *value; 13 | 14 | /** 15 | Make and return a string setter for normal string. 16 | Make and return an expression setter for expression string. 17 | Expression string starts with "@{" or @"${". 18 | 19 | @param key Property key. 20 | @param value Property value. 21 | @return A string setter or a string expression setter. 22 | */ 23 | + (nonnull VVPropertySetter *)setterWithPropertyKey:(int)key stringValue:(nullable NSString *)value; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /VirtualView/Creater/Setter/VVPropertyStringSetter.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVPropertyStringSetter.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVPropertyStringSetter.h" 9 | #import "VVPropertyExpressionSetter.h" 10 | 11 | @interface VVPropertyStringSetter () 12 | 13 | @property (nonatomic, copy, readwrite) NSString *value; 14 | 15 | @end 16 | 17 | @implementation VVPropertyStringSetter 18 | 19 | + (VVPropertySetter *)setterWithPropertyKey:(int)key stringValue:(NSString *)value 20 | { 21 | VVPropertyExpressionSetter *expressionSetter = [VVPropertyExpressionSetter setterWithPropertyKey:key expressionString:value]; 22 | if (expressionSetter) { 23 | return expressionSetter; 24 | } 25 | VVPropertyStringSetter *setter = [[self alloc] initWithPropertyKey:key]; 26 | setter.value = value; 27 | return setter; 28 | } 29 | 30 | - (NSString *)description 31 | { 32 | return [NSString stringWithFormat:@"<%@: %p; name = %@; value = %@>", self.class, self, self.name, self.value]; 33 | } 34 | 35 | - (void)applyToNode:(VVBaseNode *)node 36 | { 37 | BOOL handled; 38 | handled = [node setStringValue:self.value forKey:self.key]; 39 | #ifdef VV_DEBUG 40 | NSAssert(handled == YES, @"Property is not handled."); 41 | #endif 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /VirtualView/Creater/VVNodeCreater.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVNodeCreater.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class VVPropertySetter; 11 | @class VVBaseNode; 12 | 13 | /** 14 | VVNodeCreater's struct is similar with XML struct. 15 | It is used for creating a node tree with one call. 16 | 17 | VVNodeCreater 的结构和 XML 的结构十分类似。 18 | 它是用来一键创建一个 node 树的。 19 | */ 20 | @interface VVNodeCreater : NSObject 21 | 22 | @property (nonatomic, copy, nullable) NSString *nodeClassName; 23 | @property (nonatomic, strong, nonnull) NSMutableArray *propertySetters; 24 | @property (nonatomic, strong, nonnull) NSMutableArray *subCreaters; 25 | 26 | - (nonnull VVBaseNode *)createNodeTree; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /VirtualView/Creater/VVNodeCreater.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVNodeCreater.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVNodeCreater.h" 9 | #import "VVPropertySetter.h" 10 | #import "VVBaseNode.h" 11 | 12 | @implementation VVNodeCreater 13 | 14 | - (NSString *)description 15 | { 16 | return [NSString stringWithFormat:@"<%@: %p; nodeClassName = %@>", self.class, self, self.nodeClassName]; 17 | } 18 | 19 | - (NSMutableArray *)propertySetters 20 | { 21 | if (!_propertySetters) { 22 | _propertySetters = [NSMutableArray array]; 23 | } 24 | return _propertySetters; 25 | } 26 | 27 | - (NSMutableArray *)subCreaters 28 | { 29 | if (!_subCreaters) { 30 | _subCreaters = [NSMutableArray array]; 31 | } 32 | return _subCreaters; 33 | } 34 | 35 | - (VVBaseNode *)createNodeTree 36 | { 37 | Class class = NSClassFromString(self.nodeClassName); 38 | 39 | #ifdef VV_DEBUG 40 | NSAssert(class != NULL, @"Does not match a class."); 41 | #endif 42 | 43 | VVBaseNode *node; 44 | if (class != NULL) { 45 | node = [class new]; 46 | } else { 47 | node = [VVBaseNode new]; 48 | } 49 | 50 | for (VVPropertySetter *setter in self.propertySetters) { 51 | if ([setter isExpression] == NO) { 52 | [setter applyToNode:node]; 53 | } else { 54 | [node.expressionSetters setObject:setter forKey:setter.name]; 55 | } 56 | } 57 | 58 | for (VVNodeCreater *creater in self.subCreaters) { 59 | VVBaseNode *subNode = [creater createNodeTree]; 60 | [node addSubNode:subNode]; 61 | } 62 | 63 | return node; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVBinaryLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVBinaryLoader.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVVersionModel.h" 10 | 11 | /** 12 | This class will be deprecated. 13 | DO NOT use it. 14 | */ 15 | @interface VVBinaryLoader : NSObject 16 | 17 | + (nonnull id)shareInstance __deprecated; 18 | 19 | /** 20 | Load VirtualView template from binary buffer and return the version of it. 21 | 22 | @param buff Binary buffer. 23 | @return Version of template, will be nil if the loading is failed. 24 | */ 25 | - (nullable VVVersionModel *)loadFromBuffer:(nonnull NSData *)buff __deprecated_msg("use [VVTemplateManager loadTemplateData:forType:]"); 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVBinaryLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVBinaryLoader.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVBinaryLoader.h" 9 | #import "VVTemplateManager.h" 10 | 11 | @implementation VVBinaryLoader 12 | 13 | + (id)shareInstance 14 | { 15 | static VVBinaryLoader *shareInstance_; 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | shareInstance_ = [VVBinaryLoader new]; 19 | }); 20 | return shareInstance_; 21 | } 22 | 23 | - (VVVersionModel *)loadFromBuffer:(NSData *)buff 24 | { 25 | return [[VVTemplateManager sharedManager] loadTemplateData:buff forType:nil]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVTemplateBinaryLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVTemplateBinaryLoader.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVTemplateLoader.h" 9 | 10 | @interface VVTemplateBinaryLoader : VVTemplateLoader 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVTemplateLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVTemplateLoader.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVVersionModel.h" 10 | #import "VVNodeCreater.h" 11 | 12 | /** 13 | This is a base class for all TemplateLoader. DO NOT use it directly. 14 | Please use the sub class of it. 15 | TemplateLoader can load template and store it with a NodeCreater instance. 16 | Then you will be able to create a node tree via that NodeCreater. 17 | */ 18 | @interface VVTemplateLoader : NSObject 19 | 20 | /** 21 | Error of last template loading. Will be nil if loading is successed. 22 | */ 23 | @property (nonatomic, strong, readonly, nullable) NSError *lastError; 24 | 25 | /** 26 | Version of last template loading. Will be nil if loading is failed. 27 | */ 28 | @property (nonatomic, strong, readonly, nullable) VVVersionModel *lastVersion; 29 | 30 | /** 31 | Type of last template loading. Will be nil if loading is failed. 32 | */ 33 | @property (nonatomic, strong, readonly, nullable) NSString *lastType; 34 | 35 | /** 36 | VVNodeCreater of last template loading. Will be nil if loading is failed. 37 | */ 38 | @property (nonatomic, strong, readonly, nullable) VVNodeCreater *lastCreater; 39 | 40 | /** 41 | Load template data synchronously. 42 | 43 | @param data Template data. 44 | @return Is loading successed. 45 | */ 46 | - (BOOL)loadTemplateData:(nonnull NSData *)data; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVTemplateLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVTemplateLoader.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVTemplateLoader.h" 9 | #import "VVErrors.h" 10 | 11 | @interface VVTemplateLoader () 12 | 13 | @property (nonatomic, strong, readwrite) NSError *lastError; 14 | @property (nonatomic, strong, readwrite) VVVersionModel *lastVersion; 15 | @property (nonatomic, strong, readwrite) NSString *lastType; 16 | @property (nonatomic, strong, readwrite) VVNodeCreater *lastCreater; 17 | 18 | @end 19 | 20 | @implementation VVTemplateLoader 21 | 22 | - (BOOL)loadTemplateData:(NSData *)data 23 | { 24 | // override me 25 | self.lastError = VVMakeError(VVNeedToBeOverridedError, @"Need to be overrided."); 26 | self.lastVersion = nil; 27 | self.lastType = nil; 28 | self.lastCreater = nil; 29 | return NO; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVVersionModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVVersionModel.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // :) 7 | 8 | #import 9 | 10 | @interface VVVersionModel : NSObject 11 | 12 | @property (nonatomic, assign, readonly) NSUInteger major; 13 | @property (nonatomic, assign, readonly) NSUInteger minor; 14 | @property (nonatomic, assign, readonly) NSUInteger patch; 15 | 16 | - (nonnull instancetype)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor patch:(NSUInteger)patch; 17 | 18 | - (BOOL)isEqual:(nullable id)object; 19 | - (NSComparisonResult)compare:(nonnull VVVersionModel *)aVersion; 20 | 21 | - (nonnull NSString *)stringValue; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVVersionModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVVersionModel.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // :) 7 | 8 | #import "VVVersionModel.h" 9 | 10 | @interface VVVersionModel () { 11 | NSString *_stringValue; 12 | } 13 | 14 | @end 15 | 16 | @implementation VVVersionModel 17 | 18 | - (instancetype)initWithMajor:(NSUInteger)major minor:(NSUInteger)minor patch:(NSUInteger)patch 19 | { 20 | if (self = [super init]) { 21 | _major = major; 22 | _minor = minor; 23 | _patch = patch; 24 | } 25 | return self; 26 | } 27 | 28 | - (NSString *)description 29 | { 30 | return [NSString stringWithFormat:@"<%@: %p; version = %@>", self.class, self, self.stringValue]; 31 | } 32 | 33 | - (BOOL)isEqual:(id)object 34 | { 35 | if ([object isKindOfClass:[VVVersionModel class]]) { 36 | return [self compare:object] == NSOrderedSame; 37 | } 38 | return NO; 39 | } 40 | 41 | - (NSComparisonResult)compare:(VVVersionModel *)aVersion 42 | { 43 | if (aVersion == nil) { 44 | return NSOrderedDescending; 45 | #ifdef VV_DEBUG 46 | NSAssert(NO, @"Input version is nil."); 47 | #endif 48 | } 49 | 50 | if (aVersion.major > self.major) { 51 | return NSOrderedAscending; 52 | } else if (aVersion.major < self.major) { 53 | return NSOrderedDescending; 54 | } else { 55 | if (aVersion.minor > self.minor) { 56 | return NSOrderedAscending; 57 | } else if (aVersion.minor < self.minor) { 58 | return NSOrderedDescending; 59 | } else { 60 | if (aVersion.patch > self.patch) { 61 | return NSOrderedAscending; 62 | } else if (aVersion.patch < self.patch) { 63 | return NSOrderedDescending; 64 | } else { 65 | return NSOrderedSame; 66 | } 67 | } 68 | } 69 | } 70 | 71 | - (NSString *)stringValue 72 | { 73 | if (!_stringValue) { 74 | _stringValue = [NSString stringWithFormat:@"%zd.%zd.%zd", self.major, self.minor, self.patch]; 75 | } 76 | return _stringValue; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVViewFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVViewFactory.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVViewContainer.h" 10 | 11 | /** 12 | This class will be deprecated. 13 | DO NOT use it. 14 | */ 15 | @interface VVViewFactory : NSObject 16 | 17 | + (VVViewFactory *)shareFactoryInstance __deprecated; 18 | 19 | /** 20 | Create the node tree. Return a VVViewContainer that contains the node tree. 21 | 22 | @param key Template type 23 | @return VVViewContainer with created node tree. 24 | */ 25 | - (VVViewContainer *)obtainVirtualWithKey:(NSString *)key __deprecated_msg("use [VVViewContainer viewContainerWithTemplateType:]"); 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /VirtualView/Loader/VVViewFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVViewFactory.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVViewFactory.h" 9 | 10 | @implementation VVViewFactory 11 | 12 | + (VVViewFactory *)shareFactoryInstance 13 | { 14 | static VVViewFactory *shareFactory_; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | shareFactory_ = [VVViewFactory new]; 18 | }); 19 | return shareFactory_; 20 | } 21 | 22 | - (VVViewContainer *)obtainVirtualWithKey:(NSString *)key 23 | { 24 | return [VVViewContainer viewContainerWithTemplateType:key]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /VirtualView/Node/Layer/VVLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVLayer.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface VVLayer : CALayer 11 | 12 | @property (nonatomic, assign) CGFloat vv_borderRadius; 13 | @property (nonatomic, assign) CGFloat vv_borderTopLeftRadius; 14 | @property (nonatomic, assign) CGFloat vv_borderTopRightRadius; 15 | @property (nonatomic, assign) CGFloat vv_borderBottomLeftRadius; 16 | @property (nonatomic, assign) CGFloat vv_borderBottomRightRadius; 17 | @property (nonatomic, assign) CGFloat vv_borderWidth; 18 | @property (nonatomic, strong, nullable) UIColor *vv_borderColor; 19 | @property (nonatomic, strong, nullable) UIColor *vv_backgroundColor; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /VirtualView/Node/Layer/VVLineLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVLineLayer.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface VVLineLayer : CALayer 11 | 12 | @property (nonatomic, strong, nullable) UIColor *vv_lineColor; 13 | @property (nonatomic, assign) CGFloat vv_lineWidth; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /VirtualView/Node/Layer/VVLineLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVLineLayer.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLineLayer.h" 9 | #import 10 | 11 | @interface VVLineLayer () { 12 | CGFloat _width; 13 | CGFloat _height; 14 | } 15 | 16 | @property (nonatomic, assign, readonly) CGSize vv_size; 17 | 18 | @end 19 | 20 | @implementation VVLineLayer 21 | 22 | @dynamic vv_size; 23 | 24 | - (instancetype)init 25 | { 26 | if (self = [super init]) { 27 | _vv_lineWidth = 1; 28 | _vv_lineColor = [UIColor blackColor]; 29 | VVSetNeedsDisplayObserve(vv_size); 30 | VVSetNeedsDisplayObserve(vv_lineColor); 31 | VVSetNeedsDisplayObserve(vv_lineWidth); 32 | } 33 | return self; 34 | } 35 | 36 | - (void)dealloc 37 | { 38 | [self vv_removeAllObservers]; 39 | } 40 | 41 | - (void)setVv_lineWidth:(CGFloat)vv_lineWidth 42 | { 43 | _vv_lineWidth = vv_lineWidth > 0 ? vv_lineWidth : 0; 44 | } 45 | 46 | - (void)setFrame:(CGRect)frame 47 | { 48 | [self willChangeValueForKey:@"vv_size"]; 49 | [super setFrame:frame]; 50 | _width = CGRectGetWidth(frame); 51 | _height = CGRectGetHeight(frame); 52 | [self didChangeValueForKey:@"vv_size"]; 53 | } 54 | 55 | - (CGSize)vv_size 56 | { 57 | return CGSizeMake(_width, _height); 58 | } 59 | 60 | - (void)createPath:(CGContextRef)context 61 | { 62 | if (_width > _height) { 63 | CGFloat midY = _height / 2; 64 | CGContextMoveToPoint(context, 0, midY); 65 | CGContextAddLineToPoint(context, _width, midY); 66 | } else { 67 | CGFloat midX = _width / 2; 68 | CGContextMoveToPoint(context, midX, 0); 69 | CGContextAddLineToPoint(context, midX, _height); 70 | } 71 | } 72 | 73 | - (void)drawInContext:(CGContextRef)context 74 | { 75 | CGContextClearRect(context, CGRectMake(0, 0, _width, _height)); 76 | 77 | if (self.vv_lineWidth > 0 78 | && self.vv_lineColor 79 | && [self.vv_lineColor isEqual:[UIColor clearColor]] == NO) { 80 | CGContextSetLineWidth(context, self.vv_lineWidth); 81 | CGContextSetStrokeColorWithColor(context, self.vv_lineColor.CGColor); 82 | [self createPath:context]; 83 | CGContextStrokePath(context); 84 | } 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVFrameLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVFrameLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVFrameLayout.h" 9 | 10 | @interface NVFrameLayout : VVFrameLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVFrameLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NVFrameLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVFrameLayout.h" 9 | 10 | @interface NVFrameLayout () 11 | 12 | @property (nonatomic, strong) UIView *containerView; 13 | @property (nonatomic, assign, readwrite) CGRect nodeFrame; 14 | 15 | @end 16 | 17 | @implementation NVFrameLayout 18 | 19 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 20 | @synthesize nodeFrame; 21 | 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _containerView = [[UIView alloc] init]; 26 | _containerView.backgroundColor = [UIColor clearColor]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VVLayer *)canvasLayer 32 | { 33 | return nil; 34 | } 35 | 36 | - (UIView *)cocoaView 37 | { 38 | return _containerView; 39 | } 40 | 41 | - (void)setRootCocoaView:(UIView *)rootCocoaView 42 | { 43 | _rootCocoaView = rootCocoaView; 44 | if (self.cocoaView.superview != rootCocoaView) { 45 | if (self.cocoaView.superview) { 46 | [self.cocoaView removeFromSuperview]; 47 | } 48 | [rootCocoaView addSubview:self.cocoaView]; 49 | } 50 | for (VVBaseNode *subNode in self.subNodes) { 51 | subNode.rootCocoaView = self.cocoaView; 52 | } 53 | } 54 | 55 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 56 | { 57 | _rootCanvasLayer = rootCanvasLayer; 58 | for (VVBaseNode *subNode in self.subNodes) { 59 | subNode.rootCanvasLayer = self.cocoaView.layer; 60 | } 61 | } 62 | 63 | - (void)setBackgroundColor:(UIColor *)backgroundColor 64 | { 65 | [super setBackgroundColor:backgroundColor]; 66 | self.cocoaView.backgroundColor = backgroundColor; 67 | } 68 | 69 | - (void)setBorderColor:(UIColor *)borderColor 70 | { 71 | [super setBorderColor:borderColor]; 72 | self.cocoaView.layer.borderColor = borderColor.CGColor; 73 | } 74 | 75 | - (void)setBorderWidth:(CGFloat)borderWidth 76 | { 77 | [super setBorderWidth:borderWidth]; 78 | self.cocoaView.layer.borderWidth = borderWidth; 79 | } 80 | 81 | - (void)setBorderRadius:(CGFloat)borderRadius 82 | { 83 | [super setBorderRadius:borderRadius]; 84 | self.cocoaView.layer.cornerRadius = borderRadius; 85 | self.cocoaView.clipsToBounds = YES; 86 | } 87 | 88 | - (VVBaseNode *)hitTest:(CGPoint)point 89 | { 90 | if (self.visibility == VVVisibilityVisible 91 | && CGRectContainsPoint(self.nodeFrame, point)) { 92 | if (self.subNodes.count > 0) { 93 | point.x -= self.nodeFrame.origin.x; 94 | point.y -= self.nodeFrame.origin.y; 95 | for (VVBaseNode* subNode in [self.subNodes reverseObjectEnumerator]) { 96 | VVBaseNode *hitNode = [subNode hitTest:point]; 97 | if (hitNode) { 98 | return hitNode; 99 | } 100 | } 101 | } 102 | if ([self isClickable] || [self isLongClickable]) { 103 | return self; 104 | } 105 | } 106 | return nil; 107 | } 108 | 109 | - (void)layoutSubNodes 110 | { 111 | CGPoint origin = self.nodeFrame.origin; 112 | self.nodeFrame = CGRectMake(0, 0, self.nodeFrame.size.width, self.nodeFrame.size.height); 113 | [super layoutSubNodes]; 114 | self.nodeFrame = CGRectMake(origin.x, origin.y, self.nodeFrame.size.width, self.nodeFrame.size.height); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVGridLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVGridLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVGridLayout.h" 9 | 10 | @interface NVGridLayout : VVGridLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVGridLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NVGridLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVGridLayout.h" 9 | 10 | @interface NVGridLayout () 11 | 12 | @property (nonatomic, strong) UIView *containerView; 13 | @property (nonatomic, assign, readwrite) CGRect nodeFrame; 14 | 15 | @end 16 | 17 | @implementation NVGridLayout 18 | 19 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 20 | @synthesize nodeFrame; 21 | 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _containerView = [[UIView alloc] init]; 26 | _containerView.backgroundColor = [UIColor clearColor]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VVLayer *)canvasLayer 32 | { 33 | return nil; 34 | } 35 | 36 | - (UIView *)cocoaView 37 | { 38 | return _containerView; 39 | } 40 | 41 | - (void)setRootCocoaView:(UIView *)rootCocoaView 42 | { 43 | _rootCocoaView = rootCocoaView; 44 | if (self.cocoaView.superview != rootCocoaView) { 45 | if (self.cocoaView.superview) { 46 | [self.cocoaView removeFromSuperview]; 47 | } 48 | [rootCocoaView addSubview:self.cocoaView]; 49 | } 50 | for (VVBaseNode *subNode in self.subNodes) { 51 | subNode.rootCocoaView = self.cocoaView; 52 | } 53 | } 54 | 55 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 56 | { 57 | _rootCanvasLayer = rootCanvasLayer; 58 | for (VVBaseNode *subNode in self.subNodes) { 59 | subNode.rootCanvasLayer = self.cocoaView.layer; 60 | } 61 | } 62 | 63 | - (void)setBackgroundColor:(UIColor *)backgroundColor 64 | { 65 | [super setBackgroundColor:backgroundColor]; 66 | self.cocoaView.backgroundColor = backgroundColor; 67 | } 68 | 69 | - (void)setBorderColor:(UIColor *)borderColor 70 | { 71 | [super setBorderColor:borderColor]; 72 | self.cocoaView.layer.borderColor = borderColor.CGColor; 73 | } 74 | 75 | - (void)setBorderWidth:(CGFloat)borderWidth 76 | { 77 | [super setBorderWidth:borderWidth]; 78 | self.cocoaView.layer.borderWidth = borderWidth; 79 | } 80 | 81 | - (void)setBorderRadius:(CGFloat)borderRadius 82 | { 83 | [super setBorderRadius:borderRadius]; 84 | self.cocoaView.layer.cornerRadius = borderRadius; 85 | self.cocoaView.clipsToBounds = YES; 86 | } 87 | 88 | - (VVBaseNode *)hitTest:(CGPoint)point 89 | { 90 | if (self.visibility == VVVisibilityVisible 91 | && CGRectContainsPoint(self.nodeFrame, point)) { 92 | if (self.subNodes.count > 0) { 93 | point.x -= self.nodeFrame.origin.x; 94 | point.y -= self.nodeFrame.origin.y; 95 | for (VVBaseNode* subNode in [self.subNodes reverseObjectEnumerator]) { 96 | VVBaseNode *hitNode = [subNode hitTest:point]; 97 | if (hitNode) { 98 | return hitNode; 99 | } 100 | } 101 | } 102 | if ([self isClickable] || [self isLongClickable]) { 103 | return self; 104 | } 105 | } 106 | return nil; 107 | } 108 | 109 | - (void)layoutSubNodes 110 | { 111 | CGPoint origin = self.nodeFrame.origin; 112 | self.nodeFrame = CGRectMake(0, 0, self.nodeFrame.size.width, self.nodeFrame.size.height); 113 | [super layoutSubNodes]; 114 | self.nodeFrame = CGRectMake(origin.x, origin.y, self.nodeFrame.size.width, self.nodeFrame.size.height); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVRatioLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVRatioLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVRatioLayout.h" 9 | 10 | @interface NVRatioLayout : VVRatioLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVRatioLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NVRatioLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVRatioLayout.h" 9 | 10 | @interface NVRatioLayout () 11 | 12 | @property (nonatomic, strong) UIView *containerView; 13 | @property (nonatomic, assign, readwrite) CGRect nodeFrame; 14 | 15 | @end 16 | 17 | @implementation NVRatioLayout 18 | 19 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 20 | @synthesize nodeFrame; 21 | 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _containerView = [[UIView alloc] init]; 26 | _containerView.backgroundColor = [UIColor clearColor]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VVLayer *)canvasLayer 32 | { 33 | return nil; 34 | } 35 | 36 | - (UIView *)cocoaView 37 | { 38 | return _containerView; 39 | } 40 | 41 | - (void)setRootCocoaView:(UIView *)rootCocoaView 42 | { 43 | _rootCocoaView = rootCocoaView; 44 | if (self.cocoaView.superview != rootCocoaView) { 45 | if (self.cocoaView.superview) { 46 | [self.cocoaView removeFromSuperview]; 47 | } 48 | [rootCocoaView addSubview:self.cocoaView]; 49 | } 50 | for (VVBaseNode *subNode in self.subNodes) { 51 | subNode.rootCocoaView = self.cocoaView; 52 | } 53 | } 54 | 55 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 56 | { 57 | _rootCanvasLayer = rootCanvasLayer; 58 | for (VVBaseNode *subNode in self.subNodes) { 59 | subNode.rootCanvasLayer = self.cocoaView.layer; 60 | } 61 | } 62 | 63 | - (void)setBackgroundColor:(UIColor *)backgroundColor 64 | { 65 | [super setBackgroundColor:backgroundColor]; 66 | self.cocoaView.backgroundColor = backgroundColor; 67 | } 68 | 69 | - (void)setBorderColor:(UIColor *)borderColor 70 | { 71 | [super setBorderColor:borderColor]; 72 | self.cocoaView.layer.borderColor = borderColor.CGColor; 73 | } 74 | 75 | - (void)setBorderWidth:(CGFloat)borderWidth 76 | { 77 | [super setBorderWidth:borderWidth]; 78 | self.cocoaView.layer.borderWidth = borderWidth; 79 | } 80 | 81 | - (void)setBorderRadius:(CGFloat)borderRadius 82 | { 83 | [super setBorderRadius:borderRadius]; 84 | self.cocoaView.layer.cornerRadius = borderRadius; 85 | self.cocoaView.clipsToBounds = YES; 86 | } 87 | 88 | - (VVBaseNode *)hitTest:(CGPoint)point 89 | { 90 | if (self.visibility == VVVisibilityVisible 91 | && CGRectContainsPoint(self.nodeFrame, point)) { 92 | if (self.subNodes.count > 0) { 93 | point.x -= self.nodeFrame.origin.x; 94 | point.y -= self.nodeFrame.origin.y; 95 | for (VVBaseNode* subNode in [self.subNodes reverseObjectEnumerator]) { 96 | VVBaseNode *hitNode = [subNode hitTest:point]; 97 | if (hitNode) { 98 | return hitNode; 99 | } 100 | } 101 | } 102 | if ([self isClickable] || [self isLongClickable]) { 103 | return self; 104 | } 105 | } 106 | return nil; 107 | } 108 | 109 | - (void)layoutSubNodes 110 | { 111 | CGPoint origin = self.nodeFrame.origin; 112 | self.nodeFrame = CGRectMake(0, 0, self.nodeFrame.size.width, self.nodeFrame.size.height); 113 | [super layoutSubNodes]; 114 | self.nodeFrame = CGRectMake(origin.x, origin.y, self.nodeFrame.size.width, self.nodeFrame.size.height); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVVH2Layout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVVH2Layout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVVH2Layout.h" 9 | 10 | @interface NVVH2Layout : VVVH2Layout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVVH2Layout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NVVH2Layout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVVH2Layout.h" 9 | 10 | @interface NVVH2Layout () 11 | 12 | @property (nonatomic, strong) UIView *containerView; 13 | @property (nonatomic, assign, readwrite) CGRect nodeFrame; 14 | 15 | @end 16 | 17 | @implementation NVVH2Layout 18 | 19 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 20 | @synthesize nodeFrame; 21 | 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _containerView = [[UIView alloc] init]; 26 | _containerView.backgroundColor = [UIColor clearColor]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VVLayer *)canvasLayer 32 | { 33 | return nil; 34 | } 35 | 36 | - (UIView *)cocoaView 37 | { 38 | return _containerView; 39 | } 40 | 41 | - (void)setRootCocoaView:(UIView *)rootCocoaView 42 | { 43 | _rootCocoaView = rootCocoaView; 44 | if (self.cocoaView.superview != rootCocoaView) { 45 | if (self.cocoaView.superview) { 46 | [self.cocoaView removeFromSuperview]; 47 | } 48 | [rootCocoaView addSubview:self.cocoaView]; 49 | } 50 | for (VVBaseNode *subNode in self.subNodes) { 51 | subNode.rootCocoaView = self.cocoaView; 52 | } 53 | } 54 | 55 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 56 | { 57 | _rootCanvasLayer = rootCanvasLayer; 58 | for (VVBaseNode *subNode in self.subNodes) { 59 | subNode.rootCanvasLayer = self.cocoaView.layer; 60 | } 61 | } 62 | 63 | - (void)setBackgroundColor:(UIColor *)backgroundColor 64 | { 65 | [super setBackgroundColor:backgroundColor]; 66 | self.cocoaView.backgroundColor = backgroundColor; 67 | } 68 | 69 | - (void)setBorderColor:(UIColor *)borderColor 70 | { 71 | [super setBorderColor:borderColor]; 72 | self.cocoaView.layer.borderColor = borderColor.CGColor; 73 | } 74 | 75 | - (void)setBorderWidth:(CGFloat)borderWidth 76 | { 77 | [super setBorderWidth:borderWidth]; 78 | self.cocoaView.layer.borderWidth = borderWidth; 79 | } 80 | 81 | - (void)setBorderRadius:(CGFloat)borderRadius 82 | { 83 | [super setBorderRadius:borderRadius]; 84 | self.cocoaView.layer.cornerRadius = borderRadius; 85 | self.cocoaView.clipsToBounds = YES; 86 | } 87 | 88 | - (VVBaseNode *)hitTest:(CGPoint)point 89 | { 90 | if (self.visibility == VVVisibilityVisible 91 | && CGRectContainsPoint(self.nodeFrame, point)) { 92 | if (self.subNodes.count > 0) { 93 | point.x -= self.nodeFrame.origin.x; 94 | point.y -= self.nodeFrame.origin.y; 95 | for (VVBaseNode* subNode in [self.subNodes reverseObjectEnumerator]) { 96 | VVBaseNode *hitNode = [subNode hitTest:point]; 97 | if (hitNode) { 98 | return hitNode; 99 | } 100 | } 101 | } 102 | if ([self isClickable] || [self isLongClickable]) { 103 | return self; 104 | } 105 | } 106 | return nil; 107 | } 108 | 109 | - (void)layoutSubNodes 110 | { 111 | CGPoint origin = self.nodeFrame.origin; 112 | self.nodeFrame = CGRectMake(0, 0, self.nodeFrame.size.width, self.nodeFrame.size.height); 113 | [super layoutSubNodes]; 114 | self.nodeFrame = CGRectMake(origin.x, origin.y, self.nodeFrame.size.width, self.nodeFrame.size.height); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVVHLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVVHLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVVHLayout.h" 9 | 10 | @interface NVVHLayout : VVVHLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/NVVHLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NVVHLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVVHLayout.h" 9 | 10 | @interface NVVHLayout () 11 | 12 | @property (nonatomic, strong) UIView *containerView; 13 | @property (nonatomic, assign, readwrite) CGRect nodeFrame; 14 | 15 | @end 16 | 17 | @implementation NVVHLayout 18 | 19 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 20 | @synthesize nodeFrame; 21 | 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _containerView = [[UIView alloc] init]; 26 | _containerView.backgroundColor = [UIColor clearColor]; 27 | } 28 | return self; 29 | } 30 | 31 | - (VVLayer *)canvasLayer 32 | { 33 | return nil; 34 | } 35 | 36 | - (UIView *)cocoaView 37 | { 38 | return _containerView; 39 | } 40 | 41 | - (void)setRootCocoaView:(UIView *)rootCocoaView 42 | { 43 | _rootCocoaView = rootCocoaView; 44 | if (self.cocoaView.superview != rootCocoaView) { 45 | if (self.cocoaView.superview) { 46 | [self.cocoaView removeFromSuperview]; 47 | } 48 | [rootCocoaView addSubview:self.cocoaView]; 49 | } 50 | for (VVBaseNode *subNode in self.subNodes) { 51 | subNode.rootCocoaView = self.cocoaView; 52 | } 53 | } 54 | 55 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 56 | { 57 | _rootCanvasLayer = rootCanvasLayer; 58 | for (VVBaseNode *subNode in self.subNodes) { 59 | subNode.rootCanvasLayer = self.cocoaView.layer; 60 | } 61 | } 62 | 63 | - (void)setBackgroundColor:(UIColor *)backgroundColor 64 | { 65 | [super setBackgroundColor:backgroundColor]; 66 | self.cocoaView.backgroundColor = backgroundColor; 67 | } 68 | 69 | - (void)setBorderColor:(UIColor *)borderColor 70 | { 71 | [super setBorderColor:borderColor]; 72 | self.cocoaView.layer.borderColor = borderColor.CGColor; 73 | } 74 | 75 | - (void)setBorderWidth:(CGFloat)borderWidth 76 | { 77 | [super setBorderWidth:borderWidth]; 78 | self.cocoaView.layer.borderWidth = borderWidth; 79 | } 80 | 81 | - (void)setBorderRadius:(CGFloat)borderRadius 82 | { 83 | [super setBorderRadius:borderRadius]; 84 | self.cocoaView.layer.cornerRadius = borderRadius; 85 | self.cocoaView.clipsToBounds = YES; 86 | } 87 | 88 | - (VVBaseNode *)hitTest:(CGPoint)point 89 | { 90 | if (self.visibility == VVVisibilityVisible 91 | && CGRectContainsPoint(self.nodeFrame, point)) { 92 | if (self.subNodes.count > 0) { 93 | point.x -= self.nodeFrame.origin.x; 94 | point.y -= self.nodeFrame.origin.y; 95 | for (VVBaseNode* subNode in [self.subNodes reverseObjectEnumerator]) { 96 | VVBaseNode *hitNode = [subNode hitTest:point]; 97 | if (hitNode) { 98 | return hitNode; 99 | } 100 | } 101 | } 102 | if ([self isClickable] || [self isLongClickable]) { 103 | return self; 104 | } 105 | } 106 | return nil; 107 | } 108 | 109 | - (void)layoutSubNodes 110 | { 111 | CGPoint origin = self.nodeFrame.origin; 112 | self.nodeFrame = CGRectMake(0, 0, self.nodeFrame.size.width, self.nodeFrame.size.height); 113 | [super layoutSubNodes]; 114 | self.nodeFrame = CGRectMake(origin.x, origin.y, self.nodeFrame.size.width, self.nodeFrame.size.height); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVFrameLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVFrameLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVFrameLayout : VVLayout 11 | 12 | @property (nonatomic, assign) VVGravity gravity; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVFrameLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVFrameLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVFrameLayout.h" 9 | 10 | @interface VVFrameLayout () 11 | 12 | @property (nonatomic, assign) BOOL updatingNeedsResize; 13 | 14 | @end 15 | 16 | @implementation VVFrameLayout 17 | 18 | - (instancetype)init 19 | { 20 | if (self = [super init]) { 21 | _gravity = VVGravityDefault; 22 | } 23 | return self; 24 | } 25 | 26 | - (BOOL)setIntValue:(int)value forKey:(int)key 27 | { 28 | BOOL ret = [super setIntValue:value forKey:key]; 29 | if (!ret) { 30 | if (key == STR_ID_gravity) { 31 | self.gravity = value; 32 | ret = YES; 33 | } 34 | } 35 | return ret; 36 | } 37 | 38 | - (void)layoutSubNodes 39 | { 40 | CGSize contentSize = self.contentSize; 41 | for (VVBaseNode *subNode in self.subNodes) { 42 | if (subNode.visibility == VVVisibilityGone) { 43 | [subNode updateHiddenRecursively]; 44 | continue; 45 | } 46 | CGSize subNodeSize = [subNode calculateSize:contentSize]; 47 | if ([subNode needLayout]) { 48 | VVGravity gravity = subNode.layoutGravity != VVGravityNone ? subNode.layoutGravity : self.gravity; 49 | 50 | if (gravity & VVGravityHCenter) { 51 | CGFloat midX = (self.nodeFrame.size.width + self.paddingLeft + subNode.marginLeft - subNode.marginRight - self.paddingRight) / 2; 52 | subNode.nodeX = midX - subNodeSize.width / 2; 53 | } else if (gravity & VVGravityRight) { 54 | subNode.nodeX = self.nodeFrame.size.width - self.paddingRight - subNode.marginRight - subNodeSize.width; 55 | } else { 56 | subNode.nodeX = self.paddingLeft + subNode.marginLeft; 57 | } 58 | 59 | if (gravity & VVGravityVCenter) { 60 | CGFloat midY = (self.nodeFrame.size.height + self.paddingTop + subNode.marginTop - subNode.marginBottom - self.paddingBottom) / 2; 61 | subNode.nodeY = midY - subNodeSize.height / 2; 62 | } else if (gravity & VVGravityBottom) { 63 | subNode.nodeY = self.nodeFrame.size.height - self.paddingBottom - subNode.marginBottom - subNodeSize.height; 64 | } else { 65 | subNode.nodeY = self.paddingTop + subNode.marginTop; 66 | } 67 | } 68 | [subNode updateHidden]; 69 | [subNode updateFrame]; 70 | [subNode layoutSubNodes]; 71 | } 72 | } 73 | 74 | - (CGSize)calculateSize:(CGSize)maxSize 75 | { 76 | [super calculateSize:maxSize]; 77 | if ((self.nodeWidth <= 0 && self.layoutWidth == VV_WRAP_CONTENT) 78 | || (self.nodeHeight <= 0 && self.layoutHeight == VV_WRAP_CONTENT)) { 79 | if (self.nodeWidth <= 0) { 80 | self.nodeWidth = maxSize.width - self.marginLeft - self.marginRight; 81 | } 82 | if (self.nodeHeight <= 0) { 83 | self.nodeHeight = maxSize.height - self.marginTop - self.marginBottom; 84 | } 85 | CGSize contentSize = self.contentSize; 86 | 87 | // Calculate size of subNodes. 88 | CGFloat maxSubNodeWidth = 0, maxSubNodeHeight = 0; // maximum container size of subNodes 89 | for (VVBaseNode *subNode in self.subNodes) { 90 | if (subNode.visibility == VVVisibilityGone) { 91 | continue; 92 | } 93 | [subNode calculateSize:contentSize]; 94 | CGSize subNodeContainerSize = subNode.containerSize; 95 | if (subNode.layoutWidth != VV_MATCH_PARENT) { 96 | maxSubNodeWidth = MAX(maxSubNodeWidth, subNodeContainerSize.width); 97 | } 98 | if (subNode.layoutHeight != VV_MATCH_PARENT) { 99 | maxSubNodeHeight = MAX(maxSubNodeHeight, subNodeContainerSize.height); 100 | } 101 | } 102 | 103 | if (self.layoutWidth == VV_WRAP_CONTENT) { 104 | self.nodeWidth = maxSubNodeWidth + self.paddingLeft + self.paddingRight; 105 | } 106 | if (self.layoutHeight == VV_WRAP_CONTENT) { 107 | self.nodeHeight = maxSubNodeHeight + self.paddingTop + self.paddingBottom; 108 | } 109 | [self applyAutoDim]; 110 | 111 | // Need to resize subNodes. 112 | self.updatingNeedsResize = YES; 113 | for (VVBaseNode *subNodes in self.subNodes) { 114 | if ([subNodes needResizeIfSuperNodeResize]) { 115 | [subNodes setNeedsResize]; 116 | } 117 | } 118 | self.updatingNeedsResize = NO; 119 | } 120 | return CGSizeMake(self.nodeWidth, self.nodeHeight); 121 | } 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVGridLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVGridLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVGridLayout : VVLayout 11 | 12 | @property (nonatomic, assign) int colCount; 13 | @property (nonatomic, assign, readonly) int rowCount; 14 | @property (nonatomic, assign) CGFloat itemHeight; 15 | @property (nonatomic, assign) CGFloat itemVerticalMargin; 16 | @property (nonatomic, assign) CGFloat itemHorizontalMargin; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVBaseNode.h" 9 | #import "VVLayer.h" 10 | 11 | @interface VVLayout : VVBaseNode 12 | 13 | @property (nonatomic, strong) VVLayer *canvasLayer; 14 | @property (nonatomic, assign) CGFloat borderRadius; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVLayout.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVLayout () { 11 | VVLayer *_privateLayer; 12 | } 13 | 14 | @end 15 | 16 | @implementation VVLayout 17 | 18 | - (id)init 19 | { 20 | self = [super init]; 21 | if (self) { 22 | _privateLayer = [[VVLayer alloc] init]; 23 | _privateLayer.contentsScale = [[UIScreen mainScreen] scale]; 24 | } 25 | return self; 26 | } 27 | 28 | - (BOOL)needCanvasLayer 29 | { 30 | if (self.borderColor && [self.borderColor isEqual:[UIColor clearColor]] == NO) { 31 | return YES; 32 | } 33 | if ([self.expressionSetters.allKeys containsObject:@"borderColor"]) { 34 | return YES; 35 | } 36 | if (self.backgroundColor && [self.backgroundColor isEqual:[UIColor clearColor]] == NO) { 37 | return YES; 38 | } 39 | if ([self.expressionSetters.allKeys containsObject:@"background"]) { 40 | return YES; 41 | } 42 | return NO; 43 | } 44 | 45 | - (VVLayer *)canvasLayer 46 | { 47 | if (_canvasLayer == nil && [self needCanvasLayer]) { 48 | _canvasLayer = _privateLayer; 49 | } 50 | return _canvasLayer; 51 | } 52 | 53 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 54 | { 55 | if (self.canvasLayer) { 56 | if (self.canvasLayer.superlayer) { 57 | [self.canvasLayer removeFromSuperlayer]; 58 | } 59 | [rootCanvasLayer addSublayer:self.canvasLayer]; 60 | } 61 | [super setRootCanvasLayer:rootCanvasLayer]; 62 | } 63 | 64 | - (void)updateHidden 65 | { 66 | [super updateHidden]; 67 | _privateLayer.hidden = self.hidden; 68 | } 69 | 70 | - (void)updateFrame 71 | { 72 | [super updateFrame]; 73 | _privateLayer.frame = self.nodeFrame; 74 | } 75 | 76 | - (void)setBackgroundColor:(UIColor *)backgroundColor 77 | { 78 | [super setBackgroundColor:backgroundColor]; 79 | _privateLayer.vv_backgroundColor = backgroundColor; 80 | } 81 | 82 | - (void)setBorderColor:(UIColor *)borderColor 83 | { 84 | [super setBorderColor:borderColor]; 85 | _privateLayer.vv_borderColor = borderColor; 86 | } 87 | 88 | - (void)setBorderWidth:(CGFloat)borderWidth 89 | { 90 | [super setBorderWidth:borderWidth]; 91 | _privateLayer.vv_borderWidth = borderWidth; 92 | } 93 | 94 | - (void)setBorderRadius:(CGFloat)borderRadius 95 | { 96 | _borderRadius = borderRadius; 97 | _privateLayer.vv_borderRadius = borderRadius; 98 | } 99 | 100 | - (BOOL)setFloatValue:(float)value forKey:(int)key 101 | { 102 | BOOL ret = [super setFloatValue:value forKey:key]; 103 | if (!ret) { 104 | ret = YES; 105 | switch (key) { 106 | case STR_ID_borderRadius: 107 | self.borderRadius = value; 108 | break; 109 | case STR_ID_borderTopLeftRadius: 110 | _privateLayer.vv_borderTopLeftRadius = value; 111 | break; 112 | case STR_ID_borderTopRightRadius: 113 | _privateLayer.vv_borderTopRightRadius = value; 114 | break; 115 | case STR_ID_borderBottomLeftRadius: 116 | _privateLayer.vv_borderBottomLeftRadius = value; 117 | break; 118 | case STR_ID_borderBottomRightRadius: 119 | _privateLayer.vv_borderBottomRightRadius = value; 120 | break; 121 | default: 122 | ret = NO; 123 | break; 124 | } 125 | } 126 | return ret; 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVRatioLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVRatioLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVRatioLayout : VVLayout 11 | 12 | @property (nonatomic, assign) VVOrientation orientation; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVVH2Layout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVVH2Layout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVVH2Layout : VVLayout 11 | 12 | @property (nonatomic, assign) VVOrientation orientation; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /VirtualView/Node/Layout/VVVHLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVVHLayout.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLayout.h" 9 | 10 | @interface VVVHLayout : VVLayout 11 | 12 | @property (nonatomic, assign) VVOrientation orientation; 13 | @property (nonatomic, assign) VVGravity gravity; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /VirtualView/Node/Magic/VVGridView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVGridView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NVGridLayout.h" 9 | 10 | @interface VVGridView : NVGridLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualView/Node/Magic/VVGridView.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVGridView.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVGridView.h" 9 | #import "VVViewContainer.h" 10 | #import "VVTemplateManager.h" 11 | #import "VVPropertyExpressionSetter.h" 12 | 13 | @interface VVGridView () 14 | 15 | @property (nonatomic, weak) id lastData; 16 | 17 | @end 18 | 19 | @implementation VVGridView 20 | 21 | @synthesize rootCocoaView = _rootCocoaView, rootCanvasLayer = _rootCanvasLayer; 22 | 23 | - (instancetype)init 24 | { 25 | if (self = [super init]) { 26 | _lastData = self; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)setRootCocoaView:(UIView *)rootCocoaView 32 | { 33 | _rootCocoaView = rootCocoaView; 34 | if (self.cocoaView.superview != rootCocoaView) { 35 | if (self.cocoaView.superview) { 36 | [self.cocoaView removeFromSuperview]; 37 | } 38 | [rootCocoaView addSubview:self.cocoaView]; 39 | } 40 | } 41 | 42 | - (void)setRootCanvasLayer:(CALayer *)rootCanvasLayer 43 | { 44 | _rootCanvasLayer = rootCanvasLayer; 45 | if (self.canvasLayer) { 46 | if (self.canvasLayer.superlayer) { 47 | [self.canvasLayer removeFromSuperlayer]; 48 | } 49 | [rootCanvasLayer addSublayer:self.canvasLayer]; 50 | } 51 | } 52 | 53 | - (BOOL)setDataObj:(NSObject *)obj forKey:(int)key 54 | { 55 | if (key == STR_ID_dataTag) { 56 | if (obj != _lastData && [obj isKindOfClass:[NSArray class]]) { 57 | _lastData = obj; 58 | 59 | NSMutableArray *unusedNodes = [self.subNodes mutableCopy]; // for reusing feature 60 | NSArray *dataArray = (NSArray *)obj; 61 | for (NSDictionary *itemData in dataArray) { 62 | if ([itemData isKindOfClass:[NSDictionary class]] == NO 63 | || [itemData.allKeys containsObject:@"type"] == NO) { 64 | continue; 65 | } 66 | NSString *nodeType = [itemData objectForKey:@"type"]; 67 | VVBaseNode *node = [VVGridView popReuseableNodeWithType:nodeType fromUnusedNodes:unusedNodes]; 68 | if (!node) { 69 | node = [[VVTemplateManager sharedManager] createNodeTreeForType:nodeType]; 70 | } 71 | NSArray *variableNodes = [VVViewContainer variableNodes:node]; 72 | for (VVBaseNode *variableNode in variableNodes) { 73 | [variableNode reset]; 74 | 75 | for (VVPropertyExpressionSetter *setter in variableNode.expressionSetters.allValues) { 76 | if ([setter isKindOfClass:[VVPropertyExpressionSetter class]]) { 77 | [setter applyToNode:variableNode withObject:itemData]; 78 | } 79 | } 80 | variableNode.actionValue = [itemData objectForKey:variableNode.action]; 81 | 82 | [variableNode didUpdated]; 83 | } 84 | if (node.superNode == nil) { 85 | [self addSubNode:node]; 86 | node.rootCanvasLayer = self.cocoaView.layer; 87 | node.rootCocoaView = self.cocoaView; 88 | } 89 | } 90 | for (VVBaseNode *unusedNode in unusedNodes) { 91 | [unusedNode removeFromSuperNode]; 92 | unusedNode.rootCanvasLayer = nil; 93 | unusedNode.rootCocoaView = nil; 94 | } 95 | } 96 | return YES; 97 | } 98 | return NO; 99 | } 100 | 101 | + (VVBaseNode *)popReuseableNodeWithType:(NSString *)nodeType fromUnusedNodes:(NSMutableArray *)unusedNodes 102 | { 103 | VVBaseNode *reuseableNode = nil; 104 | for (VVBaseNode *unusedNode in unusedNodes) { 105 | if ([unusedNode.templateType isEqualToString:nodeType]) { 106 | reuseableNode = unusedNode; 107 | break; 108 | } 109 | } 110 | if (reuseableNode) { 111 | [unusedNodes removeObject:reuseableNode]; 112 | } 113 | return reuseableNode; 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /VirtualView/Node/Magic/VVLoopingScrollView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVLoopingScrollView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | /** 11 | Fisrt & last item is used for looping. 12 | Please prepare them by yourself. 13 | */ 14 | @interface VVLoopingScrollView : UIScrollView 15 | 16 | @property (nonatomic, assign) BOOL autoSwitch; 17 | @property (nonatomic, assign) NSTimeInterval stayTime; 18 | @property (nonatomic, assign) NSTimeInterval autoSwitchTime; 19 | 20 | - (void)reset; 21 | - (void)startAutoSwitch; 22 | - (void)stopAutoSwitch; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /VirtualView/Node/Magic/VVLoopingScrollView.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVLoopingScrollView.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVLoopingScrollView.h" 9 | 10 | @interface VVLoopingScrollView () 11 | 12 | @property (nonatomic, strong) NSTimer *timer; 13 | @property (nonatomic, assign) BOOL inAnimation; 14 | 15 | @end 16 | 17 | @implementation VVLoopingScrollView 18 | 19 | - (instancetype)init 20 | { 21 | if (self = [super init]) { 22 | self.delegate = self; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)reset 28 | { 29 | if (self.contentSize.width > self.frame.size.width) { 30 | self.contentOffset = CGPointMake(self.frame.size.width, 0); 31 | } else { 32 | self.contentOffset = CGPointMake(0, self.frame.size.height); 33 | } 34 | 35 | [self stopAutoSwitch]; 36 | if (self.autoSwitch) { 37 | [self startAutoSwitch]; 38 | } 39 | } 40 | 41 | - (void)startAutoSwitch 42 | { 43 | _timer = [NSTimer scheduledTimerWithTimeInterval:_stayTime target:self selector:@selector(autoSwitchHandler:) userInfo:nil repeats:YES]; 44 | } 45 | 46 | - (void)stopAutoSwitch 47 | { 48 | if (_timer) { 49 | [_timer invalidate]; 50 | _timer = nil; 51 | } 52 | } 53 | 54 | - (void)autoSwitchHandler:(id)sender 55 | { 56 | if (!self.inAnimation) { 57 | self.inAnimation = YES; 58 | if (self.contentSize.width > self.frame.size.width) { 59 | [UIView animateWithDuration:_autoSwitchTime animations:^{ 60 | self.contentOffset = CGPointMake(self.contentOffset.x + self.frame.size.width, 0); 61 | } completion:^(BOOL finished) { 62 | [self scrollViewDidEndDecelerating:self]; 63 | self.inAnimation = NO; 64 | }]; 65 | } else { 66 | [UIView animateWithDuration:_autoSwitchTime animations:^{ 67 | self.contentOffset = CGPointMake(0, self.contentOffset.y + self.frame.size.height); 68 | } completion:^(BOOL finished) { 69 | [self scrollViewDidEndDecelerating:self]; 70 | self.inAnimation = NO; 71 | }]; 72 | } 73 | } 74 | } 75 | 76 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 77 | { 78 | if (decelerate == NO) { 79 | [self scrollViewDidEndDecelerating:scrollView]; 80 | } 81 | } 82 | 83 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 84 | { 85 | if (self.contentSize.width > self.frame.size.width) { 86 | if (self.contentOffset.x <= 0) { 87 | self.contentOffset = CGPointMake(self.contentSize.width - self.frame.size.width * 2, 0); 88 | } else if (self.contentOffset.x >= self.contentSize.width - self.frame.size.width) { 89 | self.contentOffset = CGPointMake(self.frame.size.width, 0); 90 | } 91 | } else { 92 | if (self.contentOffset.y <= 0) { 93 | self.contentOffset = CGPointMake(0, self.contentSize.height - self.frame.size.height * 2); 94 | } else if (self.contentOffset.y >= self.contentSize.height - self.frame.size.height) { 95 | self.contentOffset = CGPointMake(0, self.frame.size.height); 96 | } 97 | } 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /VirtualView/Node/Magic/VVPageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVPageView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVFrameLayout.h" 9 | 10 | @interface VVPageView : VVFrameLayout 11 | 12 | @property (nonatomic, assign) VVOrientation orientation; 13 | @property (nonatomic, assign) BOOL autoSwitch; 14 | @property (nonatomic, assign) BOOL canSlide; 15 | @property (nonatomic, assign) NSTimeInterval stayTime; 16 | @property (nonatomic, assign) NSTimeInterval autoSwitchTime; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /VirtualView/Node/Widget/NVImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVImageView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVBaseNode.h" 9 | 10 | @interface NVImageView : VVBaseNode 11 | 12 | @property (nonatomic, strong, readonly) UIImageView *imageView; 13 | 14 | @property (nonatomic, strong) NSString *src; 15 | @property (nonatomic, assign) VVScaleType scaleType; 16 | @property (nonatomic, assign) CGFloat ratio; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /VirtualView/Node/Widget/NVLineView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVLineView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVBaseNode.h" 10 | #import "VVLineLayer.h" 11 | 12 | @interface NVLineView : VVBaseNode 13 | 14 | @property (nonatomic, strong, nonnull) VVLineLayer *lineLayer; 15 | 16 | @property (nonatomic, assign) VVOrientation orientation; 17 | @property (nonatomic, assign) VVGravity gravity; 18 | //@property (nonatomic, assign) VVLineStyle style; 19 | //@property (nonatomic, assign) NSArray *dashEffect; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /VirtualView/Node/Widget/NVTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // NVTextView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVBaseNode.h" 10 | 11 | @interface VVLabel : UILabel 12 | 13 | @property (nonatomic, assign) CGFloat paddingLeft; 14 | @property (nonatomic, assign) CGFloat paddingTop; 15 | @property (nonatomic, assign) CGFloat paddingRight; 16 | @property (nonatomic, assign) CGFloat paddingBottom; 17 | 18 | @end 19 | 20 | //################################################################ 21 | 22 | @interface NVTextView : VVBaseNode 23 | 24 | @property (nonatomic, strong) VVLabel *textView; 25 | 26 | @property (nonatomic, strong) NSString *text; 27 | @property (nonatomic, strong, readonly) NSAttributedString *attributedText; 28 | @property (nonatomic, strong) UIColor *textColor; 29 | @property (nonatomic, assign) CGFloat textSize; 30 | @property (nonatomic, assign) VVTextStyle textStyle; 31 | @property (nonatomic, assign) VVEllipsize ellipsize; 32 | @property (nonatomic, assign) int lines; 33 | @property (nonatomic, assign) int maxLines; 34 | @property (nonatomic, assign) VVGravity gravity; 35 | @property (nonatomic, assign) CGFloat lineHeight; 36 | @property (nonatomic, assign) CGFloat lineSpaceExtra; // appearance may be different from Android 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /VirtualView/Observer/NSObject+VVObserver.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+VVObserver.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "VVObserver.h" 10 | 11 | @interface NSObject (VVObserver) 12 | 13 | - (void)vv_addObserverForKeyPath:(nonnull NSString *)keyPath block:(nonnull VVObserverBlock)block; 14 | - (void)vv_addObserverForKeyPath:(nonnull NSString *)keyPath selector:(nonnull SEL)selector; 15 | - (void)vv_addObserverForKeyPath:(nonnull NSString *)keyPath target:(nonnull id)target selector:(nonnull SEL)selector; 16 | - (void)vv_removeObserverForKeyPath:(nonnull NSString *)keyPath; 17 | - (void)vv_removeAllObservers; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /VirtualView/Observer/NSObject+VVObserver.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+VVObserver.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NSObject+VVObserver.h" 9 | #import 10 | 11 | @implementation NSObject (VVObserver) 12 | 13 | - (NSMutableArray *)vv_observers 14 | { 15 | NSMutableArray *observers = objc_getAssociatedObject(self, @"vv_observers"); 16 | if (!observers) { 17 | observers = [NSMutableArray array]; 18 | objc_setAssociatedObject(self, @"vv_observers", observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 19 | } 20 | return observers; 21 | } 22 | 23 | - (void)vv_addObserverForKeyPath:(NSString *)keyPath block:(nonnull VVObserverBlock)block 24 | { 25 | VVObserver *observer = [[VVObserver alloc] initWithBlock:block]; 26 | observer.name = keyPath; 27 | [self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:VVObserverContext]; 28 | [self.vv_observers addObject:observer]; 29 | } 30 | 31 | - (void)vv_addObserverForKeyPath:(NSString *)keyPath selector:(SEL)selector 32 | { 33 | [self vv_addObserverForKeyPath:keyPath target:self selector:selector]; 34 | } 35 | 36 | - (void)vv_addObserverForKeyPath:(NSString *)keyPath target:(nonnull id)target selector:(nonnull SEL)selector 37 | { 38 | VVObserver *observer = [[VVObserver alloc] initWithTarget:target selector:selector]; 39 | observer.name = keyPath; 40 | [self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:VVObserverContext]; 41 | [self.vv_observers addObject:observer]; 42 | } 43 | 44 | - (void)vv_removeObserverForKeyPath:(NSString *)keyPath 45 | { 46 | NSMutableArray *observers = objc_getAssociatedObject(self, @"vv_observers"); 47 | if (!observers) { 48 | return; 49 | } 50 | 51 | NSMutableSet *toBeRemoved = [NSMutableSet set]; 52 | for (VVObserver *observer in observers) { 53 | if ([observer.name isEqualToString:keyPath]) { 54 | [toBeRemoved addObject:observer]; 55 | } 56 | } 57 | for (VVObserver *observer in toBeRemoved) { 58 | [self removeObserver:observer forKeyPath:keyPath context:VVObserverContext]; 59 | [observers removeObject:observer]; 60 | } 61 | } 62 | 63 | - (void)vv_removeAllObservers 64 | { 65 | NSMutableArray *observers = objc_getAssociatedObject(self, @"vv_observers"); 66 | if (!observers) { 67 | return; 68 | } 69 | 70 | for (VVObserver *observer in observers) { 71 | [self removeObserver:observer forKeyPath:observer.name context:VVObserverContext]; 72 | } 73 | [observers removeAllObjects]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /VirtualView/Observer/VVObserver.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVObserver.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | FOUNDATION_EXPORT void * _Nonnull const VVObserverContext; 11 | 12 | typedef void(^VVObserverBlock)(id _Nonnull value); 13 | 14 | /** 15 | Helper class to use KVO easier. 16 | Try to use this with NSObject+VVObserver. 17 | */ 18 | @interface VVObserver : NSObject 19 | 20 | @property (nonatomic, copy, nullable) NSString *name; 21 | @property (nonatomic, copy, readonly, nullable) VVObserverBlock block; 22 | @property (nonatomic, weak, readonly, nullable) id targer; 23 | @property (nonatomic, assign, readonly, nullable) SEL selector; 24 | 25 | - (nonnull instancetype)initWithBlock:(nonnull VVObserverBlock)block; 26 | - (nonnull instancetype)initWithTarget:(nonnull id)target selector:(nonnull SEL)selector; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /VirtualView/Observer/VVObserver.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVObserver.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "VVObserver.h" 9 | 10 | void * const VVObserverContext = "VVObserverContext"; 11 | 12 | @implementation VVObserver 13 | 14 | - (instancetype)initWithBlock:(VVObserverBlock)block 15 | { 16 | if (self = [super init]) { 17 | _block = block; 18 | } 19 | return self; 20 | } 21 | 22 | - (instancetype)initWithTarget:(id)target selector:(SEL)selector 23 | { 24 | if (self = [super init]) { 25 | _targer = target; 26 | _selector = selector; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context 32 | { 33 | if (context == VVObserverContext) { 34 | id oldValue = [change objectForKey:NSKeyValueChangeOldKey]; 35 | id newValue = [change objectForKey:NSKeyValueChangeNewKey]; 36 | if ([newValue isEqual:oldValue] == NO) { 37 | if (self.block) { 38 | self.block(newValue); 39 | } else if (self.targer && self.selector && [self.targer respondsToSelector:self.selector]) { 40 | #pragma clang diagnostic push 41 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 42 | [self.targer performSelector:self.selector withObject:newValue]; 43 | #pragma clang diagnostic pop 44 | } 45 | } 46 | } 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /VirtualView/Utils/UIColor+VirtualView.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+VirtualView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface UIColor (VirtualView) 11 | 12 | /** 13 | 0xFF0000 => red 14 | 0x00FF00 => green 15 | 0x0000FF => blue 16 | 0xFFFF0000 => red (alpha = 1) 17 | 0x00FF0000 => red (alpha = 1) 18 | 19 | Alpha value will be discarded. 20 | 21 | @param rgb UInteger value. 22 | @return Color. 23 | */ 24 | + (nonnull UIColor *)vv_colorWithRGB:(NSUInteger)rgb; 25 | 26 | /** 27 | 0xFFFF0000 => red 28 | 0xFF00FF00 => green 29 | 0xFF0000FF => blue 30 | 0xFF0000 => clear (alpha = 0) 31 | 0x00FF00 => clear (alpha = 0) 32 | 33 | Alpha value always take effect. 34 | 35 | @param argb UInteger value. 36 | @return Color. Will discard RGB value if alpha is zero. 37 | */ 38 | + (nonnull UIColor *)vv_colorWithARGB:(NSUInteger)argb; 39 | 40 | /** 41 | @"#FF0000" => red 42 | @"#FF00FF00" => green 43 | @"0x0000FF" => blue 44 | @"0x00FFFFFF" => clear (alpha = 0) 45 | @"FFFFFF" => white 46 | @"00FFFFFF" => clear (alpha = 0) 47 | 48 | @param string String value. 49 | @return Color. Will return nil if string is invalid. 50 | */ 51 | + (nullable UIColor *)vv_colorWithString:(nonnull NSString *)string; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /VirtualView/Utils/UIColor+VirtualView.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+VirtualView.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "UIColor+VirtualView.h" 9 | 10 | @implementation UIColor (VirtualView) 11 | 12 | + (UIColor *)vv_colorWithRGB:(NSUInteger)rgb 13 | { 14 | CGFloat red = ((rgb & 0xFF0000) >> 16) / 255.0; 15 | CGFloat green = ((rgb & 0xFF00) >> 8) / 255.0; 16 | CGFloat blue = (rgb & 0xFF) / 255.0; 17 | return [UIColor colorWithRed:red green:green blue:blue alpha:1]; 18 | } 19 | 20 | + (UIColor *)vv_colorWithARGB:(NSUInteger)argb 21 | { 22 | CGFloat alpha = ((argb & 0xFF000000) >> 24) / 255.0; 23 | if (alpha == 0) { 24 | return [UIColor clearColor]; 25 | } 26 | 27 | CGFloat red = ((argb & 0xFF0000) >> 16) / 255.0; 28 | CGFloat green = ((argb & 0xFF00) >> 8) / 255.0; 29 | CGFloat blue = (argb & 0xFF) / 255.0; 30 | return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; 31 | } 32 | 33 | + (UIColor *)vv_colorWithString:(NSString *)string 34 | { 35 | if (!string || string.length == 0) { 36 | return nil; 37 | } 38 | 39 | if ([string hasPrefix:@"0x"]) { 40 | string = [string substringFromIndex:2]; 41 | } else if ([string hasPrefix:@"#"]) { 42 | string = [string substringFromIndex:1]; 43 | } 44 | if (string.length == 8) { 45 | unsigned int argb = 0; 46 | NSScanner *scanner = [NSScanner scannerWithString:string]; 47 | if ([scanner scanHexInt:&argb]) { 48 | return [UIColor vv_colorWithARGB:argb]; 49 | } 50 | } else if (string.length == 6) { 51 | unsigned int rgb = 0; 52 | NSScanner *scanner = [NSScanner scannerWithString:string]; 53 | if ([scanner scanHexInt:&rgb]) { 54 | return [UIColor vv_colorWithRGB:rgb]; 55 | } 56 | } 57 | return nil; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /VirtualView/Utils/UIView+VirtualView.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+VirtualView.h 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface UIView (VirtualView) 11 | 12 | /** 13 | * Shortcut for frame.origin.x 14 | * 15 | * Sets frame.origin.x = left 16 | */ 17 | @property (nonatomic, assign) CGFloat vv_left; 18 | 19 | /** 20 | * Shortcut for frame.origin.y 21 | * 22 | * Sets frame.origin.y = top 23 | */ 24 | @property (nonatomic, assign) CGFloat vv_top; 25 | 26 | /** 27 | * Shortcut for frame.origin.x + frame.size.width 28 | * 29 | * Sets frame.origin.x = right - frame.size.width 30 | */ 31 | @property (nonatomic, assign) CGFloat vv_right; 32 | 33 | /** 34 | * Shortcut for frame.origin.y + frame.size.height 35 | * 36 | * Sets frame.origin.y = bottom - frame.size.height 37 | */ 38 | @property (nonatomic, assign) CGFloat vv_bottom; 39 | 40 | /** 41 | * Shortcut for center.x 42 | * 43 | * Sets center.x = centerX 44 | */ 45 | @property (nonatomic, assign) CGFloat vv_centerX; 46 | 47 | /** 48 | * Shortcut for center.y 49 | * 50 | * Sets center.y = centerY 51 | */ 52 | @property (nonatomic, assign) CGFloat vv_centerY; 53 | 54 | /** 55 | * Shortcut for frame.size.width 56 | * 57 | * Sets frame.size.width = width 58 | */ 59 | @property (nonatomic, assign) CGFloat vv_width; 60 | 61 | /** 62 | * Shortcut for frame.size.height 63 | * 64 | * Sets frame.size.height = height 65 | */ 66 | @property (nonatomic, assign) CGFloat vv_height; 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /VirtualView/Utils/UIView+VirtualView.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+VirtualView.m 3 | // VirtualView 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "UIView+VirtualView.h" 9 | 10 | @implementation UIView (VirtualView) 11 | 12 | - (CGFloat)vv_left { 13 | return self.frame.origin.x; 14 | } 15 | 16 | - (void)setVv_left:(CGFloat)x { 17 | CGRect frame = self.frame; 18 | frame.origin.x = x; 19 | self.frame = frame; 20 | } 21 | 22 | - (CGFloat)vv_top { 23 | return self.frame.origin.y; 24 | } 25 | 26 | - (void)setVv_top:(CGFloat)y { 27 | CGRect frame = self.frame; 28 | frame.origin.y = y; 29 | self.frame = frame; 30 | } 31 | 32 | - (CGFloat)vv_right { 33 | return self.frame.origin.x + self.frame.size.width; 34 | } 35 | 36 | - (void)setVv_right:(CGFloat)right { 37 | CGRect frame = self.frame; 38 | frame.origin.x = right - frame.size.width; 39 | self.frame = frame; 40 | } 41 | 42 | - (CGFloat)vv_bottom { 43 | return self.frame.origin.y + self.frame.size.height; 44 | } 45 | 46 | - (void)setVv_bottom:(CGFloat)bottom { 47 | CGRect frame = self.frame; 48 | frame.origin.y = bottom - frame.size.height; 49 | self.frame = frame; 50 | } 51 | 52 | - (CGFloat)vv_centerX { 53 | return self.center.x; 54 | } 55 | 56 | - (void)setVv_centerX:(CGFloat)centerX { 57 | self.center = CGPointMake(centerX, self.center.y); 58 | } 59 | 60 | - (CGFloat)vv_centerY { 61 | return self.center.y; 62 | } 63 | 64 | - (void)setVv_centerY:(CGFloat)centerY { 65 | self.center = CGPointMake(self.center.x, centerY); 66 | } 67 | 68 | - (CGFloat)vv_width { 69 | return self.frame.size.width; 70 | } 71 | 72 | - (void)setVv_width:(CGFloat)width { 73 | CGRect frame = self.frame; 74 | frame.size.width = width; 75 | self.frame = frame; 76 | } 77 | 78 | - (CGFloat)vv_height { 79 | return self.frame.size.height; 80 | } 81 | 82 | - (void)setVv_height:(CGFloat)height { 83 | CGRect frame = self.frame; 84 | frame.size.height = height; 85 | self.frame = frame; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /VirtualViewDemo/README: -------------------------------------------------------------------------------- 1 | Please run pod install in root path and open the workspace generated by CocoaPods. -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "MainViewController.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 16 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[MainViewController new]]; 17 | self.window.backgroundColor = [UIColor whiteColor]; 18 | [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/ContainerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContainerViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TestViewController.h" 9 | 10 | @interface ContainerViewController : TestViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/ContainerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContainerViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "ContainerViewController.h" 9 | #import 10 | 11 | @interface ContainerViewController () 12 | 13 | @property (nonatomic, strong) VVViewContainer *container; 14 | 15 | @end 16 | 17 | @implementation ContainerViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | self.container.delegate = self; 23 | } 24 | 25 | - (void)virtualViewClickedWithAction:(NSString *)action andValue:(NSString *)value 26 | { 27 | if (action) { 28 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"tap" message:action preferredStyle:UIAlertControllerStyleAlert]; 29 | [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; 30 | [self.navigationController presentViewController:alert animated:YES completion:nil]; 31 | } 32 | } 33 | 34 | - (void)virtualViewLongPressedWithAction:(NSString *)action andValue:(NSString *)value 35 | { 36 | if (action) { 37 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"long press" message:action preferredStyle:UIAlertControllerStyleAlert]; 38 | [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]]; 39 | [self.navigationController presentViewController:alert animated:YES completion:nil]; 40 | } 41 | } 42 | 43 | - (NSDictionary *)params 44 | { 45 | return @{ 46 | @"type" : @"Container", 47 | @"content" : @[ 48 | @{ 49 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i1/1910146537/TB2Xluvad3nyKJjSZFEXXXTTFXa_!!1910146537.jpg", 50 | @"title" : @"title1" 51 | }, 52 | @{ 53 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i4/2215696389/TB2uXtXXGZPyuJjy1zcXXXp1FXa_!!2215696389.jpg", 54 | @"title" : @"title2" 55 | }, 56 | @{ 57 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i3/1709193846/TB2W5neXHAlyKJjSZFwXXXtqpXa_!!1709193846.jpg", 58 | @"title" : @"title3" 59 | } 60 | ] 61 | }; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Dot9ImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Dot9ImageView.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface Dot9ImageView : VVBaseNode 11 | 12 | @property (nonatomic, strong) NSString *src; 13 | @property (nonatomic, assign) float dot9Left; 14 | @property (nonatomic, assign) float dot9Top; 15 | @property (nonatomic, assign) float dot9Right; 16 | @property (nonatomic, assign) float dot9Bottom; 17 | @property (nonatomic, assign) int dot9Scale; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/GridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "ContainerViewController.h" 9 | 10 | @interface GridViewController : ContainerViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/GridViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "GridViewController.h" 9 | #import 10 | 11 | @implementation GridViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | if (![[VVTemplateManager sharedManager].loadedTypes containsObject:@"GridItem"]) { 17 | NSString *path = [[NSBundle mainBundle] pathForResource:@"GridItem" ofType:@"out"]; 18 | [[VVTemplateManager sharedManager] loadTemplateFile:path forType:nil]; 19 | } 20 | } 21 | 22 | - (NSDictionary *)params 23 | { 24 | return @{ 25 | @"type" : @"Grid", 26 | @"data" : @[ 27 | @{ 28 | @"type" : @"GridItem", 29 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i1/1910146537/TB2Xluvad3nyKJjSZFEXXXTTFXa_!!1910146537.jpg", 30 | @"title" : @"title1", 31 | @"action" : @"action1" 32 | }, 33 | @{ 34 | @"type" : @"GridItem", 35 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i4/2215696389/TB2uXtXXGZPyuJjy1zcXXXp1FXa_!!2215696389.jpg", 36 | @"title" : @"title2", 37 | @"action" : @"action2" 38 | }, 39 | @{ 40 | @"type" : @"GridItem", 41 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i3/1709193846/TB2W5neXHAlyKJjSZFwXXXtqpXa_!!1709193846.jpg", 42 | @"title" : @"title3", 43 | @"action" : @"action3" 44 | } 45 | ] 46 | }; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/MainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface MainViewController : UITableViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/MainViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "MainViewController.h" 9 | #import "TestViewController.h" 10 | #import "ContainerViewController.h" 11 | #import "GridViewController.h" 12 | #import "PageViewController.h" 13 | #import "TableViewController.h" 14 | #import 15 | #import 16 | 17 | @interface MainViewController () 18 | 19 | @property (nonatomic, strong) NSArray *demoArray; 20 | 21 | @end 22 | 23 | @implementation MainViewController 24 | 25 | - (instancetype)init 26 | { 27 | if (self = [super init]) { 28 | self.title = @"VirtualViewDemo"; 29 | self.demoArray = @[@"FrameLayout", @"VHLayout", @"VH2Layout", @"GridLayout", @"RatioLayout", @"NText", @"NLine", @"NImage", @"NFrameLayout", @"NVHLayout", @"NVH2Layout", @"NGridLayout", @"NRatioLayout", @"Container", @"Grid", @"Page", @"TableViewCell", @"Dot9Image"]; 30 | 31 | [self registerVV]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)registerVV 37 | { 38 | [VVNodeClassMapper registerClassName:@"Dot9ImageView" forID:1001]; 39 | [VVBinaryStringMapper registerString:@"dot9Left"]; 40 | [VVBinaryStringMapper registerString:@"dot9Top"]; 41 | [VVBinaryStringMapper registerString:@"dot9Right"]; 42 | [VVBinaryStringMapper registerString:@"dot9Bottom"]; 43 | [VVBinaryStringMapper registerString:@"dot9Scale"]; 44 | } 45 | 46 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 47 | { 48 | return self.demoArray.count; 49 | } 50 | 51 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 52 | { 53 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 54 | if (!cell) { 55 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 56 | } 57 | cell.textLabel.text = self.demoArray[indexPath.row]; 58 | return cell; 59 | } 60 | 61 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 62 | { 63 | NSString *demoName = self.demoArray[indexPath.row]; 64 | UIViewController *vc; 65 | if ([demoName isEqualToString:@"Container"]) { 66 | vc = [[ContainerViewController alloc] initWithFilename:demoName]; 67 | } else if ([demoName isEqualToString:@"Grid"]) { 68 | vc = [[GridViewController alloc] initWithFilename:demoName]; 69 | } else if ([demoName isEqualToString:@"Page"]) { 70 | vc = [[PageViewController alloc] initWithFilename:demoName]; 71 | } else if ([demoName isEqualToString:@"TableViewCell"]) { 72 | vc = [[TableViewController alloc] init]; 73 | } else { 74 | vc = [[TestViewController alloc] initWithFilename:demoName]; 75 | } 76 | [self.navigationController pushViewController:vc animated:YES]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/PageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "ContainerViewController.h" 9 | 10 | @interface PageViewController : ContainerViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/PageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "PageViewController.h" 9 | #import 10 | 11 | @implementation PageViewController 12 | 13 | - (void)viewDidLoad 14 | { 15 | [super viewDidLoad]; 16 | if (![[VVTemplateManager sharedManager].loadedTypes containsObject:@"PageItem"]) { 17 | NSString *path = [[NSBundle mainBundle] pathForResource:@"PageItem" ofType:@"out"]; 18 | [[VVTemplateManager sharedManager] loadTemplateFile:path forType:nil]; 19 | } 20 | } 21 | 22 | - (NSDictionary *)params 23 | { 24 | return @{ 25 | @"type" : @"Page", 26 | @"data" : @[ 27 | @{ 28 | @"type" : @"PageItem", 29 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i1/1910146537/TB2Xluvad3nyKJjSZFEXXXTTFXa_!!1910146537.jpg", 30 | @"title" : @"title1", 31 | @"action" : @"action1" 32 | }, 33 | @{ 34 | @"type" : @"PageItem", 35 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i4/2215696389/TB2uXtXXGZPyuJjy1zcXXXp1FXa_!!2215696389.jpg", 36 | @"title" : @"title2", 37 | @"action" : @"action2" 38 | }, 39 | @{ 40 | @"type" : @"PageItem", 41 | @"imgUrl" : @"https://img.alicdn.com/imgextra/i3/1709193846/TB2W5neXHAlyKJjSZFwXXXtqpXa_!!1709193846.jpg", 42 | @"title" : @"title3", 43 | @"action" : @"action3" 44 | } 45 | ] 46 | }; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Container.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Container.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Dot9Image.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Dot9Image.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/FrameLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/FrameLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Grid.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Grid.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/GridItem.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/GridItem.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/GridLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/GridLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "right_arrow@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "right_arrow@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_arrow.imageset/right_arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_arrow.imageset/right_arrow@2x.png -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_arrow.imageset/right_arrow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_arrow.imageset/right_arrow@3x.png -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_bubble.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "right_bubble@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "right_bubble@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_bubble.imageset/right_bubble@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_bubble.imageset/right_bubble@2x.png -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_bubble.imageset/right_bubble@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Media.xcassets/right_bubble.imageset/right_bubble@3x.png -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NFrameLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NFrameLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NGridLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NGridLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NImage.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NImage.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NLine.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NLine.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NRatioLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NRatioLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NText.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NText.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NVH2Layout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NVH2Layout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/NVHLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/NVHLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/Page.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/Page.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/PageItem.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/PageItem.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/RatioLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/RatioLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/TextCell.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/TextCell.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/VH2Layout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/VH2Layout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/VHLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/VHLayout.out -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/Resources/local_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewDemo/VirtualViewDemo/Resources/local_image.png -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface TableViewController : UITableViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TableViewController.h" 9 | #import 10 | #import 11 | #import 12 | 13 | @interface TableViewController () 14 | 15 | @property (nonatomic, strong) NSMutableArray *dataArray; 16 | @property (nonatomic, strong) NSMutableArray *heightArray; 17 | @property (nonatomic, strong) VVViewContainer *publicContainer; 18 | 19 | @end 20 | 21 | @implementation TableViewController 22 | 23 | - (instancetype)init 24 | { 25 | if (self = [super init]) { 26 | self.title = @"TableViewCell"; 27 | self.edgesForExtendedLayout = UIRectEdgeNone; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | self.view.backgroundColor = [UIColor whiteColor]; 35 | 36 | if (![[VVTemplateManager sharedManager].loadedTypes containsObject:@"TextCell"]) { 37 | NSString *path = [[NSBundle mainBundle] pathForResource:@"TextCell" ofType:@"out"]; 38 | [[VVTemplateManager sharedManager] loadTemplateFile:path forType:nil]; 39 | } 40 | 41 | _dataArray = [NSMutableArray array]; 42 | _heightArray = [NSMutableArray array]; 43 | for (NSInteger i = 0; i < 10; i++) { 44 | [_dataArray addObject:@{@"imgUrl" : @"right_arrow", 45 | @"title" : @"line 1, line 1, line 1, line 1, line 1, line 1"}]; 46 | [_heightArray addObject:@(0)]; 47 | [_dataArray addObject:@{@"size" : @"16", 48 | @"title" : @"line 2, line 2, line 2, line 2, line 2, line 2, line 2, line 2, line 2, line 2"}]; 49 | [_heightArray addObject:@(0)]; 50 | [_dataArray addObject:@{@"imgUrl" : @"https://www.tmall.com/favicon.ico", 51 | @"title" : @"line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3, line 3"}]; 52 | [_heightArray addObject:@(0)]; 53 | [_dataArray addObject:@{@"title" : @"line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4, line 4"}]; 54 | [_heightArray addObject:@(0)]; 55 | } 56 | 57 | _publicContainer = [VVViewContainer viewContainerWithTemplateType:@"TextCell"]; 58 | } 59 | 60 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 61 | { 62 | return _dataArray.count; 63 | } 64 | 65 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 66 | { 67 | CGFloat height = [[_heightArray objectAtIndex:indexPath.row] floatValue]; 68 | if (height <= 0) { 69 | CGFloat viewWidth = CGRectGetWidth(self.view.bounds); 70 | CGSize size = CGSizeMake(viewWidth, 1000); 71 | [_publicContainer updateData:[_dataArray objectAtIndex:indexPath.row]]; 72 | size = [_publicContainer estimatedSize:size]; 73 | height = ceil(size.height); 74 | [_heightArray replaceObjectAtIndex:indexPath.row withObject:@(height)]; 75 | } 76 | return height; 77 | } 78 | 79 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 80 | { 81 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 82 | if (!cell) { 83 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; 84 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 85 | VVViewContainer *container = [VVViewContainer viewContainerWithTemplateType:@"TextCell" alwaysRefresh:NO]; 86 | container.tag = 1000; 87 | [cell addSubview:container]; 88 | } 89 | VVViewContainer *container = [cell viewWithTag:1000]; 90 | if (container) { 91 | CGFloat viewWidth = CGRectGetWidth(self.view.bounds); 92 | CGFloat height = [self tableView:tableView heightForRowAtIndexPath:indexPath]; 93 | container.frame = CGRectMake(0, 0, viewWidth, height); 94 | [container update:[_dataArray objectAtIndex:indexPath.row]]; 95 | } 96 | return cell; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/TestViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestViewController.h 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface TestViewController : UIViewController 11 | 12 | @property (nonatomic, strong, readonly) NSDictionary *params; 13 | 14 | - (instancetype)initWithFilename:(NSString *)filename; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/TestViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestViewController.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TestViewController.h" 9 | #import 10 | #import 11 | #import 12 | 13 | @interface TestViewController () 14 | 15 | @property (nonatomic, strong) UIScrollView *scrollView; 16 | @property (nonatomic, strong) VVViewContainer *container; 17 | 18 | @end 19 | 20 | @implementation TestViewController 21 | 22 | - (instancetype)initWithFilename:(NSString *)filename 23 | { 24 | if (self = [super init]) { 25 | self.title = filename; 26 | self.edgesForExtendedLayout = UIRectEdgeNone; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)viewDidLoad { 32 | [super viewDidLoad]; 33 | self.view.backgroundColor = [UIColor whiteColor]; 34 | 35 | self.scrollView = [UIScrollView new]; 36 | [self.view addSubview:self.scrollView]; 37 | 38 | if (![[VVTemplateManager sharedManager].loadedTypes containsObject:self.title]) { 39 | NSString *path = [[NSBundle mainBundle] pathForResource:self.title ofType:@"out"]; 40 | [[VVTemplateManager sharedManager] loadTemplateFile:path forType:nil]; 41 | } 42 | self.container = [VVViewContainer viewContainerWithTemplateType:self.title]; 43 | [self.scrollView addSubview:self.container]; 44 | } 45 | 46 | - (void)viewWillAppear:(BOOL)animated 47 | { 48 | [super viewWillAppear:animated]; 49 | 50 | self.scrollView.frame = self.view.bounds; 51 | CGFloat viewWidth = CGRectGetWidth(self.view.bounds); 52 | CGSize size = CGSizeMake(viewWidth, 1000); 53 | size = [self.container estimatedSize:size]; 54 | self.scrollView.contentSize = size; 55 | self.container.frame = CGRectMake(0, 0, size.width, size.height); 56 | [self.container update:self.params]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /VirtualViewDemo/VirtualViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // VirtualViewDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /VirtualViewTest/README: -------------------------------------------------------------------------------- 1 | Please run pod install in root path and open the workspace generated by CocoaPods. -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest.xcodeproj/xcshareddata/xcbaselines/926B403F2004950B0003D88E.xcbaseline/4547F22E-E920-471F-BD93-A11D61EF210A.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | BinaryReaderTest 8 | 9 | testReadFloatPerformance1 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.03 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | testReadFloatPerformance2 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 0.03 25 | baselineIntegrationDisplayName 26 | Local Baseline 27 | 28 | 29 | testReadFloatPerformance3 30 | 31 | com.apple.XCTPerformanceMetric_WallClockTime 32 | 33 | baselineAverage 34 | 0.03 35 | baselineIntegrationDisplayName 36 | Local Baseline 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest.xcodeproj/xcshareddata/xcbaselines/926B403F2004950B0003D88E.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 4547F22E-E920-471F-BD93-A11D61EF210A 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 2200 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookPro11,4 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone10,5 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/BinaryLoaderTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // BinaryLoaderTest.m 3 | // VirtualViewTest 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | @interface BinaryLoaderTest : XCTestCase 20 | 21 | @end 22 | 23 | @implementation BinaryLoaderTest 24 | 25 | - (void)setUp { 26 | [super setUp]; 27 | } 28 | 29 | - (void)tearDown { 30 | [super tearDown]; 31 | } 32 | 33 | - (void)testLoadTemplate { 34 | NSString *path = [[NSBundle bundleForClass:self.class] pathForResource:@"GridItem" ofType:@"out"]; 35 | NSData *data = [NSData dataWithContentsOfFile:path]; 36 | VVTemplateBinaryLoader *loader = [VVTemplateBinaryLoader new]; 37 | assertThatBool([loader loadTemplateData:data], isTrue()); 38 | assertThat([loader.lastVersion stringValue], equalTo(@"1.0.1")); 39 | assertThat(loader.lastType, equalTo(@"GridItem")); 40 | assertThat(loader.lastCreater.nodeClassName, equalTo(@"VVVHLayout")); 41 | for (VVPropertySetter *setter in loader.lastCreater.propertySetters) { 42 | if ([setter.name isEqualToString:@"orientation"]) { 43 | assertThatInt([(VVPropertyIntSetter *)setter value], equalToInt(VVOrientationVertical)); 44 | } else if ([setter.name isEqualToString:@"flag"]) { 45 | assertThatInt([(VVPropertyIntSetter *)setter value], equalToInt(VVFlagClickable | VVFlagLongClickable)); 46 | } else if ([setter.name isEqualToString:@"layoutHeight"]) { 47 | assertThatInt([(VVPropertyIntSetter *)setter value], equalToInt(180)); 48 | } else if ([setter.name isEqualToString:@"layoutWidth"]) { 49 | assertThatInt([(VVPropertyIntSetter *)setter value], equalToInt(VV_MATCH_PARENT)); 50 | } else if ([setter.name isEqualToString:@"gravity"]) { 51 | assertThatInt([(VVPropertyIntSetter *)setter value], equalToInt(VVGravityHCenter | VVGravityVCenter)); 52 | } 53 | } 54 | assertThat([(VVNodeCreater *)loader.lastCreater.subCreaters[0] nodeClassName], equalTo(@"NVImageView")); 55 | assertThat([(VVNodeCreater *)loader.lastCreater.subCreaters[1] nodeClassName], equalTo(@"NVTextView")); 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/BinaryReaderTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // BinaryReaderTest.m 3 | // VirtualViewTest 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | @interface BinaryReaderTest : XCTestCase 13 | 14 | @end 15 | 16 | @implementation BinaryReaderTest 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | } 21 | 22 | - (void)tearDown { 23 | [super tearDown]; 24 | } 25 | 26 | - (void)testReadFloat1 { 27 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 28 | uint32_t uintValue = *((uint32_t *)bytes); 29 | uintValue = CFSwapInt32(uintValue); 30 | float floatValue = *((float *)(&uintValue)); 31 | assertThatFloat(floatValue, equalToFloat(0.001f)); 32 | } 33 | 34 | - (void)testReadFloat2 { 35 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 36 | unsigned char byte = bytes[3]; bytes[3] = bytes[0]; bytes[0] = byte; // swap bytes[0] and bytes[3] 37 | byte = bytes[2]; bytes[2] = bytes[1]; bytes[1] = byte; // swap bytes[1] and bytes[2] 38 | float floatValue = *((float *)bytes); 39 | assertThatFloat(floatValue, equalToFloat(0.001f)); 40 | } 41 | 42 | - (void)testReadFloat3 { 43 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 44 | uint32_t uintValue = bytes[3] + (bytes[2] << 8) + (bytes[1] << 16) + (bytes[0] << 24); 45 | float floatValue = *((float *)(&uintValue)); 46 | assertThatFloat(floatValue, equalToFloat(0.001f)); 47 | } 48 | 49 | - (void)testReadShort { 50 | unsigned char bytes[] = {0x01, 0x02}; 51 | uint16_t uintValue = bytes[1] + (bytes[0] << 8); 52 | short shortValue = *((short *)(&uintValue)); 53 | assertThatShort(shortValue, equalToShort(0x102)); 54 | } 55 | 56 | - (void)testReadFloatPerformance1 { 57 | [self measureBlock:^{ 58 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 59 | NSData *data = [NSData dataWithBytes:bytes length:4]; 60 | float floatValue; 61 | for (NSInteger i = 0; i < 999999; i++) { 62 | uint32_t uintValue; 63 | [data getBytes:&uintValue range:NSMakeRange(0, 4)]; 64 | uintValue = CFSwapInt32(uintValue); 65 | floatValue = *((float *)(&uintValue)); 66 | } 67 | }]; 68 | } 69 | 70 | - (void)testReadFloatPerformance2 { 71 | [self measureBlock:^{ 72 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 73 | NSData *data = [NSData dataWithBytes:bytes length:4]; 74 | float floatValue; 75 | for (NSInteger i = 0; i < 999999; i++) { 76 | unsigned char buff[4]; 77 | [data getBytes:buff range:NSMakeRange(0, 4)]; 78 | unsigned char byte = buff[3]; buff[3] = buff[0]; buff[0] = byte; // swap buff[0] and buff[3] 79 | byte = buff[2]; buff[2] = buff[1]; buff[1] = byte; // swap buff[1] and buff[2] 80 | floatValue = *((float *)buff); 81 | } 82 | }]; 83 | } 84 | 85 | - (void)testReadFloatPerformance3 { 86 | [self measureBlock:^{ 87 | unsigned char bytes[] = {0x3A, 0x83, 0x12, 0x6F}; 88 | NSData *data = [NSData dataWithBytes:bytes length:4]; 89 | float floatValue; 90 | for (NSInteger i = 0; i < 999999; i++) { 91 | unsigned char buff[4]; 92 | [data getBytes:buff range:NSMakeRange(0, 4)]; 93 | uint32_t uintValue = buff[3] + (buff[2] << 8) + (buff[1] << 16) + (buff[0] << 24); 94 | floatValue = *((float *)(&uintValue)); 95 | } 96 | }]; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/ColorUtilsTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // ColorUtilsTest.m 3 | // VirtualViewTest 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | @interface ColorUtilsTest : XCTestCase 13 | 14 | @end 15 | 16 | @implementation ColorUtilsTest 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | } 21 | 22 | - (void)tearDown { 23 | [super tearDown]; 24 | } 25 | 26 | - (void)testRGBColor { 27 | assertThat([UIColor vv_colorWithRGB:0xFF0000], equalTo([UIColor redColor])); 28 | assertThat([UIColor vv_colorWithRGB:0xFF00], equalTo([UIColor greenColor])); 29 | assertThat([UIColor vv_colorWithRGB:0xFF], equalTo([UIColor blueColor])); 30 | assertThat([UIColor vv_colorWithRGB:0xFFFF0000], equalTo([UIColor redColor])); 31 | assertThat([UIColor vv_colorWithRGB:0x00FF0000], equalTo([UIColor redColor])); 32 | } 33 | 34 | - (void)testARGBColor { 35 | assertThat([UIColor vv_colorWithARGB:0xFFFF0000], equalTo([UIColor redColor])); 36 | assertThat([UIColor vv_colorWithARGB:0xFF00FF00], equalTo([UIColor greenColor])); 37 | assertThat([UIColor vv_colorWithARGB:0xFF0000FF], equalTo([UIColor blueColor])); 38 | assertThat([UIColor vv_colorWithARGB:0xFF0000], equalTo([UIColor clearColor])); 39 | assertThat([UIColor vv_colorWithARGB:0xFF00], equalTo([UIColor clearColor])); 40 | } 41 | 42 | - (void)testStringColor { 43 | assertThat([UIColor vv_colorWithString:@"0xFF0000"], equalTo([UIColor redColor])); 44 | assertThat([UIColor vv_colorWithString:@"0x00FF00"], equalTo([UIColor greenColor])); 45 | assertThat([UIColor vv_colorWithString:@"0xFF0000FF"], equalTo([UIColor blueColor])); 46 | assertThat([UIColor vv_colorWithString:@"0x00FF0000"], equalTo([UIColor clearColor])); 47 | assertThat([UIColor vv_colorWithString:@"#FF0000"], equalTo([UIColor redColor])); 48 | assertThat([UIColor vv_colorWithString:@"#00FF00"], equalTo([UIColor greenColor])); 49 | assertThat([UIColor vv_colorWithString:@"#FF0000FF"], equalTo([UIColor blueColor])); 50 | assertThat([UIColor vv_colorWithString:@"#0000FF00"], equalTo([UIColor clearColor])); 51 | assertThat([UIColor vv_colorWithString:@"FF0000"], equalTo([UIColor redColor])); 52 | assertThat([UIColor vv_colorWithString:@"00FF00"], equalTo([UIColor greenColor])); 53 | assertThat([UIColor vv_colorWithString:@"FF0000FF"], equalTo([UIColor blueColor])); 54 | assertThat([UIColor vv_colorWithString:@"000000FF"], equalTo([UIColor clearColor])); 55 | assertThat([UIColor vv_colorWithString:@"0xF00"], nilValue()); 56 | assertThat([UIColor vv_colorWithString:@"#0F0"], nilValue()); 57 | assertThat([UIColor vv_colorWithString:@"00F"], nilValue()); 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/ObserverTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // ObserverTest.m 3 | // VirtualViewTest 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | @interface ObserverTest : XCTestCase 13 | 14 | @property (nonatomic, assign) CGRect rectValue; 15 | @property (nonatomic, assign) NSInteger intValue; 16 | @property (nonatomic, strong) NSString *stringValue; 17 | 18 | @end 19 | 20 | @implementation ObserverTest 21 | 22 | - (void)setUp { 23 | [super setUp]; 24 | } 25 | 26 | - (void)tearDown { 27 | [super tearDown]; 28 | } 29 | 30 | - (void)testIntValue { 31 | __block NSInteger result; 32 | __block NSInteger times; 33 | [self vv_addObserverForKeyPath:@"intValue" block:^(id _Nonnull value) { 34 | result = [value integerValue]; 35 | times++; 36 | }]; 37 | 38 | self.intValue = 10; 39 | assertThatInteger(result, equalToInteger(10)); 40 | assertThatInteger(times, equalToInteger(1)); 41 | 42 | self.intValue = 10; 43 | assertThatInteger(result, equalToInteger(10)); 44 | assertThatInteger(times, equalToInteger(1)); 45 | 46 | self.intValue = 20; 47 | assertThatInteger(result, equalToInteger(20)); 48 | assertThatInteger(times, equalToInteger(2)); 49 | 50 | [self vv_removeObserverForKeyPath:@"intValue"]; 51 | 52 | self.intValue = 30; 53 | assertThatInteger(result, equalToInteger(20)); 54 | assertThatInteger(times, equalToInteger(2)); 55 | } 56 | 57 | - (void)testStringValue { 58 | [self vv_addObserverForKeyPath:@"stringValue" selector:@selector(updateIntValue:)]; 59 | 60 | self.stringValue = @"10"; 61 | assertThatInteger(self.intValue, equalToInteger(10)); 62 | 63 | self.stringValue = @"20"; 64 | assertThatInteger(self.intValue, equalToInteger(20)); 65 | 66 | [self vv_removeAllObservers]; 67 | 68 | self.stringValue = @"30"; 69 | assertThatInteger(self.intValue, equalToInteger(20)); 70 | } 71 | 72 | - (void)updateIntValue:(id)stringValue { 73 | self.intValue = [stringValue integerValue]; 74 | } 75 | 76 | - (void) testSizeValue { 77 | __block CGSize result; 78 | __block NSInteger times; 79 | [self vv_addObserverForKeyPath:@"sizeValue" block:^(id _Nonnull value) { 80 | result = [value sizeValue]; 81 | times++; 82 | }]; 83 | 84 | self.rectValue = CGRectMake(0, 0, 10, 10); 85 | assertThatFloat(result.width, equalToFloat(10)); 86 | assertThatFloat(result.height, equalToFloat(10)); 87 | assertThatInteger(times, equalToInteger(1)); 88 | 89 | self.rectValue = CGRectMake(0, 0, 20, 20); 90 | assertThatFloat(result.width, equalToFloat(20)); 91 | assertThatFloat(result.height, equalToFloat(20)); 92 | assertThatInteger(times, equalToInteger(2)); 93 | 94 | self.rectValue = CGRectMake(5, 5, 20, 20); 95 | assertThatFloat(result.width, equalToFloat(20)); 96 | assertThatFloat(result.height, equalToFloat(20)); 97 | assertThatInteger(times, equalToInteger(2)); 98 | } 99 | 100 | - (CGSize)sizeValue { 101 | return _rectValue.size; 102 | } 103 | 104 | - (void)setRectValue:(CGRect)rectValue { 105 | [self willChangeValueForKey:@"sizeValue"]; 106 | _rectValue = rectValue; 107 | [self didChangeValueForKey:@"sizeValue"]; 108 | } 109 | 110 | - (void)testObserveTwice { 111 | __block NSString *result1; 112 | __block NSInteger result2; 113 | [self vv_addObserverForKeyPath:@"stringValue" block:^(id _Nonnull value) { 114 | result1 = [@"=" stringByAppendingString:value]; 115 | }]; 116 | [self vv_addObserverForKeyPath:@"stringValue" block:^(id _Nonnull value) { 117 | result2 = [value integerValue]; 118 | }]; 119 | 120 | self.stringValue = @"10"; 121 | assertThat(result1, equalTo(@"=10")); 122 | assertThatInteger(result2, equalToInteger(10)); 123 | } 124 | 125 | - (void)testSelectorWithoutParam { 126 | [self vv_addObserverForKeyPath:@"stringValue" selector:@selector(updateIntValue)]; 127 | 128 | self.stringValue = @"10"; 129 | assertThatInteger(self.intValue, equalToInteger(-1)); 130 | } 131 | 132 | - (void)updateIntValue { 133 | self.intValue = -1; 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/FrameLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/FrameLayout.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/GridItem.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/GridItem.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/NImage.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/NImage.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/NLine.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/NLine.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/NText.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/NText.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/RatioLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/RatioLayout.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/Resources/VHLayout.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/VirtualView-iOS/63a60dda516c585c947fc90bb1561c37deed8058/VirtualViewTest/VirtualViewTest/Resources/VHLayout.out -------------------------------------------------------------------------------- /VirtualViewTest/VirtualViewTest/StringMapperTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // StringMapperTest.m 3 | // VirtualViewTest 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | 13 | @interface StringMapperTest : XCTestCase 14 | 15 | @end 16 | 17 | @implementation StringMapperTest 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | } 22 | 23 | - (void)tearDown { 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testHash { 28 | assertThatInt([VVBinaryStringMapper hashOfString:@"layoutWidth"], equalToInt(STR_ID_layoutWidth)); 29 | assertThatInt([VVBinaryStringMapper hashOfString:@"paddingLeft"], equalToInt(STR_ID_paddingLeft)); 30 | assertThatInt([VVBinaryStringMapper hashOfString:@"orientation"], equalToInt(STR_ID_orientation)); 31 | assertThatInt([VVBinaryStringMapper hashOfString:@"gravity"], equalToInt(STR_ID_gravity)); 32 | assertThatInt([VVBinaryStringMapper hashOfString:@"layoutGravity"], equalToInt(STR_ID_layoutGravity)); 33 | assertThatInt([VVBinaryStringMapper hashOfString:@"action"], equalToInt(STR_ID_action)); 34 | assertThatInt([VVBinaryStringMapper hashOfString:@"textColor"], equalToInt(STR_ID_textColor)); 35 | assertThatInt([VVBinaryStringMapper hashOfString:@"layoutRatio"], equalToInt(STR_ID_layoutRatio)); 36 | assertThatInt([VVBinaryStringMapper hashOfString:@"paintWidth"], equalToInt(STR_ID_paintWidth)); 37 | assertThatInt([VVBinaryStringMapper hashOfString:@"autoDimDirection"], equalToInt(STR_ID_autoDimDirection)); 38 | assertThatInt([VVBinaryStringMapper hashOfString:@"borderWidth"], equalToInt(STR_ID_borderWidth)); 39 | assertThatInt([VVBinaryStringMapper hashOfString:@"borderBottomRightRadius"], equalToInt(STR_ID_borderBottomRightRadius)); 40 | assertThatInt([VVBinaryStringMapper hashOfString:@"ratio"], equalToInt(STR_ID_ratio)); 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /update_header.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re 3 | import sys,os 4 | 5 | def updateHeader(DIR, PROJ): 6 | for path in os.listdir(DIR): 7 | fullPath = os.path.join(DIR, path) 8 | if os.path.isdir(fullPath): 9 | if path != "Pods": 10 | updateHeader(fullPath, PROJ) 11 | elif os.path.isfile(fullPath): 12 | if path.lower().endswith('.m') or path.lower().endswith('.h'): 13 | print('Updating: %s' % (path)) 14 | codeFile = open(fullPath, 'r+') 15 | content = codeFile.read() 16 | content = re.sub('^(//[^\n]*\n)+//(?P[^\n]*)\n', 17 | '//\n' + 18 | '// ' + path + '\n' + 19 | '// ' + PROJ + '\n' + 20 | '//\n' + 21 | '// Copyright (c) 2017-2018 Alibaba. All rights reserved.\n' + 22 | '//' + '\g' + '\n', 23 | content) 24 | codeFile.seek(0) 25 | codeFile.write(content) 26 | codeFile.truncate() 27 | codeFile.close() 28 | 29 | updateHeader(os.path.join(sys.path[0], 'VirtualView'), 'VirtualView') 30 | updateHeader(os.path.join(sys.path[0], 'VirtualViewDemo'), 'VirtualViewDemo') 31 | updateHeader(os.path.join(sys.path[0], 'VirtualViewTest'), 'VirtualViewTest') 32 | print('Header updating is done.') 33 | 34 | --------------------------------------------------------------------------------