├── .gitignore ├── Docs ├── getting-started.md └── layoutIndex.md ├── LICENSE ├── Podfile ├── README.md ├── Tangram.podspec ├── Tangram ├── Core │ ├── TangramView.h │ └── TangramView.m ├── EventBus │ ├── TangramAction.h │ ├── TangramAction.m │ ├── TangramBus.h │ ├── TangramBus.m │ ├── TangramBusIndex.h │ ├── TangramBusIndex.m │ ├── TangramBusIndexClass.h │ ├── TangramBusIndexClass.m │ ├── TangramBusIndexTopic.h │ ├── TangramBusIndexTopic.m │ ├── TangramContext.h │ ├── TangramContext.m │ ├── TangramEvent.h │ ├── TangramEvent.m │ ├── TangramEventDispatcher.h │ ├── TangramEventDispatcher.m │ ├── TangramEventQueue.h │ └── TangramEventQueue.m ├── Factory │ ├── TangramDefaultElementFactory.h │ ├── TangramDefaultElementFactory.m │ ├── TangramDefaultItemModelFactory.h │ ├── TangramDefaultItemModelFactory.m │ ├── TangramDefaultLayoutFactory.h │ └── TangramDefaultLayoutFactory.m ├── Helper │ ├── TangramDefaultDataSourceHelper.h │ ├── TangramDefaultDataSourceHelper.m │ ├── TangramLayoutParseHelper.h │ └── TangramLayoutParseHelper.m ├── Layouts │ ├── TangramDoubleColumnLayout.h │ ├── TangramDoubleColumnLayout.m │ ├── TangramDragableLayout.h │ ├── TangramDragableLayout.m │ ├── TangramFixBottomLayout.h │ ├── TangramFixBottomLayout.m │ ├── TangramFixLayout.h │ ├── TangramFixLayout.m │ ├── TangramFixTopLayout.h │ ├── TangramFixTopLayout.m │ ├── TangramFlowLayout.h │ ├── TangramFlowLayout.m │ ├── TangramPageScrollLayout.h │ ├── TangramPageScrollLayout.m │ ├── TangramQuintetColumnLayout.h │ ├── TangramQuintetColumnLayout.m │ ├── TangramScrollFlowLayout.h │ ├── TangramScrollFlowLayout.m │ ├── TangramScrollWaterFlowLayout.h │ ├── TangramScrollWaterFlowLayout.m │ ├── TangramSingleAndDoubleLayout.h │ ├── TangramSingleAndDoubleLayout.m │ ├── TangramSingleColumnLayout.h │ ├── TangramSingleColumnLayout.m │ ├── TangramStickyBottomLayout.h │ ├── TangramStickyBottomLayout.m │ ├── TangramStickyLayout.h │ ├── TangramStickyLayout.m │ ├── TangramTetradColumnLayout.h │ ├── TangramTetradColumnLayout.m │ ├── TangramTribleColumnLayout.h │ ├── TangramTribleColumnLayout.m │ ├── TangramWaterFlowLayout.h │ └── TangramWaterFlowLayout.m ├── Model │ ├── TangramDefaultItemModel.h │ └── TangramDefaultItemModel.m ├── Protocols │ ├── TangramDefaultEventDelegate.h │ ├── TangramEasyElementProtocol.h │ ├── TangramElementFactoryProtocol.h │ ├── TangramElementHeightProtocol.h │ ├── TangramElementReuseIdentifierProtocol.h │ ├── TangramItemModelFactoryProtocol.h │ ├── TangramItemModelProtocol.h │ ├── TangramLayoutFactoryProtocol.h │ ├── TangramLayoutProtocol.h │ └── TangramScrollLayoutProtocol.h ├── Resources │ ├── TangramKitVVElementTypeMap.plist │ ├── TangramLayoutTypeMap.plist │ └── TmallComponent2.out ├── UI │ ├── TangramPageControl.h │ ├── TangramPageControl.m │ ├── TangramProgressBar.h │ └── TangramProgressBar.m ├── Util │ ├── NSString+Tangram.h │ ├── NSString+Tangram.m │ ├── UIView+Tangram.h │ └── UIView+Tangram.m └── VirtualView │ ├── TMVVBaseElement.h │ ├── TMVVBaseElement.m │ ├── VVTempleteManager.h │ └── VVTempleteManager.m ├── TangramDemo ├── TangramDemo.xcodeproj │ └── project.pbxproj └── TangramDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── Element │ ├── TangramSimpleTextElement.h │ ├── TangramSimpleTextElement.m │ ├── TangramSingleImageElement.h │ └── TangramSingleImageElement.m │ ├── EntryTableViewController.h │ ├── EntryTableViewController.m │ ├── Info.plist │ ├── MockViewController.h │ ├── MockViewController.m │ ├── Resources │ └── TangramMock.json │ ├── ViewController.h │ ├── ViewController.m │ └── main.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 | -------------------------------------------------------------------------------- /Docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Tangram-iOS Getting-Started Guide 2 | 3 | All Code can be found in `TangramDemo` 4 | 5 | For QuickStart, we Use default helper and parser 6 | 7 | Use Default helper can significantly reduce the workload. 8 | 9 | Default Helper can easily map attribute to element(using KVC). 10 | 11 | ## Build an element 12 | 13 | Generate a element as a subclass of UIView , and implement `TangramElementHeightProtocol` and `TMLazyItemViewProtocol` 14 | , then state propreties you need. 15 | 16 | ### implement TangramElementHeightProtocol 17 | 18 | `TangramHeightProtocol` is used to return a height for Element. 19 | 20 | In Tangram, the width of element is set by layout, height returned from element class method. Unless set height or width in the style of element. 21 | 22 | Element should implement the class method following: 23 | 24 | `+ (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel;` 25 | 26 | In this method, the properties of the param `itemModel` is set.So you can get width by `itemModel.itemFrame.size.width` 27 | 28 | ### implement TMLazyItemViewProtocol 29 | 30 | Tangram is based on LazyScrollView. You can do something in the life cycle of element by LazyScrollView API. 31 | 32 | `- (void)mui_afterGetView;` 33 | 34 | This method will be executed when the element will enter visible area. So we can finish internal layout in this method. 35 | 36 | ### State properties 37 | 38 | Default Helper will map attribute from a dictionary to the element.You don't need to parse param again. The way to do this is using KVC. 39 | 40 | Finally we make a element like this. It's a element contains a singleImage. 41 | 42 | **TangramSingleImageElement.h** 43 | 44 | ```objc 45 | #import 46 | #import "TangramElementHeightProtocol.h" 47 | #import "TMLazyScrollView.h" 48 | 49 | @interface TangramSingleImageElement : UIView 50 | 51 | @property (nonatomic, strong) NSString *imgUrl; 52 | 53 | @property (nonatomic, strong) NSNumber *number; 54 | 55 | @end 56 | ``` 57 | 58 | **TangramSingleImageElement.m** 59 | 60 | ```objc 61 | #import "TangramSingleImageElement.h" 62 | #import 63 | 64 | @interface TangramSingleImageElement() 65 | 66 | @property (nonatomic, strong) UIImageView *imageView; 67 | 68 | @property (nonatomic, strong) UILabel *titleLabel; 69 | 70 | @end 71 | 72 | @implementation TangramSingleImageElement 73 | 74 | - (UIImageView *)imageView 75 | { 76 | if (_imageView == nil) { 77 | _imageView = [[UIImageView alloc] init]; 78 | _imageView.userInteractionEnabled = NO; 79 | _imageView.contentMode = UIViewContentModeScaleToFill; 80 | _imageView.clipsToBounds = YES; 81 | [self addSubview:_imageView]; 82 | self.backgroundColor = [UIColor grayColor]; 83 | } 84 | return _imageView; 85 | } 86 | 87 | - (UILabel *)titleLabel 88 | { 89 | if (nil == _titleLabel) { 90 | _titleLabel = [[UILabel alloc]init]; 91 | _titleLabel.textColor = [UIColor redColor]; 92 | [self addSubview:_titleLabel]; 93 | } 94 | return _titleLabel; 95 | } 96 | -(void)setImgUrl:(NSString *)imgUrl 97 | { 98 | if (imgUrl.length > 0) { 99 | [self.imageView sd_setImageWithURL:[NSURL URLWithString:imgUrl]]; 100 | } 101 | } 102 | 103 | - (void)setFrame:(CGRect)frame 104 | { 105 | [super setFrame:frame]; 106 | if (frame.size.width > 0 && frame.size.height > 0) { 107 | [self mui_afterGetView]; 108 | } 109 | } 110 | 111 | - (void)mui_afterGetView 112 | { 113 | self.imageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 114 | self.titleLabel.text = [NSString stringWithFormat:@"%ld",[self.number longValue]]; 115 | [self.titleLabel sizeToFit]; 116 | } 117 | 118 | 119 | + (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel; 120 | { 121 | return 100.f; 122 | } 123 | @end 124 | ``` 125 | 126 | ## Set TangramView 127 | 128 | ### Regist element to default factory 129 | 130 | If you want to regist type 1 to `TangramSingleImageElement` 131 | 132 | do like this. 133 | 134 | ```objc 135 | [TangramDefaultItemModelFactory registElementType:@"1" className:@"TangramSingleImageElement"]; 136 | ``` 137 | 138 | 139 | ### Parse JSON Data to layout instance 140 | 141 | Use `TangramDefaultDataSourceHelper` to parse JSON to a layout array. 142 | 143 | ```objc 144 | NSString *mockDataPath = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"TangramMock" ofType:@"json"] encoding:NSUTF8StringEncoding error:nil]; 145 | NSDictionary *dict = [mockDataPath objectFromJSONString]; 146 | self.layoutModelArray = [[dict objectForKey:@"data"] objectForKey:@"cards"]; 147 | self.layoutArray = [TangramDefaultDataSourceHelper layoutsWithArray:self.layoutModelArray]; 148 | ``` 149 | 150 | About `TangramMock.json` , you can find it in TangramDemo 151 | 152 | ### implement TangramViewDatasource 153 | 154 | `- (NSUInteger)numberOfLayoutsInTangramView:(TangramView *)view;` 155 | 156 | return layout count. 157 | 158 | eg: 159 | 160 | ```objc 161 | - (NSUInteger)numberOfLayoutsInTangramView:(TangramView *)view 162 | { 163 | return self.layoutModelArray.count; 164 | } 165 | ``` 166 | 167 | 168 | `- (UIView *)layoutInTangramView:(TangramView *)view atIndex:(NSUInteger)index;` 169 | 170 | Return the layout instance depend on index 171 | 172 | eg: 173 | 174 | ```objc 175 | - (UIView *)layoutInTangramView:(TangramView *)view atIndex:(NSUInteger)index 176 | { 177 | return [self.layoutArray objectAtIndex:index]; 178 | } 179 | ``` 180 | 181 | `- (NSUInteger)numberOfItemsInTangramView:(TangramView *)view forLayout:(UIView *)layout` 182 | 183 | Return itemModel count by a layout. 184 | 185 | 186 | eg: 187 | 188 | ```objc 189 | - (NSUInteger)numberOfItemsInTangramView:(TangramView *)view forLayout:(UIView *)layout 190 | { 191 | return layout.itemModels.count; 192 | } 193 | ``` 194 | 195 | 196 | `- (NSObject *)itemModelInTangramView:(TangramView *)view forLayout:(UIView *)layout atIndex:(NSUInteger)index;` 197 | 198 | Return itemModel by a itemModel index in layout . 199 | 200 | eg: 201 | 202 | ```objc 203 | - (NSObject *)itemModelInTangramView:(TangramView *)view forLayout:(UIView *)layout atIndex:(NSUInteger)index 204 | { 205 | return [layout.itemModels objectAtIndex:index];; 206 | } 207 | ``` 208 | 209 | `- (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index;` 210 | 211 | Return element(view) in the layout by index. 212 | 213 | Recommend get reuseable view first by `[view dequeueReusableItemWithIdentifier:model.reuseIdentifier]`; 214 | 215 | eg: 216 | 217 | ```objc 218 | - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index 219 | { 220 | UIView *reuseableView = [view dequeueReusableItemWithIdentifier:model.reuseIdentifier]; 221 | 222 | if (reuseableView) { 223 | reuseableView = [TangramDefaultDataSourceHelper refreshElement:reuseableView byModel:model]; 224 | } 225 | else 226 | { 227 | reuseableView = [TangramDefaultDataSourceHelper elementByModel:model]; 228 | } 229 | return reuseableView; 230 | } 231 | ``` 232 | 233 | ### Create a TangramView 234 | 235 | Create a TangramView like this... 236 | 237 | ```objc 238 | _tangramView = [[TangramView alloc]init]; 239 | _tangramView.frame = self.view.bounds; 240 | [_tangramView setDataSource:self]; 241 | _tangramView.backgroundColor = [UIColor whiteColor]; 242 | [self.view addSubview:_tangramView]; 243 | ``` 244 | 245 | ### Finally, reload 246 | 247 | ```objc 248 | [self.tangramView reloadData]; 249 | ``` 250 | -------------------------------------------------------------------------------- /Docs/layoutIndex.md: -------------------------------------------------------------------------------- 1 | # Layout Index 2 | 3 | Type is a layout property in JSON 4 | 5 | ![](https://img.alicdn.com/tfs/TB1o426PVXXXXb_XXXXXXXXXXXX-445-241.png) 6 | 7 | ## FlowLayout 8 | 9 | ![](https://img.alicdn.com/tfs/TB1UX3DPVXXXXbsXXXXXXXXXXXX-548-299.png) 10 | 11 | |type|Description| 12 | |---|----| 13 | |1|One column| 14 | |2|Two columns| 15 | |3|Three columns| 16 | |4|Four columns| 17 | |9|Five columns| 18 | |27|any column,assign by code or style in JSON| 19 | 20 | 21 | ## 1-n layout (n=2/3/4) 22 | 23 | A large element on the left ,several small element on the right,support assiging ratio of the left and right. 24 | 25 | ![](https://img.alicdn.com/tfs/TB1UmkEPVXXXXbDXXXXXXXXXXXX-559-239.png) 26 | 27 | There are three styles of the layout: 28 | 29 | * A large element on the left,one above one below on the right. 30 | * A large element on the left,one above two below on the right. 31 | * A large element on the left,one above three below on the right. 32 | 33 | Adjust depend on the count of itemModels in the layout 34 | 35 | |type|Description| 36 | |---|----| 37 | |5|One Drag N(N=2/3/4)| 38 | 39 | ## Drag Layout 40 | 41 | The layout can be dragged, auto hit to edges 42 | 43 | The No.0 element is in a drag layout. 44 | 45 | ![](https://img.alicdn.com/tfs/TB1Nv3DPVXXXXcaapXXXXXXXXXX-370-672.gif) 46 | 47 | |type|Description| 48 | |---|----| 49 | |7|Drag Layout| 50 | 51 | ## Fix Layout 52 | 53 | Fix at fixed position, or scroll to some position to show 54 | 55 | The No.0 element is in a fix layout. 56 | 57 | ![](https://img.alicdn.com/tfs/TB1tOUDPVXXXXcnaXXXXXXXXXXX-370-672.gif) 58 | 59 | |type|Description| 60 | |---|----| 61 | |8|Fix at top| 62 | |23|Fix at bottom| 63 | |28|Scroll to some layout to show and fix at top)| 64 | 65 | ## Sticky Layout 66 | 67 | If the layout hit the top of visible area, it will stick to top edge of visible area. 68 | 69 | The No.9 element is in a sticky layout 70 | 71 | ![](https://img.alicdn.com/tfs/TB1tOUDPVXXXXcnaXXXXXXXXXXX-370-672.gif) 72 | 73 | |type|Description| 74 | |---|----| 75 | |21|Stick to top| 76 | 77 | ## Page Scroll Layout 78 | 79 | Suitable for banner, it can auto scrolling , cycle scrolling or linear scrolling 80 | 81 | ![](https://img.alicdn.com/tps/TB1WOUsOpXXXXbzXFXXXXXXXXXX-373-90.gif) 82 | 83 | |type|Description| 84 | |---|----| 85 | |10|Page Scroll Layout| 86 | 87 | ## Water Flow Layout 88 | 89 | ![](https://img.alicdn.com/tfs/TB1tDEJPVXXXXXWaXXXXXXXXXXX-375-689.png) 90 | 91 | |type|Description| 92 | |---|----| 93 | |25|WaterFlow Layout| 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /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 | 3 | platform :ios, '8.0' 4 | 5 | target 'TangramDemo' do 6 | project 'TangramDemo/TangramDemo.xcodeproj' 7 | pod 'Tangram', :path => './' 8 | # pod 'VirtualView', :path => '../VirtualView/' 9 | # pod 'LazyScroll', :path => '../LazyScrollView/' 10 | end 11 | 12 | workspace 'Tangram' 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tangram - iOS 2 | 3 | Tangram is a UI Framework for building a fast and dynamic ScrollView. 4 | 5 | The system requirement for Tangram is iOS 7.0+ 6 | 7 | [中文站点](http://tangram.pingguohe.net) 8 | 9 | Tips: If you get ``[!] Unable to find a specification for `LazyScroll` `` when executed `pod install`, you can try to update `ruby` to `2.3.0` or higher and update `CocoaPods` to `1.0.0` or higher . If it doesn't work , you can try to reset or update CocoaPods master repo again . 10 | 11 | ## Feature 12 | 13 | - Two platform support (iOS & Android, See Tangram-Android in Github for Android Version) 14 | - Fast Generate View by JSON Data , provide default parser. 15 | - Easily control the reuseability of views 16 | - Provide multiple built-in layouts 17 | - Custom layout style (by JSON Data or code) 18 | - High scroll performance (Base on [LazyScrollView](https://github.com/alibaba/LazyScrollView)) 19 | - Extendable API 20 | 21 | ## Advantage 22 | 23 | Compare to system standard controls(like UICollectionView, GridView), 24 | the advantages of Tangram are : 25 | 26 | ### Easily control 'layout' selected for elements(cells). 27 | 28 | ![](https://gw.alicdn.com/tps/TB1c7HuPVXXXXaGaXXXXXXXXXXX-370-672.gif) 29 | 30 | In the picture above, it shows several kinds of layout, Tangram can easily control 31 | which kind of layout these elements use. You can find its usage in TangramDemo. 32 | 33 | ### Provide default parser , quick parse JSON to View 34 | 35 | JSON to View can be very easy by use our default parser. 36 | 37 | You can open `TangramDemo` to see how to tranfer JSON to view. 38 | 39 | The default parsers are same in two platform (Android and iOS). 40 | 41 | ### Provide several kinds of layout 42 | 43 | We provide internal layouts, including: 44 | 45 | * FlowLayout (like grid) 46 | * One drag N Layout (N=2/3/4) 47 | * Fix Layout 48 | * Sticky Layout 49 | * Dragable Layout 50 | * PageScroll Layout 51 | * WaterFlow Layout 52 | 53 | To See detailed performance of interal layouts , [Click me](https://github.com/alibaba/Tangram-iOS/blob/master/Docs/layoutIndex.md) 54 | 55 | ## Install 56 | 57 | Use Cocoapods to Get latest version of Tangram 58 | 59 | ``` 60 | pod 'Tangram' 61 | ``` 62 | 63 | 64 | ## Getting Started 65 | 66 | - See [Getting Started Guide](https://github.com/alibaba/Tangram-iOS/blob/master/Docs/getting-started.md) 67 | - Or Open project in `TangramDemo` and execute `pod install` to see detail usage. 68 | -------------------------------------------------------------------------------- /Tangram.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "Tangram" 4 | s.version = "2.1.5" 5 | s.summary = "Tangram is a modular UI solution for building native page dynamically & quickly." 6 | 7 | s.description = <<-DESC 8 | Tangram is a modular UI solution for building native page dynamically & quickly. 9 | And Tangram 2.x with VirtualView can create & release UI component dynamically. 10 | The solution also have an implementation for Andriod platform. 11 | DESC 12 | 13 | s.homepage = "https://github.com/alibaba/Tangram-iOS" 14 | s.license = { :type => 'MIT' } 15 | s.author = { "fydx" => "lbgg918@gmail.com", 16 | "HarrisonXi" => "gpra8764@gmail.com"} 17 | s.platform = :ios 18 | s.ios.deployment_target = '8.0' 19 | s.requires_arc = true 20 | s.source = { :git => "https://github.com/alibaba/Tangram-iOS.git", :tag => "2.1.5" } 21 | s.resources = 'Tangram/Resources/*' 22 | s.source_files = 'Tangram/**/*.{h,m}' 23 | 24 | s.dependency 'SDWebImage', '~> 4.2' 25 | s.dependency 'LazyScroll', '1.0.1' 26 | s.dependency 'VirtualView', '1.2.7' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /Tangram/Core/TangramView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramView.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @protocol TangramItemModelProtocol; 12 | @protocol TangramLayoutProtocol; 13 | @class TangramView; 14 | @class TangramBus; 15 | 16 | //**************************************************************** 17 | 18 | /** 19 | * Tangram get need info from datasource. 20 | */ 21 | @protocol TangramViewDatasource 22 | 23 | @required 24 | 25 | /** 26 | * return layout count in scrollView 27 | * 28 | * @param view TangramView 29 | * @return number Layout count in scrollView 30 | */ 31 | - (NSUInteger)numberOfLayoutsInTangramView:(TangramView *)view; 32 | 33 | /** 34 | * return element(subviews in layout,like UICollectionViewCell) Count in specific card. 35 | * 36 | * @param view TangramView 37 | * @param layout layout return in element Count 38 | * @return number element in the layout 39 | */ 40 | - (NSUInteger)numberOfItemsInTangramView:(TangramView *)view forLayout:(UIView *)layout; 41 | 42 | /** 43 | * Get a layout by index. 44 | * Tangram requires this Layout must be a subclass of UIView ,and implement TangramLayoutProtocol 45 | * Layout is like to Layout in UICollectionView 46 | * 47 | * @param view TangramView 48 | * @param index Layout index 49 | * @return layout layout 50 | */ 51 | - (UIView *)layoutInTangramView:(TangramView *)view atIndex:(NSUInteger)index; 52 | 53 | /** 54 | * Get element by index in layout. Element must be a UIView or a subclass of UIView 55 | * Before init a new element , you can call `dequeueReusableItemWithIdentifier` to get a reuseable view first. 56 | * 57 | * @param view TangramView 58 | * @param layout layout 59 | * @param index index in Layout 60 | * @return item element 61 | */ 62 | - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index; 63 | 64 | /** 65 | * According to the count returned from `numberOfItemsInTangramView`, generate a logical tree of models. 66 | * Here need return model by index and layout. 67 | * 68 | * @param view TangramView 69 | * @param layout Layout 70 | * @param index index in layout 71 | * @return model model,used to generate logical tree. 72 | */ 73 | - (NSObject *)itemModelInTangramView:(TangramView *)view forLayout:(UIView *)layout atIndex:(NSUInteger)index; 74 | 75 | @end 76 | 77 | //**************************************************************** 78 | 79 | @interface TangramView : TMLazyScrollView 80 | 81 | // 注意,修改 delegate 属性后需要将 scrollViewDidScroll: 事件转发回给 TangramView 82 | 83 | // Contains layouts in TangramView. Key :layout index;value:layout 84 | @property (nonatomic, strong, readonly) NSMutableDictionary *layoutDict; 85 | // Extra offset in vertical for StickyLayout and FixLayout 86 | @property (nonatomic, assign) CGFloat fixExtraOffset; 87 | // Bind TangramBus 88 | @property (nonatomic, weak) TangramBus *tangramBus; 89 | // Get FixLayout in TangramView 90 | @property (nonatomic, strong, readonly) NSMutableArray *fixLayoutArray; 91 | // Get StickyLayout in TangramView 92 | @property (nonatomic, strong, readonly) NSMutableArray *stickyLayoutArray; 93 | // Data source 94 | @property (nonatomic, weak, readonly) id clDataSource; 95 | // Enable margin deduplication function. 96 | @property (nonatomic, assign) BOOL enableMarginDeduplication; 97 | 98 | 99 | - (void)setDataSource:(id)dataSource; 100 | // Refresh view according to datasource. 101 | - (void)reloadData; 102 | // When height of layer is changed and the model is not changed, call this method. 103 | - (void)reLayoutContent; 104 | // When only a specific layout height need to be changed , or models in some layout changed 105 | // You can call this method to refresh a layout. 106 | - (void)reloadLayout:(UIView *)layout; 107 | // Models in layout not changed but height need to be changed , call this method. 108 | - (void)heightChanged; 109 | // Clean layouts. If cleanElement is YES, here will clean inner element 110 | // If cleanElement is NO, inner elements will be placed on the recycle pool. 111 | - (void)removeLayoutsAndElements:(BOOL)cleanElement; 112 | // Call this method when you need reset times in `mui_didEnterTimes` 113 | - (void)resetLayoutEnterTimes; 114 | // Set vertical offset to layouts below some specific layout 115 | - (void)changeLayoutPositionBelowLayout:(UIView *)layout offset:(CGFloat)offset; 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramAction.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramAction.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramContext; 11 | 12 | 13 | @protocol TangramActionProtocol 14 | 15 | - (void)executeWithContext:(nonnull TangramContext *)context; 16 | 17 | @end 18 | 19 | @interface TangramAction : NSObject 20 | 21 | @property (nonatomic, weak, nullable) id target; 22 | @property (nonatomic, assign, nullable) SEL selector; 23 | 24 | - (void)executeWithContext:(nonnull TangramContext *)context; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramAction.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramAction.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramAction.h" 9 | #import "TangramContext.h" 10 | #import "TMUtils.h" 11 | 12 | @implementation TangramAction 13 | 14 | - (void)executeWithContext:(TangramContext *)context 15 | { 16 | if (NULL == self.selector) { 17 | self.selector = @selector(executeWithContext:); 18 | } 19 | if (self.target && [self.target respondsToSelector:self.selector]) { 20 | // Properties of context are weak properties,so we use a holder to holder them. 21 | // Make sure they will not be released in dispatch body. 22 | __block NSMutableArray *holder = [[NSMutableArray alloc] init]; 23 | if (context.event) { 24 | [holder tm_safeAddObject:context.event]; 25 | } 26 | if (context.poster) { 27 | [holder tm_safeAddObject:context.poster]; 28 | } 29 | if (context.tangram) { 30 | [holder tm_safeAddObject:context.tangram]; 31 | } 32 | 33 | __weak typeof(self) wself = self; 34 | dispatch_async(dispatch_get_main_queue(), ^{ 35 | __strong typeof(wself) sself = wself; 36 | #pragma clang diagnostic push 37 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 38 | [sself.target performSelector:sself.selector withObject:context]; 39 | [sself releaseThings:holder]; 40 | #pragma clang diagnostic pop 41 | }); 42 | } 43 | } 44 | 45 | - (void)releaseThings:(NSArray *)array 46 | { 47 | // For holding the items in array. 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBus.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBus.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramEvent; 11 | 12 | 13 | @interface TangramBus : NSObject 14 | 15 | /** 16 | * Post a event to eventbus 17 | * 18 | * @param event TangramEvent event 19 | */ 20 | - (void)postEvent:(nonnull TangramEvent *)event; 21 | 22 | /** 23 | * Regist an action to some event. Action will be executed in main thread. 24 | * 25 | * @param action NSString the name of executing method 26 | * @param executer NSString a instance or class name 27 | * @param topic NSString event topic 28 | */ 29 | - (void)registerAction:(nonnull NSString *)action 30 | ofExecuter:(nonnull id)executer 31 | onEventTopic:(nonnull NSString *)topic; 32 | 33 | /** 34 | * Regist an action to some event. Action will be executed in main thread. 35 | * 36 | * @param action NSString the name of executing method 37 | * @param executer NSString a instance or class name 38 | * @param topic NSString event topic 39 | * @param identifier NSString the identifier of poster 40 | */ 41 | - (void)registerAction:(nonnull NSString *)action 42 | ofExecuter:(nonnull id)executer 43 | onEventTopic:(nonnull NSString *)topic 44 | fromPosterIdentifier:(nullable NSString *)identifier; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBus.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBus.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramBus.h" 9 | #import "TangramAction.h" 10 | #import "TangramEvent.h" 11 | #import "TangramEventQueue.h" 12 | #import "TangramEventDispatcher.h" 13 | 14 | @interface TangramBus () 15 | 16 | @property (nonatomic, strong) TangramEventQueue *queue; 17 | @property (nonatomic, strong) TangramEventDispatcher *dispatcher; 18 | 19 | @end 20 | 21 | @implementation TangramBus 22 | 23 | #pragma mark - Private 24 | - (void)dispatchEvent 25 | { 26 | __weak typeof(self) wself = self; 27 | dispatch_async(dispatch_get_main_queue(), ^{ 28 | __strong typeof(wself) sself = wself; 29 | TangramEvent *event = [sself.queue popEvent]; 30 | while (event) { 31 | [sself.dispatcher dispatchEvent:event]; 32 | event = [sself.queue popEvent]; 33 | } 34 | }); 35 | } 36 | 37 | #pragma mark - Getter & Setter 38 | - (TangramEventDispatcher *)dispatcher 39 | { 40 | if (nil == _dispatcher) { 41 | _dispatcher = [[TangramEventDispatcher alloc] init]; 42 | } 43 | return _dispatcher; 44 | } 45 | 46 | - (TangramEventQueue *)queue 47 | { 48 | if (nil == _queue) { 49 | _queue = [[TangramEventQueue alloc] init]; 50 | } 51 | return _queue; 52 | } 53 | 54 | #pragma mark - Public 55 | - (void)postEvent:(TangramEvent *)event 56 | { 57 | [self.queue pushEvent:event]; 58 | [self dispatchEvent]; 59 | } 60 | 61 | - (void)registerAction:(NSString *)action 62 | ofExecuter:(id)executer 63 | onEventTopic:(NSString *)topic 64 | { 65 | [self registerAction:action ofExecuter:executer onEventTopic:topic fromPosterIdentifier:nil]; 66 | } 67 | 68 | - (void)registerAction:(NSString *)anAction 69 | ofExecuter:(id)executer 70 | onEventTopic:(NSString *)topic 71 | fromPosterIdentifier:(NSString *)identifier 72 | { 73 | if (executer) { 74 | TangramAction *action = [[TangramAction alloc] init]; 75 | action.target = executer; 76 | if (anAction && anAction.length > 0) { 77 | action.selector = NSSelectorFromString(anAction); 78 | } 79 | 80 | [self.dispatcher registerAction:action onEventTopic:topic andIdentifier:identifier]; 81 | } 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndex.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndex.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramEvent; 11 | @class TangramAction; 12 | 13 | @interface TangramBusIndex : NSObject 14 | 15 | - (void)addAction:(TangramAction *)action forTopic:(NSString *)topic andPoster:(NSString *)identifier; 16 | - (NSArray *)actionsOnEvent:(TangramEvent *)event; 17 | 18 | @end 19 | 20 | 21 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndex.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndex.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramBusIndex.h" 9 | #import "TangramEvent.h" 10 | #import "TangramAction.h" 11 | 12 | @implementation TangramBusIndex 13 | 14 | - (NSArray *)actionsOnEvent:(TangramEvent *)event 15 | { 16 | return nil; 17 | } 18 | 19 | - (void)addAction:(TangramAction *)action forTopic:(NSString *)topic andPoster:(NSString *)identifier 20 | { 21 | } 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndexClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndexClass.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramBusIndex.h" 10 | 11 | @interface TangramBusIndexClass : TangramBusIndex 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndexClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndexClass.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramBusIndexClass.h" 9 | #import "TangramEvent.h" 10 | #import "TMUtils.h" 11 | 12 | 13 | @interface TangramBusIndexClass () 14 | 15 | @property (nonatomic, strong) NSMutableDictionary *index; 16 | 17 | @end 18 | 19 | @implementation TangramBusIndexClass 20 | 21 | - (NSMutableDictionary *)index 22 | { 23 | if (nil == _index) { 24 | _index = [[NSMutableDictionary alloc] init]; 25 | } 26 | return _index; 27 | } 28 | 29 | - (NSArray *)actionsOnEvent:(TangramEvent *)event 30 | { 31 | NSArray *actions = nil; 32 | if (event.identifier) { 33 | NSString *key = [event.topic stringByAppendingFormat:@"_%@", event.identifier]; 34 | actions = [self.index tm_arrayForKey:key]; 35 | } 36 | return actions; 37 | } 38 | 39 | - (void)addAction:(TangramAction *)action forTopic:(NSString *)topic andPoster:(NSString *)identifier 40 | { 41 | if (identifier && 0 < identifier.length) { 42 | NSString *key = [topic stringByAppendingFormat:@"_%@", identifier]; 43 | NSMutableArray *actions = [self.index tm_safeObjectForKey:key class:[NSMutableArray class]]; 44 | if (nil == actions) { 45 | actions = [[NSMutableArray alloc] init]; 46 | } 47 | [actions tm_safeAddObject:action]; 48 | [self.index tm_safeSetObject:actions forKey:key]; 49 | } 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndexTopic.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndexTopic.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramBusIndex.h" 10 | 11 | @interface TangramBusIndexTopic : TangramBusIndex 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramBusIndexTopic.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramBusIndexTopic.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramBusIndexTopic.h" 9 | #import "TangramEvent.h" 10 | #import "TMUtils.h" 11 | 12 | @interface TangramBusIndexTopic () 13 | 14 | @property (nonatomic, strong) NSMutableDictionary *index; 15 | 16 | @end 17 | 18 | @implementation TangramBusIndexTopic 19 | 20 | - (NSMutableDictionary *)index 21 | { 22 | if (nil == _index) { 23 | _index = [[NSMutableDictionary alloc] init]; 24 | } 25 | return _index; 26 | } 27 | 28 | - (NSArray *)actionsOnEvent:(TangramEvent *)event 29 | { 30 | return [self.index tm_arrayForKey:event.topic]; 31 | } 32 | 33 | - (void)addAction:(TangramAction *)action forTopic:(NSString *)topic andPoster:(NSString *)identifier 34 | { 35 | NSMutableArray *actions = [self.index tm_safeObjectForKey:topic class:[NSMutableArray class]]; 36 | if (nil == actions) { 37 | actions = [[NSMutableArray alloc] init]; 38 | } 39 | [actions tm_safeAddObject:action]; 40 | [self.index tm_safeSetObject:actions forKey:topic]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramContext.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramView; 11 | @class TangramEvent; 12 | 13 | 14 | @interface TangramContext : NSObject 15 | 16 | @property (nonatomic, weak) id poster; 17 | @property (nonatomic, weak) TangramView *tangram; 18 | @property (nonatomic, weak) TangramEvent *event; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramContext.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramContext.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramContext.h" 9 | 10 | @implementation TangramContext 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEvent.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEvent.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | #define TangramEventTopicJumpAction @"openUrl" 11 | #define TangramEventTopicExposure @"exposure" 12 | #define TangramEventTopicElementLeave @"elementLeave" 13 | @class TangramContext; 14 | @class TangramView; 15 | 16 | 17 | @interface TangramEvent : NSObject 18 | 19 | /** 20 | * Event Topic. 21 | */ 22 | @property (nonnull, nonatomic, copy, readonly) NSString *topic; 23 | 24 | /** 25 | * The id of sponsor. 26 | */ 27 | @property (nullable, nonatomic, copy, readonly) NSString *identifier; 28 | 29 | /** 30 | * Context, it contains the weak reference of TangramView, event instance, and poster. 31 | */ 32 | @property (nonnull, nonatomic, strong, readonly) TangramContext *context; 33 | 34 | /** 35 | * Business Params. 36 | */ 37 | - (nullable NSDictionary *)params; 38 | 39 | /** 40 | * Meta Params. 41 | */ 42 | - (nullable NSDictionary *)meta; 43 | 44 | /** 45 | * Add a param to business params (can be read from params) 46 | */ 47 | - (void)setParam:(nonnull id)param forKey:(nonnull NSString *)key; 48 | 49 | /** 50 | * Add a param to meta params (can be read from meta) 51 | */ 52 | - (void)setMeta:(nonnull id)param forKey:(nonnull NSString *)key; 53 | 54 | /** 55 | * Generate a context instance 56 | * 57 | * @param tangram TangramView Tangram instance 58 | * @param topic NSString Event topic 59 | * @param identifier NSString The id of poster 60 | * @param poster id The instance of poster 61 | */ 62 | - (nonnull instancetype)initWithTopic:(nonnull NSString *)topic 63 | withTangramView:(nullable TangramView *)tangram 64 | posterIdentifier:(nullable NSString *)identifier 65 | andPoster:(nonnull id)poster; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEvent.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEvent.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramEvent.h" 9 | #import "TangramContext.h" 10 | #import "TangramView.h" 11 | #import "TMUtils.h" 12 | 13 | @interface TangramEvent () { 14 | NSMutableDictionary *_params; 15 | NSMutableDictionary *_meta; 16 | } 17 | 18 | @end 19 | 20 | @implementation TangramEvent 21 | 22 | #pragma mark - Getter & Setter 23 | - (NSDictionary *)params 24 | { 25 | return _params ? [_params copy] : nil; 26 | } 27 | 28 | - (NSDictionary *)meta 29 | { 30 | return _meta ? [_meta copy] : nil; 31 | } 32 | 33 | - (void)setParam:(id)param forKey:(NSString *)key 34 | { 35 | if (nil == _params) { 36 | _params = [[NSMutableDictionary alloc] init]; 37 | } 38 | [_params tm_safeSetObject:param forKey:key]; 39 | } 40 | 41 | - (void)setMeta:(id)meta forKey:(NSString *)key 42 | { 43 | if (nil == _meta) { 44 | _meta = [[NSMutableDictionary alloc] init]; 45 | } 46 | [_meta tm_safeSetObject:meta forKey:key]; 47 | } 48 | 49 | #pragma mark - Public 50 | 51 | - (instancetype)initWithTopic:(NSString *)topic 52 | withTangramView:(TangramView *)tangram 53 | posterIdentifier:(NSString *)identifier 54 | andPoster:(id)poster 55 | { 56 | self = [super init]; 57 | if (self) { 58 | _topic = topic; 59 | _identifier = identifier; 60 | _context = [[TangramContext alloc] init]; 61 | _context.poster = poster; 62 | _context.tangram = tangram; 63 | _context.event = self; 64 | } 65 | return self; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEventDispatcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEventDispatcher.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramEvent; 11 | @class TangramAction; 12 | 13 | 14 | @interface TangramEventDispatcher : NSObject 15 | 16 | - (void)registerAction:(TangramAction *)action onEventTopic:(NSString *)topic andIdentifier:(NSString *)identifier; 17 | - (void)dispatchEvent:(TangramEvent *)event; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEventDispatcher.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEventDispatcher.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramEventDispatcher.h" 9 | #import "TangramAction.h" 10 | #import "TangramEvent.h" 11 | #import "TangramBusIndexClass.h" 12 | #import "TangramBusIndexTopic.h" 13 | 14 | @interface TangramEventDispatcher () 15 | 16 | @property (nonatomic, strong) TangramBusIndexClass *classIndex; 17 | @property (nonatomic, strong) TangramBusIndexTopic *topicIndex; 18 | 19 | @end 20 | 21 | @implementation TangramEventDispatcher 22 | 23 | #pragma mark - Public 24 | - (void)dispatchEvent:(TangramEvent *)event 25 | { 26 | NSMutableArray *actionList = [[NSMutableArray alloc] init]; 27 | 28 | NSArray *topicActions = [self.topicIndex actionsOnEvent:event]; 29 | if (topicActions && topicActions.count > 0) { 30 | [actionList addObjectsFromArray:topicActions]; 31 | } 32 | 33 | NSArray *classActions = [self.classIndex actionsOnEvent:event]; 34 | if (classActions && classActions.count > 0) { 35 | [actionList addObjectsFromArray:classActions]; 36 | } 37 | 38 | for (TangramAction *action in actionList) { 39 | if (action && [action isKindOfClass:[TangramAction class]]) { 40 | [action executeWithContext:event.context]; 41 | } 42 | } 43 | } 44 | 45 | - (void)registerAction:(TangramAction *)action onEventTopic:(NSString *)topic andIdentifier:(NSString *)identifier 46 | { 47 | if (identifier && 0 < identifier.length) { 48 | [self.classIndex addAction:action forTopic:topic andPoster:identifier]; 49 | } else { 50 | [self.topicIndex addAction:action forTopic:topic andPoster:nil]; 51 | } 52 | } 53 | 54 | #pragma mark - Getter * Setter 55 | - (TangramBusIndexClass *)classIndex 56 | { 57 | if (nil == _classIndex) { 58 | _classIndex = [[TangramBusIndexClass alloc] init]; 59 | } 60 | return _classIndex; 61 | } 62 | 63 | - (TangramBusIndexTopic *)topicIndex 64 | { 65 | if (nil == _topicIndex) { 66 | _topicIndex = [[TangramBusIndexTopic alloc] init]; 67 | } 68 | return _topicIndex; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEventQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEventQueue.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramEvent; 11 | 12 | 13 | @interface TangramEventQueue : NSObject 14 | 15 | /** 16 | * The length of the event queue. 17 | */ 18 | - (NSUInteger)length; 19 | 20 | /** 21 | * Add a event to queue. 22 | */ 23 | - (void)pushEvent:(nonnull TangramEvent *)event; 24 | 25 | /** 26 | * Get a event. 27 | */ 28 | - (nullable TangramEvent *)popEvent; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Tangram/EventBus/TangramEventQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEventQueue.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramEventQueue.h" 9 | #import "TangramEvent.h" 10 | 11 | @interface TangramEventQueue () 12 | 13 | @property (nonatomic, strong) NSMutableArray *queue; 14 | 15 | @end 16 | 17 | @implementation TangramEventQueue 18 | 19 | #pragma mark - Getter & Setter 20 | - (NSMutableArray *)queue 21 | { 22 | if (nil == _queue) { 23 | _queue = [[NSMutableArray alloc] init]; 24 | } 25 | return _queue; 26 | } 27 | 28 | #pragma mark - Public 29 | - (NSUInteger)length 30 | { 31 | return [self.queue count]; 32 | } 33 | 34 | - (void)pushEvent:(TangramEvent *)event 35 | { 36 | if ([event isKindOfClass:[TangramEvent class]]) { 37 | [self.queue addObject:event]; 38 | } 39 | } 40 | 41 | - (TangramEvent *)popEvent 42 | { 43 | if (self.length == 0) { 44 | return nil; 45 | } 46 | TangramEvent *firstEvent = [self.queue firstObject]; 47 | if (firstEvent) { 48 | [self.queue removeObjectAtIndex:0]; 49 | } 50 | if ([firstEvent isKindOfClass:[TangramEvent class]]) { 51 | return firstEvent; 52 | } 53 | return nil; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultElementFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultElementFactory.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramElementFactoryProtocol.h" 10 | @interface TangramDefaultElementFactory : NSObject 11 | 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultElementFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultElementFactory.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDefaultElementFactory.h" 9 | #import "TMUtils.h" 10 | #import "TangramDefaultItemModel.h" 11 | #import 12 | 13 | @interface TangramDefaultElementFactory() 14 | 15 | @property (nonatomic, strong) NSMutableArray *elementForbiddenPropertyArray; 16 | 17 | @end 18 | 19 | 20 | @implementation TangramDefaultElementFactory 21 | 22 | + (TangramDefaultElementFactory*)sharedInstance 23 | { 24 | static TangramDefaultElementFactory *_elementFactory= nil; 25 | static dispatch_once_t oncePredicate; 26 | dispatch_once(&oncePredicate, ^{ 27 | _elementFactory = [[TangramDefaultElementFactory alloc] init]; 28 | }); 29 | return _elementFactory; 30 | } 31 | - (instancetype)init 32 | { 33 | if (self = [super init]) { 34 | self.elementForbiddenPropertyArray = [[NSMutableArray alloc]init]; 35 | unsigned int count; 36 | objc_property_t *properties = class_copyPropertyList([UIView class], &count); 37 | for (int i = 0; i < count; i++) { 38 | objc_property_t property = properties[i]; 39 | // Get property name (C String) 40 | const char *cName = property_getName(property); 41 | // Transfer to objective-c string 42 | NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding]; 43 | [self.elementForbiddenPropertyArray addObject:name]; 44 | } 45 | NSArray *otherForbiddenArray = @[@"margin",@"display",@"colspan",@"aspectRatio",@"height",@"width",@"position"]; 46 | [self.elementForbiddenPropertyArray addObjectsFromArray:otherForbiddenArray]; 47 | 48 | } 49 | return self; 50 | } 51 | 52 | 53 | 54 | 55 | /** 56 | Return view by model 57 | 58 | @param model ItemModel Generated by ItemModelFactoty 59 | @return view 60 | */ 61 | + (UIView *)elementByModel:(NSObject *)model 62 | { 63 | //if it's model for inner layout, we return nil. Here won't generate any UIView 64 | if ([model respondsToSelector:@selector(layoutIdentifierForLayoutModel)] && model.layoutIdentifierForLayoutModel && model.layoutIdentifierForLayoutModel.length > 0) { 65 | return nil; 66 | } 67 | NSObject *object = nil; 68 | NSString *elementClassName = model.linkElementName; 69 | if ([model isKindOfClass:[TangramDefaultItemModel class]] && [model respondsToSelector:@selector(linkElementName)] && model.linkElementName.length > 0) { 70 | object = [[NSClassFromString(elementClassName) alloc] init]; 71 | if ([object isKindOfClass:[UIView class]]) { 72 | [self fillObjectPropertyByObject:(UIView *)object viewModel:(TangramDefaultItemModel *)model]; 73 | } 74 | } 75 | return (UIView *)object; 76 | } 77 | 78 | /** 79 | Return view after be refreshed 80 | 81 | @param element to be refreshed 82 | @return element after refreshed 83 | */ 84 | + (UIView *)refreshElement:(UIView *)element byModel:(NSObject *)model 85 | { 86 | if (nil == element) { 87 | return nil; 88 | } 89 | if ([model isKindOfClass:[TangramDefaultItemModel class]] && [model respondsToSelector:@selector(linkElementName)] && model.linkElementName.length > 0) { 90 | [self fillObjectPropertyByObject:element viewModel:(TangramDefaultItemModel *)model]; 91 | } 92 | return element; 93 | } 94 | 95 | + (void)fillObjectPropertyByObject:(NSObject *)object viewModel:(TangramDefaultItemModel *)model 96 | { 97 | TangramDefaultElementFactory *elementFactory = [TangramDefaultElementFactory sharedInstance]; 98 | for (NSString *key in [model bizKeys]) { 99 | //首先,确保可以取出来值 100 | if ([self objectHasSetter:object propertyName:key] 101 | && ![elementFactory.elementForbiddenPropertyArray containsObject:key] 102 | && [model bizValueForKey:key] != nil ) { 103 | //在这里判一下类型 104 | id bizValue = [model bizValueForKey:key]; 105 | [self fillObjectPropertyByObject:object key:key value:bizValue]; 106 | } 107 | } 108 | for (NSString *key in [model styleKeys]) { 109 | if ([self objectHasSetter:object propertyName:key] 110 | && ![elementFactory.elementForbiddenPropertyArray containsObject:key] 111 | && [model styleValueForKey:key] != nil) { 112 | id styleValue = [model styleValueForKey:key]; 113 | [self fillObjectPropertyByObject:object key:key value:styleValue]; 114 | } 115 | } 116 | } 117 | + (void)fillObjectPropertyByObject:(NSObject *)object key:(NSString *)key value:(id)value 118 | { 119 | //拿到Property 120 | objc_property_t propMeta = class_getProperty([object class], [key UTF8String]); 121 | if (propMeta != NULL) { 122 | NSString *attributesString = [NSString stringWithUTF8String:property_getAttributes(propMeta)]; 123 | //支持property参数为 NSString/NSArray/NSDictionary/NSNumber 其他的类型直接跳过 124 | if ([attributesString containsString:@"NSString"]) { 125 | if (![value isKindOfClass:[NSString class]]) { 126 | return; 127 | } 128 | [object setValue:value forKey:key]; 129 | } else if ([attributesString containsString:@"NSMutableString"]) { 130 | if (![value isKindOfClass:[NSString class]]) { 131 | return; 132 | } 133 | [object setValue:[NSMutableString stringWithString:value] forKey:key]; 134 | } else if ([attributesString containsString:@"NSArray"]) { 135 | if (![value isKindOfClass:[NSArray class]]) { 136 | return; 137 | } 138 | [object setValue:value forKey:key]; 139 | } else if ([attributesString containsString:@"NSMutableArray"]) { 140 | if (![value isKindOfClass:[NSArray class]]) { 141 | return; 142 | } 143 | [object setValue:[NSMutableArray arrayWithArray:value] forKey:key]; 144 | } else if ([attributesString containsString:@"NSDictionary"]) { 145 | if (![value isKindOfClass:[NSDictionary class]]) { 146 | return; 147 | } 148 | [object setValue:value forKey:key]; 149 | } else if ([attributesString containsString:@"NSMutableDictionary"]) { 150 | if (![value isKindOfClass:[NSDictionary class]]) { 151 | return; 152 | } 153 | [object setValue:[NSMutableDictionary dictionaryWithDictionary:value] forKey:key]; 154 | }else if ([attributesString containsString:@"NSData"]) { 155 | if (![value isKindOfClass:[NSData class]]) { 156 | return; 157 | } 158 | [object setValue:[NSData dataWithData:value] forKey:key]; 159 | } 160 | else if ([attributesString containsString:@"NSNumber"]) { 161 | if ([value isKindOfClass:[NSNumber class]]) { 162 | [object setValue:value forKey:key]; 163 | } 164 | else if ([value isKindOfClass:[NSString class]]) { 165 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 166 | formatter.numberStyle = NSNumberFormatterDecimalStyle; 167 | NSNumber *number = [formatter numberFromString:value]; 168 | [object setValue:number forKey:key];; 169 | } 170 | } 171 | } 172 | else{ 173 | @try { 174 | [object setValue:value forKey:key]; 175 | } @catch (NSException *exception) { 176 | NSLog(@"Error at Tangram find setter in element"); 177 | } @finally { 178 | 179 | } 180 | 181 | } 182 | } 183 | 184 | + (BOOL)objectHasSetter:(NSObject *)object propertyName:(NSString *)propertyName 185 | { 186 | NSString *setterString = [NSString stringWithFormat:@"set%@%@:", 187 | [[propertyName substringToIndex:1] capitalizedString], 188 | [propertyName substringFromIndex:1]]; 189 | return [object respondsToSelector:NSSelectorFromString(setterString)]; 190 | } 191 | 192 | + (BOOL)objectHasGetter:(NSObject *)object propertyName:(NSString *)propertyName 193 | { 194 | return [object respondsToSelector:NSSelectorFromString(propertyName)]; 195 | } 196 | 197 | @end 198 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultItemModelFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultItemModelFactory.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelFactoryProtocol.h" 10 | #import "TangramDefaultItemModel.h" 11 | 12 | @interface TangramDefaultItemModelFactory : NSObject 13 | 14 | + (TangramDefaultItemModel *)praseDictToItemModel:(TangramDefaultItemModel *)itemModel dict:(NSDictionary *)dict; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultItemModelFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultItemModelFactory.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDefaultItemModelFactory.h" 9 | #import "TangramDefaultItemModel.h" 10 | #import "TMUtils.h" 11 | 12 | #import "TangramDefaultLayoutFactory.h" 13 | #import "TangramDefaultDataSourceHelper.h" 14 | 15 | @interface TangramDefaultItemModelFactory() 16 | 17 | @property (nonatomic, strong) NSMutableDictionary *elementTypeMap; 18 | 19 | @end 20 | 21 | @implementation TangramDefaultItemModelFactory 22 | 23 | + (TangramDefaultItemModelFactory*)sharedInstance 24 | { 25 | static TangramDefaultItemModelFactory *_itemModelFactory= nil; 26 | static dispatch_once_t oncePredicate; 27 | dispatch_once(&oncePredicate, ^{ 28 | _itemModelFactory = [[TangramDefaultItemModelFactory alloc] init]; 29 | }); 30 | return _itemModelFactory; 31 | } 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | self.elementTypeMap = [[NSMutableDictionary alloc]init]; 37 | NSString *elementMapPath = [[NSBundle mainBundle] pathForResource:@"TangramKitVVElementTypeMap" ofType:@"plist"]; 38 | [self.elementTypeMap addEntriesFromDictionary:[TangramDefaultItemModelFactory decodeElementTypeMap:[NSArray arrayWithContentsOfFile:elementMapPath]]]; 39 | } 40 | return self; 41 | } 42 | 43 | + (NSObject *)itemModelByDict:(NSDictionary *)dict 44 | { 45 | TangramDefaultItemModel *itemModel = [[TangramDefaultItemModel alloc]init]; 46 | return [[self class]praseDictToItemModel:itemModel dict:dict]; 47 | } 48 | 49 | + (TangramDefaultItemModel *)praseDictToItemModel:(TangramDefaultItemModel *)itemModel dict:(NSDictionary *)dict 50 | { 51 | NSString *type = [dict tm_stringForKey:@"type"]; 52 | itemModel.type = type; 53 | NSDictionary *styleDict =[dict tm_dictionaryForKey:@"style"]; 54 | NSObject *margin =[styleDict objectForKey:@"margin"]; 55 | if ([margin isKindOfClass:[NSString class]]) { 56 | NSString *marginString = [(NSString *)margin stringByReplacingOccurrencesOfString:@"[" withString:@""]; 57 | marginString = [marginString stringByReplacingOccurrencesOfString:@"]" withString:@""]; 58 | NSArray *marginArray = [marginString componentsSeparatedByString:@","]; 59 | if (marginArray && 4 == marginArray.count) { 60 | itemModel.margin = [TangramDefaultDataSourceHelper parseArrayWithRP:marginArray]; 61 | } 62 | } 63 | else if(![margin isKindOfClass:[NSArray class]]) 64 | { 65 | itemModel.margin = @[@0, @0, @0, @0]; 66 | } 67 | else{ 68 | itemModel.margin = [TangramDefaultDataSourceHelper parseArrayWithRP:[styleDict tm_safeObjectForKey:@"margin"]]; 69 | } 70 | if ([[styleDict tm_stringForKey:@"display"] isEqualToString:@"block"]) { 71 | itemModel.display = @"block"; 72 | } 73 | else{ 74 | itemModel.display = @"inline"; 75 | } 76 | //针对style中的height和width 77 | if ([styleDict tm_safeObjectForKey:@"height"] != nil) { 78 | if([[styleDict tm_stringForKey:@"height"]containsString:@"rp"]){ 79 | itemModel.heightFromStyle = [TangramDefaultDataSourceHelper floatValueByRPObject:[styleDict tm_safeObjectForKey:@"height"]]; 80 | } 81 | else{ 82 | itemModel.heightFromStyle = [styleDict tm_floatForKey:@"height"]; 83 | } 84 | } 85 | if ([styleDict tm_safeObjectForKey:@"width"] != nil) { 86 | if([[styleDict tm_stringForKey:@"width"]containsString:@"rp"]){ 87 | itemModel.heightFromStyle = [TangramDefaultDataSourceHelper floatValueByRPObject:[styleDict tm_safeObjectForKey:@"width"]]; 88 | } 89 | else{ 90 | itemModel.widthFromStyle = [styleDict tm_floatForKey:@"width"]; 91 | } 92 | } 93 | else if ([[styleDict tm_stringForKey:@"width"] isEqualToString:@"-1"]) { 94 | //width 配-1 意味着屏幕宽度 95 | itemModel.widthFromStyle = [UIScreen mainScreen].bounds.size.width; 96 | } 97 | if ([styleDict tm_floatForKey:@"aspectRatio"] > 0.f) { 98 | itemModel.modelAspectRatio = [styleDict tm_floatForKey:@"aspectRatio"]; 99 | } 100 | if ([styleDict tm_floatForKey:@"ratio"] > 0.f) { 101 | itemModel.modelAspectRatio = [styleDict tm_floatForKey:@"ratio"]; 102 | } 103 | itemModel.colspan = [styleDict tm_integerForKey:@"colspan"]; 104 | itemModel.position = [dict tm_stringForKey:@"position"]; 105 | itemModel.specificReuseIdentifier = [styleDict tm_stringForKey:@"reuseId"]; 106 | itemModel.disableReuse = [styleDict tm_boolForKey:@"disableReuse"]; 107 | 108 | for (NSString *key in [dict allKeys]) { 109 | if ([key isEqualToString:@"type"] || [key isEqualToString:@"style"] ) { 110 | continue; 111 | } 112 | else{ 113 | [itemModel setBizValue:[dict tm_safeObjectForKey:key] forKey:key]; 114 | } 115 | } 116 | for (NSString *key in [styleDict allKeys]) { 117 | if ([key isEqualToString:@"margin"] || [key isEqualToString:@"display"]||[key isEqualToString:@"colspan"] 118 | || [key isEqualToString:@"height"] || [key isEqualToString:@"width"] ) { 119 | continue; 120 | } 121 | else{ 122 | [itemModel setStyleValue:[styleDict tm_safeObjectForKey:key] forKey:key]; 123 | } 124 | } 125 | if ([[dict tm_stringForKey:@"kind"] isEqualToString:@"row"] || [TangramDefaultLayoutFactory layoutClassNameByType:type].length > 0) { 126 | itemModel.layoutIdentifierForLayoutModel = [dict tm_stringForKey:@"id"]; 127 | } 128 | //itemModel.specificReuseIdentifier = [dict tm_stringForKey:@"muiID"]; 129 | itemModel.linkElementName = [[TangramDefaultItemModelFactory sharedInstance].elementTypeMap tm_stringForKey:itemModel.type]; 130 | //TODO specificMuiID 增加逻辑 131 | return itemModel; 132 | } 133 | + (NSMutableDictionary *)decodeElementTypeMap:(NSArray *)mapArray 134 | { 135 | NSMutableDictionary *mapDict = [[NSMutableDictionary alloc]init]; 136 | for (NSDictionary *dict in mapArray) { 137 | NSString *key = [dict tm_stringForKey:@"type"]; 138 | NSString *value = [dict tm_stringForKey:@"element"]; 139 | if (key.length > 0 && value.length > 0) { 140 | NSAssert(![[mapDict allKeys] containsObject:key], @"There are repeat registration for element!Please check type!"); 141 | [mapDict setObject:value forKey:key]; 142 | } 143 | } 144 | return mapDict; 145 | } 146 | /** 147 | Regist Element 148 | 149 | @param type In ItemModel we need return a itemType, the itemType will be used here 150 | */ 151 | + (void)registElementType:(NSString *)type className:(NSString *)elementClassName 152 | { 153 | if ([type isKindOfClass:[NSString class]] && type.length > 0 154 | && [elementClassName isKindOfClass:[NSString class]] && elementClassName.length > 0) { 155 | [[TangramDefaultItemModelFactory sharedInstance].elementTypeMap tm_safeSetObject:[elementClassName copy] forKey:[type copy]]; 156 | } 157 | } 158 | 159 | + (BOOL)isTypeRegisted:(NSString *)type 160 | { 161 | if ([[[TangramDefaultItemModelFactory sharedInstance].elementTypeMap allKeys]containsObject:type]) { 162 | return YES; 163 | } 164 | return NO; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultLayoutFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultLayoutFactory.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutFactoryProtocol.h" 10 | 11 | @interface TangramDefaultLayoutFactory : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Tangram/Factory/TangramDefaultLayoutFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultLayoutFactory.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDefaultLayoutFactory.h" 9 | #import "TMUtils.h" 10 | #import "TangramScrollFlowLayout.h" 11 | #import "TangramLayoutParseHelper.h" 12 | #import 13 | 14 | @interface TangramDefaultLayoutFactory() 15 | //Key : type(String) , Value: layout class name(String) 16 | @property (nonatomic, strong) NSMutableDictionary *layoutTypeMap; 17 | 18 | @end 19 | 20 | @implementation TangramDefaultLayoutFactory 21 | 22 | + (TangramDefaultLayoutFactory*)sharedInstance 23 | { 24 | static TangramDefaultLayoutFactory *_layoutFactory = nil; 25 | static dispatch_once_t oncePredicate; 26 | dispatch_once(&oncePredicate, ^{ 27 | _layoutFactory = [[TangramDefaultLayoutFactory alloc] init]; 28 | 29 | }); 30 | return _layoutFactory; 31 | } 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | _layoutTypeMap = [[NSMutableDictionary alloc]init]; 37 | NSString *layoutMapPath = [[NSBundle mainBundle] pathForResource:@"TangramLayoutTypeMap" ofType:@"plist"]; 38 | [_layoutTypeMap addEntriesFromDictionary:[TangramDefaultLayoutFactory decodeTypeMap:[NSArray arrayWithContentsOfFile:layoutMapPath]]]; 39 | } 40 | return self; 41 | } 42 | 43 | /** 44 | Regist Layout Type and its className 45 | 46 | @param type is TangramLayoutType In TangramLayoutProtocol 47 | @param layoutClassName layoutClassName 48 | */ 49 | + (void)registLayoutType:(NSString *)type className:(NSString *)layoutClassName 50 | { 51 | if (type.length > 0 && layoutClassName.length > 0) { 52 | [[TangramDefaultLayoutFactory sharedInstance].layoutTypeMap setObject:[type copy] forKey:[layoutClassName copy]]; 53 | } 54 | } 55 | 56 | /** 57 | Generate a layout by a dictionary 58 | 59 | @param dict dict 60 | @return layout 61 | */ 62 | + (UIView *)layoutByDict:(NSDictionary *)dict 63 | { 64 | NSString *type = [dict tm_safeObjectForKey:@"type" class:[NSString class]]; 65 | if (type.length <= 0) { 66 | return nil; 67 | } 68 | NSString *layoutClassName = [[TangramDefaultLayoutFactory sharedInstance].layoutTypeMap tm_stringForKey:type]; 69 | UIView *layout = nil; 70 | if ([dict tm_boolForKey:@"canHorizontalScroll"] && ([type integerValue] <= 4 || [type integerValue] == 9)) { 71 | layout = [[TangramScrollFlowLayout alloc] init]; 72 | ((TangramScrollFlowLayout *)layout).numberOfColumns = (NSUInteger)[type integerValue]; 73 | } 74 | else { 75 | layout = (UIView *)[[NSClassFromString(layoutClassName) alloc]init]; 76 | } 77 | if (!layout) { 78 | NSLog(@"[TangramDefaultLayoutFactory] layoutByDict : cannot find layout by type , type :%@",type); 79 | return nil; 80 | } 81 | return [TangramDefaultLayoutFactory fillLayoutProperty:layout withDict:dict]; 82 | } 83 | 84 | /** 85 | Fill Layout Property 86 | 87 | @param layout layout 88 | @param dict dict 89 | @return layout filled property 90 | */ 91 | + (UIView *)fillLayoutProperty:(UIView *)layout withDict:(NSDictionary *)dict 92 | { 93 | layout.identifier = [dict tm_stringForKey:@"id"]; 94 | NSDictionary *styleDict = [dict tm_dictionaryForKey:@"style"]; 95 | NSString *backgroundColor = [styleDict tm_stringForKey:@"bgColor"]; 96 | if (backgroundColor.length <= 0 ) { 97 | backgroundColor = [styleDict tm_stringForKey:@"background-color"]; 98 | } 99 | if (backgroundColor.length > 0) { 100 | layout.backgroundColor = [UIColor vv_colorWithString:backgroundColor]; 101 | } 102 | NSString *bgImgURL = [styleDict tm_stringForKey:@"bgImgUrl"]; 103 | if (bgImgURL.length <= 0) { 104 | bgImgURL = [styleDict tm_stringForKey:@"background-image"]; 105 | } 106 | if (bgImgURL.length > 0 && [layout respondsToSelector:@selector(setBgImgURL:)]) { 107 | layout.bgImgURL = bgImgURL; 108 | } 109 | return [TangramLayoutParseHelper layoutConfigByOriginLayout:layout withDict:dict]; 110 | } 111 | 112 | + (NSMutableDictionary *)decodeTypeMap:(NSArray *)mapArray 113 | { 114 | NSMutableDictionary *mapDict = [[NSMutableDictionary alloc]init]; 115 | for (NSDictionary *dict in mapArray) { 116 | NSString *key = [dict tm_safeObjectForKey:@"type" class:[NSString class]]; 117 | NSString *value = [dict tm_safeObjectForKey:@"class" class:[NSString class]]; 118 | if (key.length > 0 && value.length > 0) { 119 | //NSAssert(![[mapDict allKeys] containsObject:key], @"model有重复注册!请检查注册的type!"); 120 | [mapDict setObject:value forKey:key]; 121 | } 122 | } 123 | return mapDict; 124 | } 125 | 126 | + (NSArray *)preprocessedDataArrayFromOriginalArray:(NSArray *)originalArray 127 | { 128 | NSMutableArray *layouts = [[NSMutableArray alloc]init]; 129 | for (NSUInteger i = 0 ; i < originalArray.count ; i ++) { 130 | NSDictionary *dict = [originalArray tm_dictionaryAtIndex:i]; 131 | NSString *type = [dict tm_stringForKey:@"type"]; 132 | if (type.length <= 0) { 133 | break; 134 | } 135 | NSDictionary *style = [dict tm_dictionaryForKey:@"style"]; 136 | NSString *forLabel = [style tm_stringForKey:@"forLabel"]; 137 | if (forLabel.length > 0 && i < originalArray.count - 1) 138 | { 139 | NSDictionary *nestLayoutDict = [originalArray tm_safeObjectAtIndex:i + 1]; 140 | if (![forLabel isEqualToString:[nestLayoutDict tm_stringForKey:@"id"]] || [nestLayoutDict tm_arrayForKey:@"items"].count <= 0) 141 | { 142 | continue; 143 | } 144 | } 145 | NSArray *originalItems = [dict tm_arrayForKey:@"items"]; 146 | //24 TabsLayout,做数据拆分 147 | if ([type isEqualToString:@"24"]) { 148 | //解析出来顶部的header 149 | NSString *originalIdentifier = [dict tm_stringForKey:@"id"]; 150 | NSString *layoutClassType = @"20"; 151 | NSString *headerIdentifier = [NSString stringWithFormat:@"%@-tabheader",originalIdentifier]; 152 | NSMutableDictionary *tabHeaderDictionary = [[NSMutableDictionary alloc]init]; 153 | [tabHeaderDictionary setObject:headerIdentifier forKey:@"id"]; 154 | if (style) { 155 | [tabHeaderDictionary setObject:[style copy] forKey:@"style"]; 156 | } 157 | [tabHeaderDictionary setObject:layoutClassType forKey:@"type"]; 158 | [tabHeaderDictionary setObject:[NSNumber numberWithBool:YES] forKey:@"canHorizontalScroll"]; 159 | [tabHeaderDictionary setObject:[[originalItems tm_dictionaryAtIndex:0] copy] forKey:@"items"]; 160 | [layouts tm_safeAddObject:tabHeaderDictionary]; 161 | //解析其他内容,删掉第一个组件 162 | NSMutableDictionary *layoutMutableDict = [dict mutableCopy]; 163 | NSMutableArray *contentItems = [originalItems mutableCopy]; 164 | if (contentItems.count > 1) { 165 | [contentItems removeObjectAtIndex:0]; 166 | [layoutMutableDict setObject:contentItems forKey:@"items"]; 167 | } 168 | [layouts tm_safeAddObject:[layoutMutableDict copy]]; 169 | } 170 | //11 MixLayout, 做数据拆分 171 | else if ([type isEqualToString:@"11"]) { 172 | NSMutableArray *mutableOriginalItems = [originalItems mutableCopy]; 173 | NSString *identifier = [dict tm_stringForKey:@"id"]; 174 | //获得了N个Layout实例 175 | NSArray *mixLayoutoriginalArray = [[dict tm_dictionaryForKey:@"style"] tm_arrayForKey:@"mixedLayouts"]; 176 | NSUInteger originalArrayCount = 1; 177 | if (![type isEqualToString:@"24"]) { 178 | originalArrayCount = mixLayoutoriginalArray.count; 179 | } 180 | 181 | for (NSUInteger i = 0 ; i< originalArrayCount ; i++) { 182 | NSDictionary *mixLayoutDict = [mixLayoutoriginalArray tm_dictionaryAtIndex:i]; 183 | NSMutableDictionary *mutableDict = [mixLayoutDict mutableCopy]; 184 | [mutableDict setObject:[NSString stringWithFormat:@"%@-%ld",identifier,(long)(i+1)] forKey:@"id"]; 185 | NSUInteger count = [dict tm_integerForKey:@"count"]; 186 | if (mutableOriginalItems.count > 0 ) { 187 | if (mutableOriginalItems.count < count) { 188 | count = mutableOriginalItems.count; 189 | } 190 | NSArray *itemModels = [mutableOriginalItems objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, count)]]; 191 | [mutableDict setObject:[itemModels copy] forKey:@"items"]; 192 | [mutableOriginalItems removeObjectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, count)]]; 193 | } 194 | [layouts tm_safeAddObject:mutableDict]; 195 | } 196 | } 197 | //如果找不到了对应的layout,那么外面套一个onecolumn 当做组件处理 198 | else if (![[self class]layoutClassNameByType:type]){ 199 | NSMutableDictionary *singleColumnDict = [[NSMutableDictionary alloc]init]; 200 | [singleColumnDict tm_safeSetObject:@"container-oneColumn" forKey:@"type"]; 201 | [singleColumnDict tm_safeSetObject:[dict tm_stringForKey:@"id"] forKey:@"id"]; 202 | [singleColumnDict tm_safeSetObject:@[dict] forKey:@"items"]; 203 | [layouts tm_safeAddObject:[singleColumnDict copy]]; 204 | } 205 | else{ 206 | [layouts tm_safeAddObject:dict]; 207 | } 208 | } 209 | return [layouts copy]; 210 | } 211 | 212 | + (NSString *)layoutClassNameByType:(NSString *)type 213 | { 214 | return [[TangramDefaultLayoutFactory sharedInstance].layoutTypeMap tm_stringForKey:type]; 215 | } 216 | @end 217 | -------------------------------------------------------------------------------- /Tangram/Helper/TangramDefaultDataSourceHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultDataSourceHelper.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelProtocol.h" 10 | #import "TangramLayoutProtocol.h" 11 | #import "TangramDefaultItemModel.h" 12 | #import "TangramBus.h" 13 | #import "TangramLayoutFactoryProtocol.h" 14 | #import "TangramItemModelFactoryProtocol.h" 15 | #import "TangramElementFactoryProtocol.h" 16 | 17 | // There are three main functions in TangramDataSourceHelper 18 | // 1. JSON to Layout 19 | // 2. JSON to model 20 | // 3. Model to element 21 | // Three functions executed by three generator. These three generator can be replaced. 22 | 23 | @interface TangramDefaultDataSourceHelper : NSObject 24 | /////////////////////////////////Core Class Method/////////////////////////////////////////// 25 | 26 | 27 | /** 28 | * Generate layouts array by a NSDictionary array 29 | * Besides, this method will generate itemModels for every layout 30 | * You can get itemModels from layout generated. 31 | * @param dictArray NSDictionary array 32 | * 33 | * @return Layout Array 34 | */ 35 | +(NSArray *> *)layoutsWithArray: (NSArray *)dictArray; 36 | /** 37 | * Generate layouts array by a NSDictionary array 38 | * Besides, this method will generate itemModels for every layout 39 | * You can get itemModels from layout generated. 40 | * If tangram is not nil, helper will bind layouts with this tangrambus. 41 | * @param dictArray NSDictionary array 42 | * 43 | * @return Layout Array 44 | */ 45 | +(NSArray *> *)layoutsWithArray: (NSArray *)dictArray 46 | tangramBus: (TangramBus *)tangramBus; 47 | 48 | /** 49 | * Generate element By Model 50 | * 51 | * @param model TangramModel 52 | * 53 | * @return Element instance 54 | */ 55 | +(UIView *)elementByModel:(NSObject *)model; 56 | /** 57 | * Generate element By Model. Here will bind Model,Layout,tangramBus to element 58 | * 59 | * @param model model 60 | * @param layout layout 61 | * @param tangramBus Tangram event bus 62 | * 63 | * @return Element instance 64 | */ 65 | +(UIView *)elementByModel:(NSObject *)model 66 | layout:(UIView *)layout 67 | tangramBus:(TangramBus *)tangramBus; 68 | /** 69 | * Refresh element By Model 70 | * 71 | * @param element UIView element can be reused 72 | * 73 | * @return refreshed element 74 | */ 75 | +(UIView *)refreshElement:(UIView *)element byModel:(NSObject *)model; 76 | /** 77 | * Refresh element By Model. Here will bind Model,Layout,tangramBus to element 78 | * 79 | * @param element element can be reused 80 | * @param model model 81 | * @param layout layout 82 | * @param tangramBus Tangram event bus 83 | * 84 | * @return refreshed element 85 | */ 86 | +(UIView *)refreshElement:(UIView *)element byModel:(NSObject *)model 87 | layout:(UIView *)layout 88 | tangramBus:(TangramBus *)tangramBus; 89 | 90 | /** 91 | * Generate a model by NSDictionary 92 | * 93 | * @param dict The Dictionary of a model 94 | * 95 | * @return TangramModel 96 | */ 97 | +(TangramDefaultItemModel *)modelWithDictionary : (NSDictionary *)dict; 98 | /** 99 | * Generate models by NSDictionary array 100 | * 101 | * @param dictArray dictArray 102 | * 103 | * @return NSArray 104 | */ 105 | +(NSArray *)modelsWithDictArray : (NSArray *)dictArray; 106 | 107 | /** 108 | * 根据Dict生成layout 109 | * 这里生成的layout内的itemModels是有值的,那model可以直接用layout.itemModels取 110 | * @param dict layout的dict 111 | * 112 | * @return layout 113 | */ 114 | +(UIView *)layoutWithDictionary: (NSDictionary *)dict tangramBus:(TangramBus *)tangramBus; 115 | 116 | 117 | /////////////////////////////////Core Class Method end///////////////////////////////////////////// 118 | 119 | 120 | /////////////////////////////////Method will be depracated///////////////////////////////////////// 121 | +(UIView *)layoutWithDictionary:(NSDictionary *)dict; 122 | 123 | +(NSMutableArray *)modelsWithLayoutDictionary : (NSDictionary *)dict; 124 | //////////////////////////////Method will be depracated end//////////////////////////////////////// 125 | 126 | 127 | //////////////////////////////////Other Helper method////////////////////////////////////////////// 128 | 129 | /** 130 | Return inner View in layouts 131 | 132 | @param layoutArray layout instance array 133 | @return Inner view count. 134 | */ 135 | + (NSUInteger)innerViewCountInLayouts:(NSArray *)layoutArray; 136 | 137 | /** 138 | Return inner models in layouts 139 | 140 | @param layoutArray layout instance array 141 | @return Inner model count 142 | */ 143 | + (NSUInteger)innerModelCountInLayouts:(NSArray *)layoutArray; 144 | 145 | //////////////////////////////////Other Helper method end////////////////////////////////////////// 146 | 147 | //////////////////////////////Regist Factory class Method Start//////////////////////////////////// 148 | 149 | /** 150 | Regist layout factory 151 | */ 152 | + (void)registLayoutFactoryClassName:(NSString *)layoutFactoryClassName; 153 | 154 | /** 155 | Regist itemModel factory 156 | */ 157 | + (void)registItemModelFactoryClassName:(NSString *)itemModelFactoryClassName; 158 | 159 | /** 160 | Regist element factory 161 | */ 162 | + (void)registElementFactoryClassName:(NSString *)elementFactoryClassName; 163 | 164 | ////////////////////////////////Regist Factory class Method end///////////////////////////////////// 165 | 166 | + (float)floatValueByRPObject:(id)rpObject; 167 | + (NSArray *)parseArrayWithRP:(NSArray *)originArray; 168 | + (TangramDefaultItemModel *)itemModelByJSONDictionary:(NSDictionary *)dict; 169 | + (BOOL)isTypeRegisted:(NSString *)type; 170 | 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /Tangram/Helper/TangramLayoutParseHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramLayoutParseHelper.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | 11 | @interface TangramLayoutParseHelper : NSObject 12 | 13 | //Config layout property 14 | + (UIView *)layoutConfigByOriginLayout:(UIView *)layout withDict:(NSDictionary *)dict; 15 | 16 | + (float)floatRPValueByObject:(id)marginObject; 17 | //+ (CGFloat)imageHeightByWidth:(CGFloat)width imgUrl:(NSString *)imgUrl; 18 | //+ (CGFloat)imageWidthByHeight:(CGFloat)height imgUrl:(NSString *)imgUrl; 19 | @end 20 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramDoubleColumnLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDoubleColumnLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFlowLayout.h" 9 | 10 | @interface TangramDoubleColumnLayout : TangramFlowLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramDoubleColumnLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDoubleColumnLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDoubleColumnLayout.h" 9 | 10 | @implementation TangramDoubleColumnLayout 11 | 12 | -(NSUInteger)numberOfColumns 13 | { 14 | return 2; 15 | } 16 | -(void)calculateLayout 17 | { 18 | [super calculateLayout]; 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramDragableLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDragableLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | #import "TangramFixLayout.h" 11 | 12 | @interface TangramDragableLayout : TangramFixLayout 13 | 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramDragableLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDragableLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDragableLayout.h" 9 | #import 10 | #import "TangramItemModelProtocol.h" 11 | #import "TangramView.h" 12 | @interface TangramDragableLayout () 13 | 14 | @property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer; 15 | 16 | @end 17 | @implementation TangramDragableLayout 18 | 19 | @synthesize panGestureRecognizer = _panGestureRecognizer; 20 | @synthesize itemModels = _itemModels; 21 | 22 | - (UIPanGestureRecognizer *)panGestureRecognizer 23 | { 24 | if (_panGestureRecognizer == nil) { 25 | _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPanned:)]; 26 | _panGestureRecognizer.minimumNumberOfTouches = 1; 27 | _panGestureRecognizer.maximumNumberOfTouches = 1; 28 | } 29 | return _panGestureRecognizer; 30 | } 31 | 32 | #pragma mark - overrided methods 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | [self addGestureRecognizer:self.panGestureRecognizer]; 37 | } 38 | return self; 39 | } 40 | 41 | //- (void)calculateLayout 42 | //{ 43 | // CGFloat width = 0.f,height = 0.f; 44 | // for (NSObject *model in self.itemModels) { 45 | // if ([model respondsToSelector:@selector(marginLeft)]) { 46 | // [model setItemFrame:CGRectMake(model.itemFrame.origin.x + model.marginLeft, model.itemFrame.origin.y, model.itemFrame.size.width, model.itemFrame.size.height)]; 47 | // } 48 | // if ([model respondsToSelector:@selector(marginTop)]) { 49 | // [model setItemFrame:CGRectMake(model.itemFrame.origin.x, model.itemFrame.origin.y + model.marginTop, model.itemFrame.size.width, model.itemFrame.size.height)]; 50 | // } 51 | // if (CGRectGetMaxX(model.itemFrame) > width) { 52 | // width = CGRectGetMaxX(model.itemFrame); 53 | // } 54 | // if (CGRectGetMaxY(model.itemFrame) > height) { 55 | // height = CGRectGetMaxY(model.itemFrame); 56 | // } 57 | // } 58 | // self.width = width; 59 | // self.height = height; 60 | // [self.superview bringSubviewToFront:self]; 61 | //} 62 | 63 | #pragma mark - event response 64 | - (void)didPanned:(UIPanGestureRecognizer *)gestureRecognizer 65 | { 66 | //CGFloat originY = 0.f; 67 | CGPoint originPoint = self.originPoint; 68 | CGPoint offset = [gestureRecognizer translationInView:self.superview]; 69 | originPoint.y += offset.y; 70 | self.originPoint = originPoint; 71 | gestureRecognizer.view.center = CGPointMake(gestureRecognizer.view.center.x + offset.x, gestureRecognizer.view.center.y + offset.y); 72 | [gestureRecognizer setTranslation:CGPointMake(0, 0) inView:self.superview]; 73 | if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { 74 | [self slideToEdge]; 75 | } 76 | } 77 | 78 | #pragma mark - animation 79 | - (void)slideToEdge 80 | { 81 | __weak typeof(self) wself = self; 82 | [UIView animateWithDuration:0.3 animations:^{ 83 | __strong typeof(wself) sself = wself; 84 | if (sself) { 85 | if (sself.superview.vv_centerX < sself.vv_centerX) { 86 | // 滑向右边贴边 87 | sself.vv_right = sself.superview.vv_width; 88 | } else { 89 | // 滑向左边贴边 90 | sself.vv_left = 0.f; 91 | } 92 | //sself.originPoint = sself.frame.origin; 93 | // CGFloat topEdge = 0.f; 94 | // CGFloat bottomEdge = 0.f; 95 | // if ([sself.superview isKindOfClass:[TangramView class]]) { 96 | // if ((((TangramView *)(sself.superview)).contentOffset.y + sself.superview.height - topEdge) < sself.vv_bottom) { 97 | // sself.vv_bottom = topEdge; 98 | // } 99 | // else if (bottomEdge > sself.top) { 100 | // sself.top = bottomEdge; 101 | // } 102 | // } 103 | 104 | } 105 | }]; 106 | 107 | } 108 | - (TangramLayoutType *)layoutType 109 | { 110 | return @"tangram_layout_dragable"; 111 | } 112 | - (NSString *)position 113 | { 114 | return @"float"; 115 | } 116 | //这个方法的调用时机 117 | //-(void)setAlignType:(DragableAlignType)alignType 118 | //{ 119 | // _alignType = alignType; 120 | // 121 | //} 122 | @end 123 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixBottomLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixBottomLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFixLayout.h" 9 | @interface TangramFixBottomLayout : TangramFixLayout 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixBottomLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixBottomLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFixBottomLayout.h" 9 | 10 | @implementation TangramFixBottomLayout 11 | 12 | - (NSString *)position 13 | { 14 | return @"bottom-fixed"; 15 | } 16 | - (FixAlignType)alignType 17 | { 18 | return BottomLeft; 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | typedef NS_ENUM(NSInteger, FixAlignType) 11 | { 12 | TopLeft = 0, 13 | TopRight, 14 | BottomLeft, 15 | BottomRight 16 | }; 17 | typedef NS_ENUM(NSInteger, FixShowType) 18 | { 19 | //Always Show 20 | FixLayoutShowAlways = 0, 21 | //Show before the previous layout enter 22 | FixLayoutShowOnEnter, 23 | //Show before the previous layout leave 24 | FixLayoutShowOnLeave 25 | }; 26 | 27 | typedef NS_ENUM(NSInteger, TangramFixAppearanceType) 28 | { 29 | //Default inline 30 | TangramFixAppearanceInline = 0, 31 | //Scroll Type 32 | TangramFixAppearanceScroll, 33 | }; 34 | @interface TangramFixLayout : UIView 35 | 36 | // Models Array , as protocol request. 37 | @property (nonatomic, strong) NSArray *itemModels; 38 | // Margin , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 39 | @property (nonatomic, strong) NSArray *margin; 40 | // Padding , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 41 | @property (nonatomic, strong) NSArray *padding; 42 | //@property (nonatomic, assign) CGFloat originalY; 43 | // offset in horizontal direction 44 | @property (nonatomic, assign) CGFloat offsetX; 45 | // offset in vertical direction 46 | @property (nonatomic, assign) CGFloat offsetY; 47 | // Align type, four corner of the scrollview 48 | @property (nonatomic, assign) FixAlignType alignType; 49 | 50 | @property (nonatomic, assign) FixShowType showType; 51 | 52 | @property (nonatomic, assign) CGPoint originPoint; 53 | // Vertical position to show. 54 | @property (nonatomic, assign) CGFloat showY; 55 | 56 | @property (nonatomic, weak) TangramBus *tangramBus; 57 | 58 | @property (nonatomic, assign) TangramFixAppearanceType appearanceType; 59 | 60 | // Gap in horizontal direction (Only for `TangramFixAppearanceScroll` alignType) 61 | @property (nonatomic, assign) CGFloat hGap; 62 | // Animation Duration for enter and leave time (Only in FixLayoutShowOnEnter and FixLayoutShowOnLeave AlignType) 63 | @property (nonatomic, assign) CGFloat animationDuration; 64 | // Whether enable alpha effect for enter and leave time (Only in FixLayoutShowOnEnter and FixLayoutShowOnLeave AlignType) 65 | @property (nonatomic, assign) BOOL enableAlphaEffect; 66 | // Whether retain scroll position (Only for TangramFixAppearanceScroll) 67 | @property (nonatomic, assign) BOOL retainScrollState; 68 | 69 | @property (nonatomic, assign) CGFloat zIndex; 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFixLayout.h" 9 | #import "TangramItemModelProtocol.h" 10 | #import "TangramView.h" 11 | #import "UIImageView+WebCache.h" 12 | #import "TangramContext.h" 13 | #import "TangramEvent.h" 14 | #import "TMUtils.h" 15 | #import 16 | 17 | @interface TangramFixLayout() 18 | 19 | @property (nonatomic, strong) NSString *layoutIdentifier; 20 | @property (nonatomic, strong) NSString *bgImgURL; 21 | @property (nonatomic, strong) UIImageView *bgImageView; 22 | @property (nonatomic, strong) UIScrollView *scrollView; 23 | @property (nonatomic, assign) CGFloat layoutHeight; 24 | @property (nonatomic, assign) BOOL animating; 25 | 26 | 27 | @end 28 | 29 | @implementation TangramFixLayout 30 | 31 | @synthesize itemModels = _itemModels; 32 | 33 | - (instancetype)init 34 | { 35 | if (self = [super init]) { 36 | self.retainScrollState = YES; 37 | } 38 | return self; 39 | } 40 | 41 | - (TangramLayoutType *)layoutType 42 | { 43 | return @"tangram_layout_fix"; 44 | } 45 | - (NSString *)identifier 46 | { 47 | return self.layoutIdentifier; 48 | } 49 | -(void)setIdentifier:(NSString *)identifier 50 | { 51 | self.layoutIdentifier = identifier; 52 | } 53 | -(UIImageView *)bgImageView 54 | { 55 | if (_bgImageView == nil) { 56 | _bgImageView = [[UIImageView alloc]init]; 57 | [self addSubview:_bgImageView]; 58 | } 59 | return _bgImageView; 60 | } 61 | - (UIScrollView *)scrollView 62 | { 63 | if (nil == _scrollView) { 64 | _scrollView = [[UIScrollView alloc] init]; 65 | _scrollView.scrollEnabled = YES; 66 | _scrollView.scrollsToTop = NO; 67 | _scrollView.showsHorizontalScrollIndicator = NO; 68 | _scrollView.showsVerticalScrollIndicator = NO; 69 | [self addSubview:_scrollView]; 70 | } 71 | return _scrollView; 72 | } 73 | 74 | - (void)setTangramBus:(TangramBus *)tangramBus 75 | { 76 | if (tangramBus != _tangramBus) { 77 | _tangramBus = tangramBus; 78 | [self registEvent]; 79 | } 80 | else{ 81 | _tangramBus = tangramBus; 82 | } 83 | 84 | } 85 | - (void)calculateLayout 86 | { 87 | CGFloat height = 0.f; 88 | //Fix暂时只会取第一个 89 | if (self.appearanceType == TangramFixAppearanceScroll) { 90 | NSObject *lastModel = nil; 91 | for (int i = 0; i < self.itemModels.count; i++) { 92 | NSObject *model = [self.itemModels tm_safeObjectAtIndex:i]; 93 | if ([model respondsToSelector:@selector(marginLeft)]) { 94 | if (i == 0) { 95 | [model setItemFrame:CGRectMake([self.padding tm_floatAtIndex:3] + model.marginLeft, model.itemFrame.origin.y ,model.itemFrame.size.width, model.itemFrame.size.height)]; 96 | } 97 | else{ 98 | [model setItemFrame:CGRectMake(lastModel.itemFrame.origin.x + lastModel.itemFrame.size.width + model.marginLeft + self.hGap, model.itemFrame.origin.y,model.itemFrame.size.width, model.itemFrame.size.height)]; 99 | } 100 | } 101 | if ([model respondsToSelector:@selector(marginTop)]) { 102 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x, model.itemFrame.origin.y + model.marginTop + [self.padding tm_floatAtIndex:0], model.itemFrame.size.width, model.itemFrame.size.height)]; 103 | } 104 | if ([lastModel respondsToSelector:@selector(marginRight)]) { 105 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x + lastModel.marginRight, model.itemFrame.origin.y , model.itemFrame.size.width, model.itemFrame.size.height)]; 106 | } 107 | if (height < model.itemFrame.size.height) { 108 | height = model.itemFrame.size.height; 109 | } 110 | lastModel = model; 111 | } 112 | self.scrollView.frame = CGRectMake(0, 0, self.superview.vv_width, height); 113 | if ([lastModel respondsToSelector:@selector(marginRight)]) { 114 | self.scrollView.contentSize = CGSizeMake(lastModel.itemFrame.origin.x + lastModel.itemFrame.size.width + lastModel.marginRight + [self.padding tm_floatAtIndex:1], height); 115 | } 116 | else{ 117 | self.scrollView.contentSize = CGSizeMake(lastModel.itemFrame.origin.x + lastModel.itemFrame.size.width + [self.padding tm_floatAtIndex:1], height); 118 | } 119 | 120 | self.vv_width = self.superview.vv_width; 121 | } 122 | else{ 123 | //保持老逻辑不动 124 | NSObject *model = [self.itemModels tm_safeObjectAtIndex:0]; 125 | if ([model respondsToSelector:@selector(marginLeft)]) { 126 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x + model.marginLeft, model.itemFrame.origin.y,CGRectGetWidth(self.frame), model.itemFrame.size.height)]; 127 | } 128 | if ([model respondsToSelector:@selector(marginTop)]) { 129 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x, model.itemFrame.origin.y + model.marginTop, CGRectGetWidth(self.frame), model.itemFrame.size.height)]; 130 | } 131 | if (CGRectGetMaxY(model.itemFrame) > height) { 132 | height = CGRectGetMaxY(model.itemFrame); 133 | } 134 | self.vv_width = model.itemFrame.size.width; 135 | } 136 | self.vv_height = height + [self.padding tm_floatAtIndex:2]; 137 | self.layoutHeight = self.vv_height; 138 | if (self.bgImgURL && self.bgImgURL.length > 0) { 139 | self.bgImageView.frame = CGRectMake(0, 0, self.vv_width, self.vv_height); 140 | [self.bgImageView sd_setImageWithURL:[NSURL URLWithString:self.bgImgURL]]; 141 | } 142 | [self.superview bringSubviewToFront:self]; 143 | } 144 | 145 | - (void)addSubview:(UIView *)view 146 | { 147 | if ([view respondsToSelector:@selector(setReuseIdentifier:)] && view != self.scrollView) { 148 | view.reuseIdentifier = @""; 149 | } 150 | if (view && view.reuseIdentifier && self.appearanceType == TangramFixAppearanceScroll && view != self.scrollView) { 151 | [self.scrollView addSubview:view]; 152 | } 153 | else { 154 | [super addSubview:view]; 155 | } 156 | } 157 | 158 | -(void)setFrame:(CGRect)frame 159 | { 160 | [super setFrame:frame]; 161 | } 162 | -(NSString *)position 163 | { 164 | return @"fixed"; 165 | } 166 | - (NSArray *)itemModels 167 | { 168 | return _itemModels; 169 | } 170 | - (NSArray *)margin 171 | { 172 | if (_margin && 4 == _margin.count) { 173 | return _margin; 174 | } 175 | return @[@0, @0, @0, @0]; 176 | } 177 | - (CGFloat)marginTop 178 | { 179 | return [[self.margin tm_safeObjectAtIndex:0] floatValue]; 180 | } 181 | 182 | - (CGFloat)marginRight 183 | { 184 | return [[self.margin tm_safeObjectAtIndex:1] floatValue]; 185 | } 186 | 187 | - (CGFloat)marginBottom 188 | { 189 | return [[self.margin tm_safeObjectAtIndex:2] floatValue]; 190 | } 191 | 192 | - (CGFloat)marginLeft 193 | { 194 | return [[self.margin tm_safeObjectAtIndex:3] floatValue]; 195 | } 196 | - (void)heightChangedWithElement:(UIView *)element model:(NSObject *)model 197 | { 198 | // [self calculateLayout]; 199 | // if ([self.superview isKindOfClass:[TangramView class]]) { 200 | // [((TangramView *)self.superview) heightChanged]; 201 | // } 202 | } 203 | - (void)setBgImgURL:(NSString *)imgURL 204 | { 205 | _bgImgURL = imgURL; 206 | } 207 | //注册事件 208 | - (void)registEvent 209 | { 210 | [self.tangramBus registerAction:@"showLayout:" ofExecuter:self onEventTopic:@"TangramFixLayoutShouldShow"]; 211 | [self.tangramBus registerAction:@"hideLayout:" ofExecuter:self onEventTopic:@"TangramFixLayoutShouldHide"]; 212 | } 213 | 214 | - (void)showLayout:(TangramContext *)context 215 | { 216 | //必须是这个layout 才做对应的事情 217 | NSObject *layout = [context.event.params tm_safeObjectForKey:@"layout"]; 218 | if (layout != self) { 219 | return; 220 | } 221 | self.hidden = NO; 222 | if (!self.retainScrollState && self.appearanceType == TangramFixAppearanceScroll) { 223 | [self.scrollView setContentOffset:CGPointMake(0, 0)]; 224 | } 225 | if (self.animating || self.animationDuration == 0.f) { 226 | return; 227 | } 228 | self.vv_height = 0.f; 229 | self.animating = YES; 230 | for (UIView *view in self.subviews) { 231 | view.vv_top -= self.layoutHeight; 232 | } 233 | [UIView animateWithDuration:self.animationDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 234 | self.vv_height = self.layoutHeight; 235 | for (UIView *view in self.subviews) { 236 | view.vv_top += self.layoutHeight; 237 | } 238 | } completion:^(BOOL finished) { 239 | self.animating = NO; 240 | }]; 241 | } 242 | 243 | - (void)hideLayout:(TangramContext *)context 244 | { 245 | //必须是这个layout 才做对应的事情 246 | NSObject *layout = [context.event.params tm_safeObjectForKey:@"layout"]; 247 | if (layout != self) { 248 | return; 249 | } 250 | if (self.animationDuration == 0.f) { 251 | self.hidden = YES; 252 | return; 253 | } 254 | if (self.animating) { 255 | return; 256 | } 257 | self.animating = YES; 258 | [UIView animateWithDuration:0.5 animations:^{ 259 | self.vv_height = 0.f; 260 | if (self.enableAlphaEffect) { 261 | self.alpha = 0.f; 262 | } 263 | for (UIView *view in self.subviews) { 264 | view.vv_top -= self.layoutHeight; 265 | } 266 | } completion:^(BOOL finished) { 267 | for (UIView *view in self.subviews) { 268 | view.vv_top += self.layoutHeight; 269 | } 270 | self.vv_height = self.layoutHeight; 271 | self.hidden = YES; 272 | if (self.enableAlphaEffect) { 273 | self.alpha = 1.f; 274 | } 275 | self.animating = NO; 276 | }]; 277 | } 278 | 279 | 280 | 281 | @end 282 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixTopLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixTopLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFixLayout.h" 9 | 10 | @interface TangramFixTopLayout : TangramFixLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFixTopLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFixTopLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFixTopLayout.h" 9 | 10 | @implementation TangramFixTopLayout 11 | 12 | - (NSString *)position 13 | { 14 | return @"top-fixed"; 15 | } 16 | - (FixAlignType)alignType 17 | { 18 | return TopLeft; 19 | } 20 | @end 21 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramFlowLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | #import "TangramView.h" 11 | typedef NS_ENUM(NSUInteger,TangramFlowLayoutBgImageScaleType) 12 | { 13 | TangramFlowLayoutBgImageScaleTypeFitXY = 0, 14 | TangramFlowLayoutBgImageScaleTypeFitStart 15 | }; 16 | @interface TangramFlowLayout : UIView 17 | 18 | // Models Array , as protocol request. 19 | @property (nonatomic, strong) NSArray *itemModels; 20 | // Number of columns.The default value is 1. 21 | @property (nonatomic, assign) NSUInteger numberOfColumns; 22 | // Percentage number of every cols 23 | @property (nonatomic, strong) NSArray *cols; 24 | // Aspect ratio of every row 25 | @property (nonatomic, strong) NSString *aspectRatio; 26 | // Margin , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 27 | @property (nonatomic, strong) NSArray *margin; 28 | // Padding , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 29 | @property (nonatomic, strong) NSArray *padding; 30 | // Gap in horizontal direction 31 | @property (nonatomic, assign) CGFloat hGap; 32 | // Gap in vertical direction 33 | @property (nonatomic, assign) CGFloat vGap; 34 | // Only for first row,if `autoFill` is true,flowlayout will change the number of columns to fill a row, 35 | // when the count of `itemModels` less than the number of columns 36 | @property (nonatomic, assign) BOOL autoFill; 37 | //id 38 | @property (nonatomic, strong) NSString *layoutLoadAPI; 39 | // Background Image View 40 | @property (nonatomic, strong) UIImageView *bgImageView; 41 | // Background ImageUrl 42 | @property (nonatomic, strong) NSString *bgImgURL; 43 | // TangramBus 44 | @property (nonatomic, weak) TangramBus *tangramBus; 45 | 46 | @property (nonatomic, weak) TangramView *tangramView; 47 | // Background ImageView scale. 48 | @property (nonatomic, assign) TangramFlowLayoutBgImageScaleType bgScaleType; 49 | // Params for LoadAPI 50 | @property (nonatomic, strong) NSDictionary *loadParams; 51 | 52 | @property (nonatomic, assign) TangramLayoutLoadType loadType; 53 | 54 | @property (nonatomic, strong) NSDictionary *subLayoutDict; 55 | 56 | @property (nonatomic, strong) NSArray *subLayoutIdentifiers; 57 | 58 | @property (nonatomic, assign) BOOL enableMarginDeduplication; 59 | 60 | @property (nonatomic, assign) CGFloat zIndex; 61 | 62 | @property (nonatomic, assign) BOOL disableUserInteraction; 63 | 64 | @property (nonatomic, assign) BOOL enableInnerZIndexLayout; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramPageScrollLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramPageScrollLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | #import "TangramBus.h" 11 | @interface TangramPageScrollLayout : UIView 12 | typedef NS_ENUM(NSUInteger,IndicatorGravityType) 13 | { 14 | IndicatorGravityCenter = 0, 15 | IndicatorGravityLeft, 16 | IndicatorGravityRight 17 | }; 18 | typedef NS_ENUM(NSUInteger,IndicatorPositionType) 19 | { 20 | 21 | IndicatorPositionOutside = 0, 22 | IndicatorPositionInside , 23 | }; 24 | 25 | typedef NS_ENUM(NSUInteger,IndicatorStyleType) 26 | { 27 | IndicatorStyleDot = 0, 28 | IndicatorStyleStripe , 29 | }; 30 | 31 | @property (nonatomic, strong) NSString *indicatorImg1; 32 | @property (nonatomic, strong) NSString *indicatorImg2; 33 | 34 | 35 | @property (nonatomic, assign) CGFloat indicatorGap; 36 | // Models Array , as protocol request. 37 | @property (nonatomic, strong) NSArray *itemModels; 38 | // Margin , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 39 | @property (nonatomic, strong) NSArray *margin; 40 | // Padding , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 41 | @property (nonatomic, strong) NSArray *padding; 42 | // Aspect ratio of the whole layout 43 | @property (nonatomic, strong) NSString *aspectRatio; 44 | // Indicator position in horizontal 45 | @property (nonatomic, assign) IndicatorGravityType indicatorGravity; 46 | // Indicator position in vertical 47 | @property (nonatomic, assign) IndicatorPositionType indicatorPosition; 48 | // Auto scroll time. 49 | @property (nonatomic, assign) NSTimeInterval autoScrollTime; 50 | // Has more Jump Action 51 | @property (nonatomic, strong) NSString *hasMoreAction; 52 | // TangramBus 53 | @property (nonatomic, weak) TangramBus *tangramBus; 54 | // Gap in horizontal direction 55 | @property (nonatomic, assign) CGFloat hGap; 56 | // Whether infinite loop. Only 57 | @property (nonatomic, assign) BOOL infiniteLoop; 58 | // Load More ImageUrl 59 | @property (nonatomic, strong) NSString *loadMoreImgUrl; 60 | // Indicator style type 61 | @property (nonatomic, assign) IndicatorStyleType indicatorStyleType; 62 | // Page Height for every element. 63 | @property (nonatomic, assign) CGFloat pageHeight; 64 | // Page Height for every element. If `pageWidth` is 0, This pageScroll will scroll by page(pagingEnabled = YES). 65 | @property (nonatomic, assign) CGFloat pageWidth; 66 | // Whether need Indicator 67 | @property (nonatomic, assign) BOOL hasIndicator; 68 | // Whether indicator need auto hide. 69 | @property (nonatomic, assign) BOOL indicatorAutoHide; 70 | 71 | @property (nonatomic, assign) CGFloat indicatorRadius; 72 | @property (nonatomic, assign) CGFloat indicatorHeight; 73 | @property (nonatomic, assign) CGFloat indicatorWidth; 74 | @property (nonatomic, strong) NSString *indicatorColor; 75 | @property (nonatomic, strong) NSString *defaultIndicatorColor; 76 | @property (nonatomic, assign) CGFloat indicatorMargin; 77 | @property (nonatomic, assign) CGFloat scrollMarginLeft; 78 | @property (nonatomic, assign) CGFloat scrollMarginRight; 79 | // Margin for every page, the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 80 | @property (nonatomic, strong) NSArray *pageMargin; 81 | @property (nonatomic, strong) NSString *layoutLoadAPI; 82 | 83 | @property (nonatomic, strong) NSString *bgImgURL; 84 | 85 | @property (nonatomic, assign) CGFloat zIndex; 86 | 87 | //启用margin去重,留个坑,滚动布局暂时不做处理 88 | //@property (nonatomic, assign) BOOL enableMarginDeduplication; 89 | 90 | @end 91 | 92 | @protocol TangramPageScrollLayoutDelegate 93 | //当滑动到某一页的时候会调用 94 | -(void)layout:(TangramPageScrollLayout *)layout atIndex:(NSUInteger)index; 95 | 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramQuintetColumnLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramQuintetColumnLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFlowLayout.h" 9 | 10 | @interface TangramQuintetColumnLayout : TangramFlowLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramQuintetColumnLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramQuintetColumnLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramQuintetColumnLayout.h" 9 | 10 | @implementation TangramQuintetColumnLayout 11 | 12 | -(NSUInteger)numberOfColumns 13 | { 14 | return 5; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramScrollFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramScrollFlowLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramFlowLayout.h" 10 | #import "TangramScrollLayoutProtocol.h" 11 | 12 | 13 | @protocol TangramListPagingFlowLayoutDelegate 14 | 15 | - (CGFloat)pagingListLayoutAbsY; 16 | 17 | - (void)pagingListLayoutDidPaging:(NSInteger)pagingIndex; 18 | 19 | - (void)pagingListLayoutMovingToNext; 20 | 21 | - (void)pagingListLayoutMovingToPre; 22 | 23 | @end 24 | 25 | @interface TangramScrollFlowLayout : TangramFlowLayout 26 | 27 | @property (nonatomic, weak) id pagingDelegate; 28 | 29 | @property (nonatomic, strong) UIView *loadingView; 30 | @property (nonatomic, assign) CGFloat offsetToLoadingView; 31 | @property (nonatomic, assign) BOOL disableScroll; 32 | 33 | @property (nonatomic, assign) NSInteger pagingIndex; 34 | @property (nonatomic, assign) NSInteger pagingLength; 35 | 36 | @property (nonatomic, strong) NSArray *bgColors; 37 | 38 | 39 | -(void)removeLoadingView; 40 | @end 41 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramScrollWaterFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramScrollWaterFlowLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramWaterFlowLayout.h" 10 | #import "TangramScrollLayoutProtocol.h" 11 | 12 | @interface TangramScrollWaterFlowLayout : TangramWaterFlowLayout 13 | 14 | @property (nonatomic, strong) UIView *loadingView; 15 | @property (nonatomic, assign) CGFloat offsetToLoadingView; 16 | @property (nonatomic, assign) BOOL disableScroll; 17 | 18 | @property (nonatomic, assign) NSInteger pagingIndex; 19 | @property (nonatomic, assign) NSInteger pagingLength; 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramSingleAndDoubleLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSingleAndDoubleLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFlowLayout.h" 9 | 10 | @interface TangramSingleAndDoubleLayout : TangramFlowLayout 11 | 12 | // The ratio of the upper and lower lines. 13 | // Only read first two element. Two elements added up should be 100 14 | // The type of element in `rows` can be NSString or NSNumber 15 | @property (nonatomic, strong) NSArray *rows; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramSingleColumnLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSingleColumnLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | #import "TangramFlowLayout.h" 8 | 9 | @interface TangramSingleColumnLayout : TangramFlowLayout 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramSingleColumnLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSingleColumnLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramSingleColumnLayout.h" 9 | 10 | @implementation TangramSingleColumnLayout 11 | 12 | -(NSUInteger)numberOfColumns 13 | { 14 | return 1; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramStickyBottomLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramStickyBottomLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramStickyLayout.h" 10 | 11 | 12 | @interface TangramStickyBottomLayout : TangramStickyLayout 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramStickyBottomLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramStickyBottomLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramStickyBottomLayout.h" 9 | 10 | @implementation TangramStickyBottomLayout 11 | 12 | -(BOOL)stickyBottom 13 | { 14 | return YES; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramStickyLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramStickyLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #define TangramStickyEnterEvent @"TangramStickyEnterEvent" 9 | 10 | #import "TangramItemModelProtocol.h" 11 | #import "TangramLayoutProtocol.h" 12 | 13 | @interface TangramStickyLayout : UIView 14 | //默认NO吸顶 YES吸底 15 | @property (nonatomic, assign) BOOL stickyBottom; 16 | //暂未启用 -- 吸顶/吸底的终结Layoutid 17 | //@property (nonatomic, assign) NSString *endLayoutId; 18 | //记录原始的高度,为TangramView内部判断而准备 19 | @property (nonatomic, assign) CGFloat originalY; 20 | //是否进入"吸"的状态,为TangramView内部判断而准备 21 | @property (nonatomic, assign) BOOL enterFloatStatus; 22 | // Margin top, right, bottom, left的顺序, 接收NSNumber / NSString 23 | @property (nonatomic, strong) NSArray *margin; 24 | // Models数组,Protocol要求的 25 | @property (nonatomic, strong) NSArray *itemModels; 26 | //针对吸顶但是需要离顶端/底端一定距离的情况,增加一个可设定的值 27 | @property (nonatomic, assign) CGFloat extraOffset; 28 | @property (nonatomic, weak) TangramBus *tangramBus; 29 | 30 | @property (nonatomic, assign) CGFloat zIndex; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramStickyLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramStickyLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramStickyLayout.h" 9 | #import "TangramView.h" 10 | #import "UIImageView+WebCache.h" 11 | #import 12 | #import "TMUtils.h" 13 | #import "TangramEvent.h" 14 | 15 | @interface TangramStickyLayout () 16 | 17 | @property (nonatomic, strong) NSString *layoutIdentifier; 18 | @property (nonatomic, strong) NSString *bgImgURL; 19 | @property (nonatomic, strong) UIImageView *bgImageView; 20 | @end 21 | @implementation TangramStickyLayout 22 | 23 | @synthesize itemModels = _itemModels; 24 | - (TangramLayoutType *)layoutType 25 | { 26 | return @"tangram_layout_sticky"; 27 | } 28 | -(UIImageView *)bgImageView 29 | { 30 | if (_bgImageView == nil) { 31 | _bgImageView = [[UIImageView alloc]init]; 32 | [self addSubview:_bgImageView]; 33 | } 34 | return _bgImageView; 35 | } 36 | - (void)calculateLayout 37 | { 38 | CGFloat height = 0.f; 39 | //吸顶只会取第一个 40 | NSObject *model = [self.itemModels tm_safeObjectAtIndex:0]; 41 | if ([model respondsToSelector:@selector(marginLeft)]) { 42 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x + model.marginLeft, model.itemFrame.origin.y,CGRectGetWidth(self.frame), model.itemFrame.size.height)]; 43 | } 44 | if ([model respondsToSelector:@selector(marginTop)]) { 45 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x, model.itemFrame.origin.y + model.marginTop, CGRectGetWidth(self.frame), model.itemFrame.size.height)]; 46 | } 47 | if (CGRectGetMaxY(model.itemFrame) > height) { 48 | height = CGRectGetMaxY(model.itemFrame); 49 | } 50 | //self.width = width; 51 | self.vv_height = height; 52 | if (self.bgImgURL && self.bgImgURL.length > 0) { 53 | self.bgImageView.frame = CGRectMake(0, 0, self.vv_width, self.vv_height); 54 | [self.bgImageView sd_setImageWithURL:[NSURL URLWithString:self.bgImgURL]]; 55 | } 56 | [self.superview bringSubviewToFront:self]; 57 | //避免吸顶透出更多的东西 58 | self.clipsToBounds = YES; 59 | } 60 | 61 | -(void)setFrame:(CGRect)frame 62 | { 63 | [super setFrame:frame]; 64 | if (self.enterFloatStatus == NO) { 65 | self.originalY = frame.origin.y; 66 | } 67 | } 68 | 69 | -(NSString *)position 70 | { 71 | return @"sticky"; 72 | } 73 | - (NSArray *)margin 74 | { 75 | if (_margin && 4 == _margin.count) { 76 | return _margin; 77 | } 78 | return @[@0, @0, @0, @0]; 79 | } 80 | - (void)setItemModels:(NSArray *)itemModels 81 | { 82 | _itemModels = itemModels; 83 | } 84 | 85 | - (NSArray *)itemModels 86 | { 87 | return _itemModels; 88 | } 89 | - (CGFloat)marginTop 90 | { 91 | return [[self.margin tm_safeObjectAtIndex:0] floatValue]; 92 | } 93 | 94 | - (CGFloat)marginRight 95 | { 96 | return [[self.margin tm_safeObjectAtIndex:1] floatValue]; 97 | } 98 | 99 | - (CGFloat)marginBottom 100 | { 101 | return [[self.margin tm_safeObjectAtIndex:2] floatValue]; 102 | } 103 | 104 | - (CGFloat)marginLeft 105 | { 106 | return [[self.margin tm_safeObjectAtIndex:3] floatValue]; 107 | 108 | } 109 | 110 | - (void)heightChangedWithElement:(UIView *)element model:(NSObject *)model 111 | { 112 | [self calculateLayout]; 113 | if ([self.superview isKindOfClass:[TangramView class]]) { 114 | [((TangramView *)self.superview) reloadData]; 115 | } 116 | } 117 | -(NSString *)identifier 118 | { 119 | return self.layoutIdentifier; 120 | } 121 | - (void)setIdentifier:(NSString *)identifier 122 | { 123 | self.layoutIdentifier = identifier; 124 | } 125 | 126 | - (void)setEnterFloatStatus:(BOOL)enterFloatStatus 127 | { 128 | if (_enterFloatStatus == NO && enterFloatStatus == YES) { 129 | TangramEvent *captureImageEvent = [[TangramEvent alloc]initWithTopic:TangramStickyEnterEvent withTangramView:((TangramView *)self.superview) posterIdentifier:nil andPoster:self]; 130 | [self.tangramBus postEvent:captureImageEvent]; 131 | } 132 | _enterFloatStatus = enterFloatStatus; 133 | } 134 | @end 135 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramTetradColumnLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramTetradColumnLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFlowLayout.h" 9 | 10 | @interface TangramTetradColumnLayout : TangramFlowLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramTetradColumnLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramTetradColumnLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramTetradColumnLayout.h" 9 | 10 | @implementation TangramTetradColumnLayout 11 | 12 | -(NSUInteger)numberOfColumns 13 | { 14 | return 4; 15 | } 16 | @end 17 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramTribleColumnLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramTribleColumnLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramFlowLayout.h" 9 | 10 | @interface TangramTribleColumnLayout : TangramFlowLayout 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramTribleColumnLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramTribleColumnLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramTribleColumnLayout.h" 9 | 10 | @implementation TangramTribleColumnLayout 11 | 12 | -(NSUInteger)numberOfColumns 13 | { 14 | return 3; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramWaterFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramWaterFlowLayout.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | @interface TangramWaterFlowLayout : UIView 11 | 12 | // Models Array , as protocol request. 13 | @property (nonatomic, strong) NSArray *itemModels; 14 | // Number of columns. 15 | @property (nonatomic, assign) NSUInteger numberOfColumns; 16 | // Percentage number of every cols 17 | @property (nonatomic, strong) NSArray *cols; 18 | // Aspect ratio of every row 19 | @property (nonatomic, strong) NSString *aspectRatio; 20 | // Margin , the sequence of top, right, bottom, left, the class type in array can be NSNumber or NSString 21 | @property (nonatomic, strong) NSArray *margin; 22 | // padding top, right, bottom, left的顺序, 接收NSString 23 | @property (nonatomic, strong) NSArray *padding; 24 | // Gap in horizontal direction 25 | @property (nonatomic, assign) CGFloat hGap; 26 | // Gap in vertical direction 27 | @property (nonatomic, assign) CGFloat vGap; 28 | // Element width 29 | @property (nonatomic, assign, readonly) CGFloat cellWidth; 30 | 31 | @property (nonatomic, weak) TangramBus *tangramBus; 32 | 33 | @property (nonatomic, assign) TangramLayoutLoadType loadType; 34 | 35 | @property (nonatomic, strong) NSString *layoutLoadAPI; 36 | 37 | @property (nonatomic, assign) CGFloat zIndex; 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Tangram/Layouts/TangramWaterFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramWaterFlowLayout.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramWaterFlowLayout.h" 9 | #import "TangramItemModelProtocol.h" 10 | #import "TangramView.h" 11 | #import "UIImageView+WebCache.h" 12 | #import "TMUtils.h" 13 | #import 14 | 15 | @interface TangramWaterFlowLayout() 16 | @property (nonatomic, assign) CGRect minRect; 17 | @property (nonatomic, assign) CGRect maxRect; 18 | @property (nonatomic, strong) NSMutableDictionary *bottomRects; 19 | @property (nonatomic, strong) NSString *layoutIdentifier; 20 | // 收到reload请求的次数 21 | @property (atomic, assign ) int numberOfReloadRequests; 22 | // 首次收到reload请求的时间点,毫秒级 23 | @property (atomic, assign ) NSTimeInterval firstReloadRequestTS; 24 | @property (nonatomic, strong) NSString *bgImgURL; 25 | @property (nonatomic, strong) UIImageView *bgImageView; 26 | 27 | 28 | 29 | 30 | @end 31 | 32 | @implementation TangramWaterFlowLayout 33 | 34 | @synthesize itemModels = _itemModels; 35 | @synthesize cellWidth = _cellWidth; 36 | 37 | 38 | - (NSString *)identifier 39 | { 40 | return self.layoutIdentifier; 41 | } 42 | -(void)setIdentifier:(NSString *)identifier 43 | { 44 | self.layoutIdentifier = identifier; 45 | } 46 | //基础部分 47 | - (TangramLayoutType *)layoutType 48 | { 49 | return @"tangram_layout_waterFlow"; 50 | } 51 | 52 | - (void)setItemModels:(NSArray *)itemModels 53 | { 54 | NSMutableArray *toBeAddedItemModels = [[NSMutableArray alloc]init]; 55 | NSMutableArray *mutableItemModels = [itemModels mutableCopy]; 56 | for (NSObject *model in mutableItemModels) { 57 | if ([model respondsToSelector:@selector(position)] && [[model position] isKindOfClass:[NSString class]] &&[model position].length > 0) { 58 | [toBeAddedItemModels tm_safeAddObject:model]; 59 | } 60 | } 61 | for (NSObject *model in toBeAddedItemModels) { 62 | [mutableItemModels removeObject:model]; 63 | if ([[model position] integerValue] > mutableItemModels.count) { 64 | [mutableItemModels tm_safeInsertObject:model atIndex:mutableItemModels.count]; 65 | } 66 | else{ 67 | [mutableItemModels tm_safeInsertObject:model atIndex:[[model position] integerValue]]; 68 | } 69 | } 70 | _itemModels = [mutableItemModels copy]; 71 | } 72 | - (NSString *)position 73 | { 74 | return @""; 75 | } 76 | - (NSArray *)itemModels 77 | { 78 | return _itemModels; 79 | } 80 | - (NSArray *)margin 81 | { 82 | if (_margin && 4 == _margin.count) { 83 | return _margin; 84 | } 85 | return @[@0, @0, @0, @0]; 86 | } 87 | - (CGFloat)marginTop 88 | { 89 | return [[self.margin tm_safeObjectAtIndex:0] floatValue]; 90 | } 91 | 92 | - (CGFloat)marginRight 93 | { 94 | return [[self.margin tm_safeObjectAtIndex:1] floatValue]; 95 | } 96 | 97 | - (CGFloat)marginBottom 98 | { 99 | return [[self.margin tm_safeObjectAtIndex:2] floatValue]; 100 | } 101 | 102 | - (CGFloat)marginLeft 103 | { 104 | return [[self.margin tm_safeObjectAtIndex:3] floatValue]; 105 | } 106 | 107 | - (NSMutableDictionary *)bottomRects 108 | { 109 | if (nil == _bottomRects) { 110 | _bottomRects = [[NSMutableDictionary alloc] init]; 111 | } 112 | return _bottomRects; 113 | } 114 | 115 | - (CGFloat)cellWidth 116 | { 117 | if (0 == _cellWidth) { 118 | _cellWidth = ceilf((self.vv_width - [self.padding tm_floatAtIndex:1] - [self.padding tm_floatAtIndex:3] - self.hGap * (self.numberOfColumns - 1)) / self.numberOfColumns); 119 | } 120 | return _cellWidth; 121 | } 122 | -(NSUInteger)numberOfColumns 123 | { 124 | if (_numberOfColumns <= 0) { 125 | _numberOfColumns = 2; 126 | } 127 | return _numberOfColumns; 128 | } 129 | -(UIImageView *)bgImageView 130 | { 131 | if (_bgImageView == nil) { 132 | _bgImageView = [[UIImageView alloc]init]; 133 | } 134 | return _bgImageView; 135 | } 136 | //核心·CalculateLayout 137 | -(void)calculateLayout 138 | { 139 | self.minRect = CGRectZero; 140 | self.maxRect = CGRectZero; 141 | [self.bottomRects removeAllObjects]; 142 | CGFloat cellX = 0.f; 143 | CGFloat cellY = 0.f; 144 | for (NSUInteger i = 0; i < self.itemModels.count; i++) { 145 | NSObject *itemModel = [self.itemModels tm_safeObjectAtIndex:i]; 146 | cellX = CGRectGetMinX(self.minRect); 147 | //第一行不会受vGap的影响 148 | if (i / self.numberOfColumns == 0) { 149 | cellY = CGRectGetMaxY(self.minRect) + [itemModel marginTop] + [self.padding tm_floatAtIndex:0]; 150 | } 151 | else{ 152 | cellY = CGRectGetMaxY(self.minRect) + [itemModel marginTop] + self.vGap ; 153 | } 154 | //第一个组件给一个padding 的影响 155 | if (i == 0) { 156 | cellX += [self.padding tm_floatAtIndex:3]; 157 | if ([itemModel.display isEqualToString:@"block"]) { 158 | [itemModel setItemFrame:CGRectMake(cellX + [itemModel marginLeft] , cellY, self.vv_width - [itemModel marginRight] - [itemModel marginLeft] - [self.padding tm_floatAtIndex:3] - [self.padding tm_floatAtIndex:1] , itemModel.itemFrame.size.height)]; 159 | } 160 | else{ 161 | [itemModel setItemFrame:CGRectMake(cellX + [itemModel marginLeft] , cellY, self.cellWidth, itemModel.itemFrame.size.height)]; 162 | } 163 | } 164 | else{ 165 | [itemModel setItemFrame:CGRectMake(cellX + [itemModel marginLeft] , cellY, self.cellWidth, itemModel.itemFrame.size.height)]; 166 | } 167 | CGRect cellFrame = CGRectMake(itemModel.itemFrame.origin.x, itemModel.itemFrame.origin.y, itemModel.itemFrame.size.width, itemModel.itemFrame.size.height + [itemModel marginBottom]); 168 | // 看看是不是变成最大的了 169 | if (CGRectGetMaxY(self.maxRect) < CGRectGetMaxY(cellFrame) + [itemModel marginBottom]) { 170 | self.maxRect = cellFrame; 171 | } 172 | // 更新最下边一行的记录字典,用rect.x做key,保证不重复 173 | [self.bottomRects tm_safeSetObject:[NSValue valueWithCGRect:cellFrame] forKey:[NSString stringWithFormat:@"%f", CGRectGetMinX(cellFrame)]]; 174 | // 先随便给一个值,然后去最后一行找 175 | self.minRect = cellFrame; 176 | // 还没排满的时候 177 | if (self.numberOfColumns > self.bottomRects.count) { 178 | self.minRect = CGRectMake(CGRectGetMaxX(self.minRect) + self.hGap , 0.f, 0.f, 0.f); 179 | } 180 | //获取一个对象 181 | NSValue *value = [self.bottomRects.allValues firstObject]; 182 | //获取到它的值 183 | CGFloat anyY = CGRectGetMaxY([value CGRectValue]); 184 | BOOL allSameY = YES; 185 | for (NSValue *value in self.bottomRects.allValues) { 186 | CGRect rect = [value CGRectValue]; 187 | if (CGRectGetMaxY([value CGRectValue]) != anyY) { 188 | allSameY = NO; 189 | } 190 | if (CGRectGetMaxY(rect) < CGRectGetMaxY(self.minRect)) { 191 | self.minRect = rect; 192 | } 193 | } 194 | if (allSameY && self.bottomRects.allValues.count >= 2) { 195 | for (NSValue *value in self.bottomRects.allValues) { 196 | CGRect rect = [value CGRectValue]; 197 | if (CGRectGetMinX(rect) < CGRectGetMinX(self.minRect)) { 198 | self.minRect = rect; 199 | } 200 | } 201 | } 202 | 203 | } 204 | self.vv_height = MAX(CGRectGetMaxY(self.maxRect), CGRectGetMaxY(self.minRect)) + [self.padding tm_floatAtIndex:2]; 205 | if (self.bgImgURL && self.bgImgURL.length > 0) { 206 | self.bgImageView.frame = CGRectMake(0, 0, self.vv_width, self.vv_height); 207 | [self.bgImageView sd_setImageWithURL:[NSURL URLWithString:self.bgImgURL]]; 208 | } 209 | 210 | 211 | } 212 | - (void)heightChangedWithElement:(UIView *)element model:(NSObject *)model 213 | { 214 | self.numberOfReloadRequests += 1; 215 | int currentNumber = self.numberOfReloadRequests; 216 | if (0 <= self.firstReloadRequestTS) { 217 | self.firstReloadRequestTS = [[NSDate date] timeIntervalSince1970]; 218 | } 219 | __weak typeof(self) wself = self; 220 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 221 | __strong typeof(wself) sself = wself; 222 | NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; 223 | // 没有新请求,或超过500毫秒了 224 | if (currentNumber == sself.numberOfReloadRequests 225 | || 500 > now - sself.firstReloadRequestTS) { 226 | sself.firstReloadRequestTS = 0; 227 | [sself calculateLayout]; 228 | if ([sself.superview isKindOfClass:[TangramView class]]) { 229 | //NSLog(@"relayout invoke time inwaterFlow : %lf ",[[NSDate date] timeIntervalSince1970]); 230 | [((TangramView *)sself.superview) performSelector:@selector(reLayoutContent) withObject:nil]; 231 | } 232 | } 233 | }); 234 | 235 | } 236 | 237 | -(NSString *)layoutLoadAPI 238 | { 239 | if (nil == _layoutLoadAPI) { 240 | _layoutLoadAPI = @""; 241 | } 242 | return _layoutLoadAPI; 243 | } 244 | -(NSString *)loadAPI 245 | { 246 | return self.layoutLoadAPI; 247 | } 248 | @end 249 | -------------------------------------------------------------------------------- /Tangram/Model/TangramDefaultItemModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultItemModel.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelProtocol.h" 10 | #import "TMLazyScrollView.h" 11 | 12 | @interface TangramDefaultItemModel : TMLazyItemModel 13 | 14 | // type 15 | @property (nonatomic, strong) NSString *type; 16 | // Margin for layouts 17 | @property (nonatomic, strong) NSArray *margin; 18 | // If the value of `display` is block in FlowLayout, the view will be shown in a whole line. 19 | @property (nonatomic, strong) NSString *display; 20 | // Insert squence in layout 21 | @property (nonatomic, strong) NSString *position; 22 | // The number of occupying columns, Only use in FlowLayout . 23 | @property (nonatomic, assign) NSUInteger colspan; 24 | // AspectRatio for model.The priority of this property is below `heightFromStyle` and `widthFromStyle` 25 | @property (nonatomic, assign) CGFloat modelAspectRatio; 26 | // Height in style.This property is highest priority for height. 27 | @property (nonatomic, assign) CGFloat heightFromStyle; 28 | // Width in style. This property is highest priority for width. 29 | @property (nonatomic, assign) CGFloat widthFromStyle; 30 | // Height get from `TangramElementHeightProtocol`. 31 | @property (nonatomic, assign) CGFloat heightFromElement; 32 | // Index in layout. 33 | @property (nonatomic, assign) NSUInteger index; 34 | // Binded element(UIView or its subclass) classname. 35 | @property (nonatomic, strong) NSString *linkElementName; 36 | // Specific ReuseIdentifier 37 | @property (nonatomic, strong) NSString *specificReuseIdentifier; 38 | // Whether disable reuse. 39 | @property (nonatomic, assign) BOOL disableReuse; 40 | // Whether a model for nested card.(For nested card,experiment function) 41 | @property (nonatomic, assign) BOOL innerItemModel; 42 | // The identifier for the outer layout of this nested card.(For nested card,experiment function) 43 | @property (nonatomic, strong) NSString *inLayoutIdentifier; 44 | // Layout identifier(For nested card,experiment function) 45 | @property (nonatomic, strong) NSString *layoutIdentifierForLayoutModel; 46 | // 47 | @property (nonatomic, assign) CGFloat zIndex; 48 | 49 | @property (nonatomic, assign) CGRect modelRect; 50 | 51 | - (NSArray *)bizKeys; 52 | 53 | - (NSArray *)styleKeys; 54 | 55 | - (void)setBizValue:(id)value forKey:(NSString *)key; 56 | 57 | - (void)setStyleValue:(id)value forKey:(NSString *)key; 58 | 59 | // Get a business param 60 | - (id)bizValueForKey:(NSString *)key; 61 | // Get a business param, if not match the desired class type, here will return nil. 62 | - (id)bizValueForKey:(NSString *)key desiredClass:(__unsafe_unretained Class)aClass; 63 | // Get a style param 64 | - (id)styleValueForKey:(NSString *)key; 65 | // Get a style param, if not match the desired class type, here will return nil. 66 | - (id)styleValueForKey:(NSString *)key desiredClass:(__unsafe_unretained Class)aClass; 67 | 68 | - (NSDictionary *)privateOriginalDict; 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Tangram/Model/TangramDefaultItemModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultItemModel.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramDefaultItemModel.h" 9 | #import "TMUtils.h" 10 | #import "TangramElementHeightProtocol.h" 11 | #import "TangramElementReuseIdentifierProtocol.h" 12 | 13 | @interface TangramDefaultItemModel() 14 | 15 | @property (nonatomic, strong) NSMutableDictionary *bizDict; 16 | 17 | 18 | @property (nonatomic, strong) NSMutableDictionary *styleDict; 19 | 20 | @end 21 | 22 | @implementation TangramDefaultItemModel 23 | 24 | - (NSArray *)margin 25 | { 26 | if (_margin && 4 == _margin.count) { 27 | return _margin; 28 | } 29 | return @[@0, @0, @0, @0]; 30 | } 31 | - (CGFloat)marginTop 32 | { 33 | return [[self.margin tm_safeObjectAtIndex:0] floatValue]; 34 | } 35 | 36 | - (CGFloat)marginRight 37 | { 38 | return [[self.margin tm_safeObjectAtIndex:1] floatValue]; 39 | } 40 | 41 | - (CGFloat)marginBottom 42 | { 43 | return [[self.margin tm_safeObjectAtIndex:2] floatValue]; 44 | } 45 | 46 | - (CGFloat)marginLeft 47 | { 48 | return [[self.margin tm_safeObjectAtIndex:3] floatValue]; 49 | } 50 | 51 | //Default it is `inline` 52 | - (NSString *)display 53 | { 54 | if (_display.length > 0) { 55 | return _display; 56 | } else { 57 | return @"inline"; 58 | } 59 | } 60 | 61 | - (TangramItemType *)itemType 62 | { 63 | return self.type; 64 | } 65 | 66 | - (NSUInteger )colspan 67 | { 68 | if (_colspan > 1) { 69 | return _colspan; 70 | } 71 | return 1; 72 | } 73 | 74 | // For overriding 75 | - (CGRect)itemFrame 76 | { 77 | CGRect rect = self.modelRect; 78 | if (self.heightFromElement > 0) { 79 | rect.size.height = self.heightFromElement; 80 | } 81 | if (self.widthFromStyle > 0) { 82 | rect.size.width = self.widthFromStyle; 83 | } 84 | if (rect.size.width > 0 && self.modelAspectRatio > 0) { 85 | rect.size.height = rect.size.width / self.modelAspectRatio; 86 | } 87 | if (self.heightFromStyle > 0) { 88 | rect.size.height = self.heightFromStyle; 89 | } 90 | return rect; 91 | } 92 | 93 | - (void)setItemFrame:(CGRect)itemFrame 94 | { 95 | self.modelRect = itemFrame; 96 | 97 | if (self.heightFromElement <=0 && self.linkElementName.length > 0 && itemFrame.size.width > 0 && itemFrame.size.height <= 0) { 98 | if ([NSClassFromString(self.linkElementName) conformsToProtocol:@protocol(TangramElementHeightProtocol)]) { 99 | Class elementClass = NSClassFromString(self.linkElementName); 100 | if ([NSClassFromString(self.linkElementName) instanceMethodForSelector:@selector(heightByModel:)]) { 101 | self.heightFromElement = [elementClass heightByModel:self]; 102 | } 103 | } 104 | } 105 | } 106 | 107 | - (NSString *)reuseIdentifier 108 | { 109 | if (self.disableReuse) { 110 | return @""; 111 | } 112 | if(self.specificReuseIdentifier && self.specificReuseIdentifier.length > 0){ 113 | return self.specificReuseIdentifier; 114 | } 115 | if ([NSClassFromString(self.linkElementName) conformsToProtocol:@protocol(TangramElementReuseIdentifierProtocol)] && [NSClassFromString(self.linkElementName) instanceMethodForSelector:@selector(reuseIdByModel:)]) { 116 | Class elementClass = NSClassFromString(self.linkElementName); 117 | return [elementClass reuseIdByModel:self]; 118 | } 119 | return self.type; 120 | } 121 | 122 | - (id)bizValueForKey:(NSString *)key; 123 | { 124 | return [self.bizDict tm_safeObjectForKey:key]; 125 | } 126 | 127 | - (id)bizValueForKey:(NSString *)key desiredClass:(__unsafe_unretained Class)aClass 128 | { 129 | return [self.bizDict tm_safeObjectForKey:key class:aClass]; 130 | } 131 | 132 | - (id)styleValueForKey:(NSString *)key 133 | { 134 | return [self.styleDict objectForKey:key]; 135 | } 136 | 137 | - (id)styleValueForKey:(NSString *)key desiredClass:(__unsafe_unretained Class)aClass 138 | { 139 | return [self.styleDict tm_safeObjectForKey:key class:aClass]; 140 | } 141 | 142 | - (NSArray *)bizKeys 143 | { 144 | return [self.bizDict allKeys]; 145 | } 146 | 147 | - (NSArray *)styleKeys 148 | { 149 | return [self.styleDict allKeys]; 150 | } 151 | 152 | - (NSMutableDictionary *)bizDict 153 | { 154 | if (nil == _bizDict) { 155 | _bizDict = [[NSMutableDictionary alloc]init]; 156 | } 157 | return _bizDict; 158 | } 159 | 160 | - (NSMutableDictionary *)styleDict 161 | { 162 | if (nil == _styleDict) { 163 | _styleDict = [[NSMutableDictionary alloc]init]; 164 | } 165 | return _styleDict; 166 | } 167 | 168 | - (void)setBizValue:(id)value forKey:(NSString *)key 169 | { 170 | [self.bizDict tm_safeSetObject:value forKey:key]; 171 | } 172 | 173 | - (void)setStyleValue:(id)value forKey:(NSString *)key 174 | { 175 | [self.styleDict tm_safeSetObject:value forKey:key]; 176 | } 177 | 178 | - (NSDictionary *)privateOriginalDict 179 | { 180 | NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 181 | [dict addEntriesFromDictionary:self.bizDict]; 182 | [dict addEntriesFromDictionary:self.styleDict]; 183 | [dict tm_safeSetObject:self.type forKey:@"type"]; 184 | return [dict copy]; 185 | } 186 | 187 | @end 188 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramDefaultEventDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramDefaultEventDelegate.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @class TangramDefaultItemModel; 12 | 13 | 14 | @protocol TangramDefaultEventDelegate 15 | 16 | @required 17 | 18 | - (void)elementClicked:(UIView *)element itemModel:(TangramDefaultItemModel *)itemModel action:(NSString *)action userInfo:(NSDictionary *)userInfo; 19 | 20 | @optional 21 | 22 | - (void)elementExposure:(UIView *)element itemModel:(TangramDefaultItemModel *)itemModel; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramEasyElementProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramEasyElementProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramDefaultItemModel.h" 10 | #import "TangramDefaultEventDelegate.h" 11 | #import "TangramLayoutProtocol.h" 12 | #import "TangramBus.h" 13 | 14 | 15 | @protocol TangramEasyElementProtocol 16 | 17 | @required 18 | 19 | //Get itemModel 20 | - (TangramDefaultItemModel *)tangramItemModel; 21 | //Set itemModel 22 | - (void)setTangramItemModel: (TangramDefaultItemModel *)tangramItemModel; 23 | 24 | @optional 25 | 26 | // delegate 27 | - (id)tangramEventDelegate; 28 | - (void)setTangramEventDelegate: (id)delegate; 29 | 30 | // Bind layout 31 | - (void)setAtLayout: (UIView *)layout; 32 | - (UIView *)atLayout; 33 | 34 | // Bind TangramBus 35 | - (void)setTangramBus:(TangramBus *)tangramBus; 36 | 37 | - (void)buildControlNameByPrefix:(NSString *)prefix extraArgs:(NSDictionary *)args; 38 | 39 | - (void)setReadCacheInMainThread:(BOOL)mainThread; 40 | 41 | - (void)buildContent:(BOOL)shouldReload; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramElementFactoryProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramElementFactoryProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelProtocol.h" 10 | @protocol TangramElementFactoryProtocol 11 | 12 | @required 13 | 14 | 15 | 16 | /** 17 | Return view by model 18 | 19 | @param model ItemModel Generated by ItemModelFactoty 20 | @return view 21 | */ 22 | + (UIView *)elementByModel:(NSObject *)model; 23 | 24 | /** 25 | Return view after be refreshed 26 | 27 | @param element to be refreshed 28 | @return element after refreshed 29 | */ 30 | + (UIView *)refreshElement:(UIView *)element byModel:(NSObject *)model; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramElementHeightProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramElementHeightProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramDefaultItemModel; 11 | 12 | 13 | @protocol TangramElementHeightProtocol 14 | 15 | + (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramElementReuseIdentifierProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramElementReuseIdentifierProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramDefaultItemModel; 11 | 12 | 13 | @protocol TangramElementReuseIdentifierProtocol 14 | 15 | + (NSString *)reuseIdByModel:(TangramDefaultItemModel *)itemModel; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramItemModelFactoryProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramItemModelFactoryProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelProtocol.h" 10 | 11 | @protocol TangramItemModelFactoryProtocol 12 | 13 | @required 14 | 15 | /** 16 | Generate itemModel by a dictionary 17 | 18 | @return itemModel 19 | */ 20 | + (NSObject *)itemModelByDict:(NSDictionary *)dict; 21 | /** 22 | Regist Element 23 | 24 | @param type In ItemModel we need return a itemType, the itemType will be used here 25 | */ 26 | + (void)registElementType:(NSString *)type className:(NSString *)elementClassName; 27 | 28 | + (BOOL)isTypeRegisted:(NSString *)type; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramItemModelProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramItemModelProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | typedef NSString TangramItemType; 11 | 12 | @protocol TangramItemModelProtocol 13 | 14 | @required 15 | 16 | // This property is used for layout to change the frame of itemModel. 17 | @property (nonatomic, assign) CGRect itemFrame; 18 | 19 | // model type 20 | - (TangramItemType *)itemType; 21 | 22 | // Reuseidentifier for LazyscrollView , 23 | - (NSString *)reuseIdentifier; 24 | 25 | // Margin Top/Right/Bottom/Right 26 | - (CGFloat)marginTop; 27 | - (CGFloat)marginRight; 28 | - (CGFloat)marginBottom; 29 | - (CGFloat)marginLeft; 30 | 31 | // Like `display` in CSS. if `display` return 'block' , element will fill the whole row in flowlayout. 32 | - (NSString *)display; 33 | 34 | @optional 35 | 36 | // Absolute Rect 37 | @property (nonatomic, assign) CGRect absRect; 38 | // MuiID for LazyScrollView 39 | @property (nonatomic, strong) NSString *muiID; 40 | 41 | ////////////////For nested cards(Experimental function, only for FlowLayout and PageScrollLayout) 42 | // Layout identifier(if the model is for nested card) 43 | @property (nonatomic, strong) NSString *layoutIdentifierForLayoutModel; 44 | // Whether this model is for a nested card 45 | @property (nonatomic, assign) BOOL innerItemModel; 46 | // The identifier for the outer layout of this nested card 47 | @property (nonatomic, strong) NSString *inLayoutIdentifier; 48 | ////////////////For nested cards end 49 | // Binded element class name 50 | @property (nonatomic, strong) NSString *linkElementName; 51 | // Insert position for layout 52 | -(NSString *)position; 53 | // The number of colums an element will fill. 54 | -(NSUInteger )colspan; 55 | // Bound View ClassName, if has value, get height from view's static method 56 | -(NSString *)linkElementClassName; 57 | 58 | -(CGFloat )zIndex; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramLayoutFactoryProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramLayoutFactoryProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramLayoutProtocol.h" 10 | @protocol TangramLayoutFactoryProtocol 11 | 12 | @required 13 | /** 14 | Generate a layout by a dictionary 15 | 16 | @return layout 17 | */ 18 | + (UIView *)layoutByDict:(NSDictionary *)dict; 19 | 20 | 21 | 22 | @optional 23 | 24 | /** 25 | Return class name by type to ItemModelFactory 26 | in order to support nesting of layout 27 | 28 | @return layout class 29 | */ 30 | + (NSString *)layoutClassNameByType:(NSString *)type; 31 | /** 32 | Regist Layout Type and its className 33 | 34 | @param type is TangramLayoutType In TangramLayoutProtocol 35 | */ 36 | + (void)registLayoutType:(NSString *)type className:(NSString *)layoutClassName; 37 | 38 | /** 39 | Preprocess DataArray from original Array 40 | if implement this method in the layout factory, helper will call this methid in `layoutsWithArray` 41 | 42 | @return preprocess in originalarray 43 | */ 44 | + (NSArray *)preprocessedDataArrayFromOriginalArray:(NSArray *)originalArray; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramLayoutProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramLayoutProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramItemModelProtocol.h" 10 | #import "TangramBus.h" 11 | 12 | typedef NS_ENUM(NSInteger,TangramLayoutLoadType) 13 | { 14 | TangramLayoutLoadTypeNone = 0 , //No Network request 15 | TangramLayoutLoadTypeLoadOnce = -1, //Request once 16 | TangramLayoutLoadTypeByPage = 1 //Request By Page 17 | }; 18 | typedef NSString TangramLayoutType; 19 | 20 | 21 | @protocol TangramLayoutProtocol 22 | 23 | @required 24 | 25 | - (TangramLayoutType *)layoutType; 26 | // Contains itemModels 27 | @property (nonatomic, strong) NSArray *itemModels; 28 | // Weak reference for tangrambus 29 | @property (nonatomic, weak) TangramBus *tangramBus; 30 | // identifier for layout 31 | @property (nonatomic, strong) NSString *identifier; 32 | // Calculate itemModel position 33 | - (void)calculateLayout; 34 | - (void)heightChangedWithElement:(UIView *)element model:(NSObject *)model; 35 | @optional 36 | 37 | // Margin 38 | - (CGFloat)marginTop; 39 | - (CGFloat)marginRight; 40 | - (CGFloat)marginBottom; 41 | - (CGFloat)marginLeft; 42 | // Position is for Layout like FixLayout to get fixed position 43 | - (NSString *)position; 44 | // loadAPI 45 | - (NSString *)loadAPI; 46 | - (TangramLayoutLoadType)loadType; 47 | - (NSDictionary *)loadParams; 48 | 49 | - (void)setSubLayoutIndex:(NSString *)layoutIndex; 50 | 51 | - (void)setEnableMarginDeduplication:(BOOL)enableMarginDeduplication; 52 | 53 | // Set Background Url 54 | - (void)setBgImgURL:(NSString *)imgURL; 55 | 56 | - (void)setHeaderItemModel:(NSObject *)model; 57 | - (NSObject *)headerItemModel; 58 | - (void)setFooterItemModel:(NSObject *)model; 59 | - (NSObject *)footerItemModel; 60 | - (void)addHeaderView:(UIView *)headerView; 61 | - (void)addFooterView:(UIView *)footerView; 62 | ///////////////////////////////For nested cards(Experimental function, only for FlowLayout and PageScrollLayout) 63 | // If a card want to support nested card, must implement these method 64 | // return sub layouts,key is the identifier for some nested card,value is a nested layout(UIView or its subclass) 65 | - (NSDictionary *)subLayoutDict; 66 | - (void)setSubLayoutDict:(NSDictionary *)subLayouts; 67 | // return identifiers of sub layouts. 68 | - (NSArray *)subLayoutIdentifiers; 69 | - (void)setSubLayoutIdentifiers:(NSString *)layoutIdentifier; 70 | // To be layout.layer.zPosition 71 | - (CGFloat)zIndex; 72 | - (BOOL)disableUserInteraction; 73 | - (void)addSubView:(UIView *)view withModel:(NSObject *)model; 74 | ///////////////////////////////For nested cards end 75 | 76 | @end 77 | 78 | 79 | -------------------------------------------------------------------------------- /Tangram/Protocols/TangramScrollLayoutProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramScrollLayoutProtocol.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #define ScrollLayoutDidMoveToPage @"ScrollFlowLayoutDidMoveToPage" 10 | #define ScrollLayoutCaptureImage @"ScrollLayoutCaptureImage" 11 | @protocol TangramScrollLayoutProtocol 12 | 13 | @required 14 | 15 | @property (nonatomic, assign) NSInteger pagingIndex; 16 | @property (nonatomic, assign) NSInteger pagingLength; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Tangram/Resources/TangramKitVVElementTypeMap.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | element 7 | TMVVBaseElement 8 | type 9 | TmallComponent2 10 | fileName 11 | TmallComponent2 12 | ratio 13 | 1.2 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Tangram/Resources/TangramLayoutTypeMap.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | class 7 | TangramSingleColumnLayout 8 | type 9 | 1 10 | 11 | 12 | class 13 | TangramDoubleColumnLayout 14 | type 15 | 2 16 | 17 | 18 | class 19 | TangramTribleColumnLayout 20 | type 21 | 3 22 | 23 | 24 | class 25 | TangramTetradColumnLayout 26 | type 27 | 4 28 | 29 | 30 | class 31 | TangramSingleAndDoubleLayout 32 | type 33 | 5 34 | 35 | 36 | class 37 | TangramDragableLayout 38 | type 39 | 7 40 | 41 | 42 | class 43 | TangramFixBottomLayout 44 | type 45 | 8 46 | 47 | 48 | class 49 | TangramQuintetColumnLayout 50 | type 51 | 9 52 | 53 | 54 | class 55 | TangramPageScrollLayout 56 | type 57 | 10 58 | 59 | 60 | class 61 | TangramStickyLayout 62 | type 63 | 20 64 | 65 | 66 | class 67 | TangramStickyLayout 68 | type 69 | 21 70 | 71 | 72 | class 73 | TangramScrollWaterFlowLayout 74 | type 75 | 25 76 | 77 | 78 | class 79 | TangramScrollFlowLayout 80 | type 81 | 27 82 | 83 | 84 | class 85 | TangramFixLayout 86 | type 87 | 28 88 | 89 | 90 | class 91 | TangramFixTopLayout 92 | type 93 | 23 94 | 95 | 96 | class 97 | TangramPageScrollLayout 98 | type 99 | 29 100 | 101 | 102 | class 103 | TangramFixLayout 104 | type 105 | 30 106 | 107 | 108 | class 109 | TangramFixLayout 110 | type 111 | container-fix 112 | 113 | 114 | class 115 | TangramScrollFlowLayout 116 | type 117 | container-flow 118 | 119 | 120 | class 121 | TangramScrollWaterFlowLayout 122 | type 123 | container-waterfall 124 | 125 | 126 | class 127 | TangramPageScrollLayout 128 | type 129 | container-scroll 130 | 131 | 132 | class 133 | TangramFixLayout 134 | type 135 | container-scrollFix 136 | 137 | 138 | class 139 | TangramStickyLayout 140 | type 141 | container-sticky 142 | 143 | 144 | class 145 | TangramDragableLayout 146 | type 147 | container-float 148 | 149 | 150 | class 151 | TangramSingleAndDoubleLayout 152 | type 153 | container-onePlusN 154 | 155 | 156 | class 157 | TangramSingleColumnLayout 158 | type 159 | container-oneColumn 160 | 161 | 162 | class 163 | TangramDoubleColumnLayout 164 | type 165 | container-twoColumn 166 | 167 | 168 | class 169 | TangramTribleColumnLayout 170 | type 171 | container-threeColumn 172 | 173 | 174 | class 175 | TangramTetradColumnLayout 176 | type 177 | container-fourColumn 178 | 179 | 180 | class 181 | TangramQuintetColumnLayout 182 | type 183 | container-fiveColumn 184 | 185 | 186 | class 187 | TangramFixLayout 188 | type 189 | container-scrollFixBanner 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /Tangram/Resources/TmallComponent2.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/Tangram-iOS/2f11572c9d9098185f1ce301a28c961ad38f438f/Tangram/Resources/TmallComponent2.out -------------------------------------------------------------------------------- /Tangram/UI/TangramPageControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramPageControl.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | typedef enum { 11 | TangramPageControlStyleDefault, 12 | TangramPageControlStyleImage 13 | } TangramPageControlStyle; 14 | 15 | 16 | @interface TangramPageControl : UIControl 17 | 18 | @property (nonatomic) TangramPageControlStyle style; 19 | 20 | @property (nonatomic) NSInteger numberOfPages; 21 | @property (nonatomic) NSInteger currentPage; 22 | 23 | @property (nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO 24 | 25 | @property (nonatomic, retain) UIColor * maskColor; 26 | @property (nonatomic) CGFloat maskAlpha; // worked when the maskColor is not set to clear. assign to 0.0 ~ 1.0 27 | 28 | @property (nonatomic) CGFloat pageWidth; 29 | @property (nonatomic) CGFloat pageHeight; 30 | @property (nonatomic) CGFloat pageSpacing; 31 | 32 | // for TangramPageControlStyleImage 33 | @property (nonatomic, strong) UIImage * normalImage; 34 | @property (nonatomic, strong) UIImage * selectedImage; 35 | // for TangramPageControlStyleDefault 36 | @property (nonatomic, strong) UIColor *normalFillColor; 37 | @property (nonatomic, strong) UIColor *selectedFillColor; 38 | 39 | 40 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Tangram/UI/TangramPageControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramPageControl.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramPageControl.h" 9 | #import "TMUtils.h" 10 | #define ELEMENT_WIDTH 4 11 | #define ELEMENT_HEIGHT 4 12 | #define ELEMENT_SPACING 10 13 | 14 | @interface TangramPageControl() 15 | { 16 | NSMutableArray *_norDots; 17 | } 18 | @end 19 | 20 | @implementation TangramPageControl 21 | 22 | - (id)initWithFrame:(CGRect)frame 23 | { 24 | self = [super initWithFrame:frame]; 25 | if (self) 26 | { 27 | _pageWidth = ELEMENT_WIDTH; 28 | _pageHeight = ELEMENT_HEIGHT; 29 | _pageSpacing = ELEMENT_SPACING; 30 | 31 | _norDots = [[NSMutableArray alloc] init]; 32 | 33 | _selectedFillColor = [UIColor clearColor]; 34 | _normalFillColor = [UIColor clearColor]; 35 | } 36 | 37 | return self; 38 | } 39 | 40 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount 41 | { 42 | CGFloat width = (pageCount * _pageWidth) + ((pageCount-1) * _pageSpacing); 43 | return CGSizeMake(width, _pageHeight); 44 | } 45 | 46 | -(void)createDotIndicators 47 | { 48 | [_norDots makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 49 | [_norDots removeAllObjects]; 50 | 51 | for (int i=0; i<_numberOfPages; i++) { 52 | 53 | switch (_style) 54 | { 55 | case TangramPageControlStyleDefault: 56 | { 57 | CAShapeLayer *layer = [CAShapeLayer layer]; 58 | UIBezierPath *ellipse = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, _pageWidth, _pageHeight)]; 59 | layer.path = [ellipse CGPath]; 60 | 61 | layer.fillColor = i==_currentPage? _selectedFillColor.CGColor:_normalFillColor.CGColor; 62 | [self.layer addSublayer:layer]; 63 | 64 | [_norDots addObject:layer]; 65 | break; 66 | } 67 | 68 | case TangramPageControlStyleImage: 69 | { 70 | CALayer *layer = [CALayer layer]; 71 | layer.contents=(__bridge id)(i==_currentPage?_selectedImage.CGImage:_normalImage.CGImage); 72 | layer.contentsScale = [UIScreen mainScreen].scale; 73 | [self.layer addSublayer:layer]; 74 | 75 | [_norDots addObject:layer]; 76 | break; 77 | } 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | -(void)layoutSublayersOfLayer:(CALayer *)layer 85 | { 86 | if (_numberOfPages <= 1 && _hidesForSinglePage) { 87 | return; 88 | } 89 | 90 | CGSize size = self.bounds.size; 91 | CGSize dotSize = [self sizeForNumberOfPages:_numberOfPages]; 92 | __block CGRect frame = CGRectMake((size.width-dotSize.width)/2.0, 93 | (size.height-dotSize.height)/2.0, 94 | _pageWidth, 95 | _pageHeight); 96 | 97 | [_norDots enumerateObjectsUsingBlock:^(CALayer *layer, NSUInteger idx, BOOL *stop) { 98 | switch (self->_style) 99 | { 100 | case TangramPageControlStyleDefault: 101 | { 102 | if ([layer isKindOfClass:[CAShapeLayer class]]) { 103 | CAShapeLayer *shapeLayer = (CAShapeLayer*)layer; 104 | shapeLayer.frame = frame; 105 | 106 | UIBezierPath *ellipse = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self->_pageWidth, self->_pageHeight)]; 107 | shapeLayer.path = [ellipse CGPath]; 108 | 109 | shapeLayer.fillColor = idx==self->_currentPage? self->_selectedFillColor.CGColor:self->_normalFillColor.CGColor; 110 | 111 | } 112 | break; 113 | } 114 | case TangramPageControlStyleImage: 115 | { 116 | layer.frame = frame; 117 | layer.contents=(__bridge id)(idx==self->_currentPage?self->_selectedImage.CGImage:self->_normalImage.CGImage); 118 | break; 119 | } 120 | } 121 | frame.origin.x += (self->_pageWidth + self->_pageSpacing); 122 | }]; 123 | } 124 | 125 | - (CGSize)sizeThatFits:(CGSize)size 126 | { 127 | CGSize dotSize = [self sizeForNumberOfPages:_numberOfPages]; 128 | return CGSizeMake(MAX(dotSize.width, self.bounds.size.width), MAX(dotSize.height, self.bounds.size.height)); 129 | } 130 | 131 | //static int index_clamp(int index,int arraylength) 132 | //{ 133 | // return index - floorf((float)index / arraylength) * arraylength; 134 | //} 135 | // 136 | //- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 137 | //{ 138 | // UITouch *touch = [touches anyObject]; 139 | // CGPoint point = [touch locationInView:self]; 140 | // 141 | // if (point.x < self.width/2) { 142 | // self.currentPage = index_clamp(_currentPage-1, _numberOfPages); 143 | // } else { 144 | // self.currentPage = index_clamp(_currentPage+1, _numberOfPages); 145 | // } 146 | //} 147 | 148 | #pragma mark getter & setter 149 | - (void)setCurrentPage:(NSInteger)currentPage 150 | { 151 | if (_currentPage == currentPage || 0 > currentPage) { 152 | return; 153 | } 154 | 155 | //swap dots 156 | NSInteger prepage = _currentPage; 157 | _currentPage = currentPage; 158 | 159 | switch (_style) 160 | { 161 | case TangramPageControlStyleDefault: 162 | { 163 | CAShapeLayer *cur = (CAShapeLayer*)[_norDots tm_safeObjectAtIndex:_currentPage]; 164 | cur.fillColor = _selectedFillColor.CGColor; 165 | 166 | CAShapeLayer *prev = (CAShapeLayer*)[_norDots tm_safeObjectAtIndex:prepage]; 167 | prev.fillColor = _normalFillColor.CGColor; 168 | break; 169 | } 170 | 171 | case TangramPageControlStyleImage: 172 | { 173 | CALayer *cur = (CALayer*)[_norDots tm_safeObjectAtIndex:_currentPage]; 174 | cur.contents = (id)_selectedImage.CGImage; 175 | 176 | CALayer *prev = (CALayer*)[_norDots tm_safeObjectAtIndex:prepage]; 177 | prev.contents = (id)_normalImage.CGImage; 178 | break; 179 | } 180 | } 181 | } 182 | 183 | - (void)setStyle:(TangramPageControlStyle)style 184 | { 185 | _style = style; 186 | if (_norDots.count) { 187 | [self setNeedsLayout]; 188 | } 189 | } 190 | 191 | - (void)setSelectedImage:(UIImage *)selectedImage 192 | { 193 | _selectedImage = selectedImage; 194 | if (_norDots.count) { 195 | [self setNeedsLayout]; 196 | } 197 | } 198 | 199 | - (void)setNormalImage:(UIImage *)normalImage 200 | { 201 | _normalImage = normalImage; 202 | 203 | if (_norDots.count) { 204 | [self setNeedsLayout]; 205 | } 206 | } 207 | 208 | - (void)setNumberOfPages:(NSInteger)numberOfPages 209 | { 210 | if (numberOfPages == _numberOfPages) { 211 | return; 212 | } 213 | _numberOfPages = numberOfPages; 214 | 215 | [self createDotIndicators]; 216 | 217 | if (_currentPage >= numberOfPages) { 218 | self.currentPage = 0; 219 | } 220 | 221 | [self setNeedsLayout]; 222 | } 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /Tangram/UI/TangramProgressBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramProgressBar.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | typedef NS_ENUM(NSUInteger,TangramProgressBarType) 10 | { 11 | LinearTangramProgressBar = 0, 12 | BlockTangramProgressBar 13 | }; 14 | 15 | 16 | @interface TangramProgressBar : UIView 17 | 18 | //bar的颜色 19 | @property (nonatomic, strong) UIColor *barColor; 20 | //背景URL 底边和View的bottom对齐 21 | @property (nonatomic, strong) NSString *bgImgURL; 22 | //背景Image 23 | @property (nonatomic, strong) UIImageView *bgImageView; 24 | //bar的高度 25 | @property (nonatomic, assign) CGFloat barHeight; 26 | //bar的宽度 27 | @property (nonatomic, assign) CGFloat barWidth; 28 | //设置进度 29 | @property (nonatomic, assign) CGFloat progress; 30 | 31 | @property (nonatomic, assign) TangramProgressBarType progressBarType; 32 | 33 | @property (nonatomic, assign) BOOL autoHide; 34 | 35 | @property (nonatomic, strong) UIView *barView; 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Tangram/UI/TangramProgressBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramProgressBar.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramProgressBar.h" 9 | #import "UIImageView+WebCache.h" 10 | #import 11 | #import "TMUtils.h" 12 | 13 | 14 | @interface TangramProgressBar() 15 | 16 | @property (nonatomic, assign) BOOL shouldAnim; 17 | 18 | @property (nonatomic, assign) CGFloat autoHideTime; 19 | 20 | @end 21 | 22 | @implementation TangramProgressBar 23 | 24 | -(UIView *)barView 25 | { 26 | if (_barView == nil) { 27 | _barView = [[UIView alloc]init]; 28 | _barView.vv_left = 0; 29 | [self addSubview:_barView]; 30 | [self bringSubviewToFront:_barView]; 31 | } 32 | return _barView; 33 | } 34 | -(UIImageView *)bgImageView 35 | { 36 | if (_bgImageView == nil) { 37 | _bgImageView = [[UIImageView alloc]init]; 38 | _bgImageView.vv_left = 0; 39 | _bgImageView.contentMode = UIViewContentModeScaleToFill; 40 | [self addSubview:_bgImageView]; 41 | [self sendSubviewToBack:_bgImageView]; 42 | } 43 | return _bgImageView; 44 | } 45 | -(void)setBarColor:(UIColor *)barColor 46 | { 47 | _barColor = barColor; 48 | self.barView.backgroundColor = barColor; 49 | } 50 | -(void)setBarWidth:(CGFloat)barWidth 51 | { 52 | _barWidth = barWidth; 53 | self.barView.vv_width = barWidth; 54 | } 55 | -(void)setBarHeight:(CGFloat)barHeight 56 | { 57 | _barHeight = barHeight; 58 | self.barView.vv_height = barHeight; 59 | } 60 | -(CGFloat)autoHideTime 61 | { 62 | return .5f; 63 | } 64 | -(void)setAutoHide:(BOOL)autoHide 65 | { 66 | _autoHide = autoHide; 67 | self.alpha = 0.f; 68 | } 69 | -(void)setProgress:(CGFloat)progress 70 | { 71 | _progress = progress; 72 | if (self.progressBarType == LinearTangramProgressBar) { 73 | self.barView.vv_width = self.barWidth + (self.vv_width - self.barWidth) * progress; 74 | self.barView.vv_left = 0.f; 75 | if (self.barView.vv_width <= self.barWidth) { 76 | self.barView.vv_width = self.barWidth; 77 | } 78 | } 79 | else 80 | { 81 | self.alpha = 1.f; 82 | self.barView.vv_centerX = (self.vv_width - self.barView.vv_width) * progress + self.barView.vv_width / 2; 83 | if (self.barView.vv_left < 0) 84 | { 85 | self.barView.vv_left = 0; 86 | } 87 | else if(self.barView.vv_right > self.vv_width) 88 | { 89 | self.barView.vv_right = self.vv_width; 90 | } 91 | if (self.autoHide && !self.shouldAnim) { 92 | self.shouldAnim = YES; 93 | __weak typeof(self) weakSelf = self; 94 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.autoHideTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 95 | __strong typeof(weakSelf) strongSelf = self; 96 | [UIView animateWithDuration:0.5f animations:^{ 97 | strongSelf.alpha = 0.f; 98 | strongSelf.shouldAnim = NO; 99 | }]; 100 | }); 101 | } 102 | } 103 | } 104 | -(void)setBgImgURL:(NSString *)bgImgURL 105 | { 106 | _bgImgURL = bgImgURL; 107 | if (bgImgURL.length > 0) { 108 | __weak typeof(self) weakSelf = self; 109 | 110 | [[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:[NSURL URLWithString:bgImgURL] options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) { 111 | __strong typeof(self) strongSelf = weakSelf; 112 | if (!error) 113 | { 114 | strongSelf.bgImageView.vv_width = strongSelf.vv_width; 115 | strongSelf.bgImageView.vv_height = strongSelf.bgImageView.vv_width * image.size.height / strongSelf.bgImageView.vv_width; 116 | if(strongSelf.bgImageView.vv_height > strongSelf.vv_height) 117 | { 118 | strongSelf.bgImageView.vv_height = strongSelf.vv_height; 119 | } 120 | strongSelf.bgImageView.vv_bottom = strongSelf.vv_height; 121 | strongSelf.bgImageView.image = image; 122 | } 123 | }]; 124 | } 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /Tangram/Util/NSString+Tangram.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Tangram.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface NSString (Tangram) 11 | 12 | - (NSString *)trim; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /Tangram/Util/NSString+Tangram.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Tangram.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "NSString+Tangram.h" 9 | 10 | @implementation NSString (Tangram) 11 | 12 | - (NSString *)trim 13 | { 14 | return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Tangram/Util/UIView+Tangram.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Tangram.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @class TangramView; 12 | 13 | 14 | @interface UIView (Tangram) 15 | 16 | /** 17 | * Get outer TangramView for UIView 18 | */ 19 | - (TangramView *)inTangramView; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Tangram/Util/UIView+Tangram.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Tangram.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramView.h" 10 | 11 | 12 | @implementation UIView (Tangram) 13 | 14 | - (TangramView *)inTangramView 15 | { 16 | for (UIView *next = [self superview]; next; next = next.superview) { 17 | if ([next isKindOfClass:[TangramView class]]) { 18 | return (TangramView *)next; 19 | } 20 | } 21 | return nil; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Tangram/VirtualView/TMVVBaseElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // TMVVBaseElement.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TangramElementReuseIdentifierProtocol.h" 12 | #import "TangramElementHeightProtocol.h" 13 | #import "TangramEasyElementProtocol.h" 14 | #import "TangramDefaultItemModel.h" 15 | #import "TangramBus.h" 16 | 17 | @interface TMVVBaseElement : UIView 18 | @property(nonatomic, strong)VVViewContainer* contentView; 19 | @property(nonatomic, assign)BOOL disableCache; 20 | @property (nonatomic, strong) TangramDefaultItemModel *tangramItemModel; 21 | @property (nonatomic, weak) TangramBus *tangramBus; 22 | 23 | //实际用来刷新vv的内容 24 | @property(nonatomic, strong)NSMutableDictionary *vvDict; 25 | 26 | - (void)calculateLayout; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Tangram/VirtualView/TMVVBaseElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // TMVVBaseElement.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | #import "VVTempleteManager.h" 8 | #import "TMVVBaseElement.h" 9 | #import 10 | #import 11 | #import "TangramEvent.h" 12 | #import "UIView+Tangram.h" 13 | #import 14 | #import 15 | 16 | static BOOL xmlIsLoad = NO; 17 | 18 | @interface TMVVBaseElement (){ 19 | // 20 | } 21 | @property(assign, nonatomic)BOOL appear; 22 | @end 23 | 24 | @implementation TMVVBaseElement 25 | 26 | + (void)initVirtualViewSystem{ 27 | [VVTempleteManager sharedInstance]; 28 | } 29 | 30 | - (id)init{ 31 | self = [super init]; 32 | if (self) { 33 | if (xmlIsLoad==NO) { 34 | [TMVVBaseElement initVirtualViewSystem]; 35 | xmlIsLoad = YES; 36 | } 37 | } 38 | return self; 39 | } 40 | 41 | - (CGRect)fitRect:(CGRect)originalFrame 42 | { 43 | CGFloat left = CGRectGetMinX(originalFrame); 44 | CGFloat right = CGRectGetMaxX(originalFrame); 45 | CGFloat top = CGRectGetMinY(originalFrame); 46 | CGFloat bottom = CGRectGetMaxY(originalFrame); 47 | left = round(left); 48 | right = round(right); 49 | top = round(top); 50 | bottom = round(bottom); 51 | return CGRectMake(left, top, right - left, bottom - top); 52 | } 53 | 54 | - (void)calculateLayout 55 | { 56 | ///self.itemModel.type 57 | self.frame = [self fitRect:self.frame]; 58 | if (self.contentView==nil) { 59 | self.contentView = [VVViewContainer viewContainerWithTemplateType:self.tangramItemModel.type]; 60 | self.contentView.delegate = self; 61 | [self addSubview:self.contentView]; 62 | } 63 | NSUInteger w = self.frame.size.width; 64 | NSUInteger h = self.frame.size.height; 65 | self.contentView.frame = CGRectMake(0, 0, w, h); 66 | [self.contentView update:self.tangramItemModel.privateOriginalDict]; 67 | } 68 | 69 | - (void)virtualViewClickedWithAction:(NSString *)action andValue:(NSString *)value 70 | { 71 | NSString *actualAction = value; 72 | if (actualAction.length <= 0) { 73 | actualAction = [self.tangramItemModel bizValueForKey:action]; 74 | } 75 | 76 | if (self.tangramBus) { 77 | TangramEvent *event = [[TangramEvent alloc]initWithTopic:TangramEventTopicJumpAction withTangramView:self.inTangramView posterIdentifier:@"singleImage" andPoster:self]; 78 | [event setParam:action forKey:@"action"]; 79 | 80 | [self.tangramBus postEvent:event]; 81 | } 82 | } 83 | 84 | 85 | + (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel 86 | { 87 | CGFloat ratio = [[VVTempleteManager sharedInstance]ratioByElementType:itemModel.type]; 88 | if(ratio > 0.f) 89 | { 90 | return itemModel.itemFrame.size.width / ratio; 91 | } 92 | return [[VVTempleteManager sharedInstance]heightByElementType:itemModel.type]; 93 | } 94 | 95 | + (NSString *)reuseIdByModel:(TangramDefaultItemModel *)itemModel 96 | { 97 | NSString *version = [[VVTempleteManager sharedInstance]localVersionByElementType:itemModel.type]; 98 | return [NSString stringWithFormat:@"%@_%@",itemModel.type,version]; 99 | } 100 | 101 | -(void)mui_afterGetView 102 | { 103 | [self calculateLayout]; 104 | } 105 | 106 | - (void)dealloc 107 | { 108 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Tangram/VirtualView/VVTempleteManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // VVTempleteManager.h 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @class TangramBus; 11 | @interface VVTempleteManager : NSObject 12 | 13 | @property (nonatomic, strong) NSMutableDictionary *templeteHeightDict; 14 | 15 | @property (nonatomic, strong) NSMutableDictionary *templeteRatioDict; 16 | 17 | /** 18 | 用于更新模板的时候向外面发消息用 19 | */ 20 | @property (nonatomic, weak) TangramBus *tangramBus; 21 | 22 | /** 23 | 获取Manager单例 24 | 25 | @return Manager单例 26 | */ 27 | + (VVTempleteManager*)sharedInstance; 28 | 29 | /** 30 | 加载缓存模板,这个方法目前只在初始化的时候执行一次 31 | 32 | */ 33 | - (void)registCacheTemplete; 34 | 35 | /** 36 | 读取本地模板 37 | */ 38 | - (void)loadCachedTemplete; 39 | 40 | /** 41 | 获取目前已有的组件列表 42 | 43 | @return 组件列表,Array里面是String 44 | */ 45 | - (NSArray *)elementList; 46 | 47 | /** 48 | 获取组件高度 49 | 50 | @param elementType 组件type 51 | @return 组件高度 52 | */ 53 | - (CGFloat)heightByElementType:(NSString *)elementType; 54 | 55 | 56 | - (CGFloat)ratioByElementType:(NSString *)elementType; 57 | /** 58 | 获取某个组件的本地使用版本 59 | 60 | @param elementType 组件type 61 | @return 本地使用版本 62 | */ 63 | - (NSString *)localVersionByElementType:(NSString *)elementType; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /Tangram/VirtualView/VVTempleteManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // VVTempleteManager.m 3 | // Tangram 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | // 这个里面维持的内容有:全量组件列表,打底组件列表对应的文件名,下发组件对应的文件名 9 | // NativeCache映射关系: NSDictionary : key type value : dictionary-> version/filePath/sign 10 | 11 | #define VVBundleName @"VVTempleteBundle" 12 | #define APPMONITOR_MODULE @"VirtualView" 13 | #import "VVTempleteManager.h" 14 | #import 15 | #import "VVBinaryLoader.h" 16 | #import "TangramDefaultDataSourceHelper.h" 17 | #import "TangramEvent.h" 18 | #import 19 | 20 | @interface VVTempleteManager() 21 | 22 | @property (nonatomic, strong) NSDictionary *localTempleteDict; 23 | 24 | @property (nonatomic, strong) NSMutableArray *templeteVersionArray; 25 | 26 | @property (nonatomic, strong) NSMutableDictionary *templeteVersionDict; 27 | 28 | @end 29 | 30 | @implementation VVTempleteManager 31 | 32 | + (VVTempleteManager*)sharedInstance 33 | { 34 | static VVTempleteManager *_vvTempleteManager = nil; 35 | static dispatch_once_t oncePredicate; 36 | dispatch_once(&oncePredicate, ^{ 37 | _vvTempleteManager = [[VVTempleteManager alloc] init]; 38 | }); 39 | return _vvTempleteManager; 40 | } 41 | 42 | - (instancetype)init 43 | { 44 | if (self = [super init]) { 45 | [self loadCachedTemplete]; 46 | } 47 | return self; 48 | } 49 | 50 | - (NSArray *)elementList 51 | { 52 | return [self.localTempleteDict allKeys]; 53 | } 54 | 55 | - (NSMutableArray *)templeteVersionArray 56 | { 57 | if (nil == _templeteVersionArray) { 58 | _templeteVersionArray = [[NSMutableArray alloc]init]; 59 | } 60 | return _templeteVersionArray; 61 | } 62 | 63 | - (NSMutableDictionary *)templeteVersionDict 64 | { 65 | if (nil == _templeteVersionDict) { 66 | _templeteVersionDict = [[NSMutableDictionary alloc]init]; 67 | } 68 | return _templeteVersionDict; 69 | } 70 | 71 | - (NSMutableDictionary *)templeteHeightDict 72 | { 73 | if (nil == _templeteHeightDict) 74 | { 75 | _templeteHeightDict = [[NSMutableDictionary alloc]init]; 76 | } 77 | return _templeteHeightDict; 78 | } 79 | 80 | - (NSMutableDictionary *)templeteRatioDict 81 | { 82 | if (nil == _templeteRatioDict) 83 | { 84 | _templeteRatioDict = [[NSMutableDictionary alloc]init]; 85 | } 86 | return _templeteRatioDict; 87 | } 88 | 89 | - (void)loadCachedTemplete 90 | { 91 | //获取本地全量组件列表 92 | [self readLocalTempleteDict]; 93 | [self registCacheTemplete]; 94 | } 95 | 96 | //获取全量组件列表 97 | - (void)readLocalTempleteDict 98 | { 99 | NSMutableDictionary *localTempleteDict = [[NSMutableDictionary alloc]init]; 100 | //目前来说,先获取TangramKit的全量组件列表,把type拿出来 101 | //从 TangramKitVVElementTypeMap.plist里面拿 102 | NSString *vvElementMapPath = [[NSBundle mainBundle] pathForResource:@"TangramKitVVElementTypeMap" ofType:@"plist"]; 103 | NSArray *vvElementArrayFromPlist = [NSArray arrayWithContentsOfFile:vvElementMapPath]; 104 | for (NSDictionary *dict in vvElementArrayFromPlist) { 105 | NSString *key = [dict tm_stringForKey:@"type"]; 106 | if (key.length == 0) { 107 | continue; 108 | } 109 | NSString *fileName = [dict tm_stringForKey:@"fileName"]; 110 | NSString *height = [dict tm_stringForKey:@"height"]; 111 | NSString *ratio = [dict tm_stringForKey:@"ratio"]; 112 | if (fileName.length > 0) { 113 | [localTempleteDict tm_safeSetObject:fileName forKey:key]; 114 | } 115 | if (height.length > 0) { 116 | [self.templeteHeightDict tm_safeSetObject:height forKey:key]; 117 | } 118 | if(ratio.length > 0) 119 | { 120 | [self.templeteRatioDict tm_safeSetObject:ratio forKey:key]; 121 | } 122 | } 123 | //返回本地组件列表 124 | self.localTempleteDict = localTempleteDict; 125 | } 126 | 127 | 128 | - (void)registCacheTemplete 129 | { 130 | //获取全量组件列表,来源:本地内置 + 网络请求后存在NativeCache的列表 131 | NSArray *elementList = [self.localTempleteDict allKeys]; 132 | for (NSString *templeteType in elementList) { 133 | NSMutableDictionary *templeteVersionDict = [[NSMutableDictionary alloc]init]; 134 | [templeteVersionDict tm_safeSetObject:templeteType forKey:@"type"]; 135 | NSFileManager* fileManager = [NSFileManager defaultManager]; 136 | 137 | //如果是本地的模板的话,直接version = 1 138 | NSString *localFileName = [self.localTempleteDict tm_stringForKey:templeteType]; 139 | NSString* localPath =[[NSBundle mainBundle] pathForResource:localFileName ofType:@"out"]; 140 | if (localFileName.length > 0 && localPath.length > 0 && [fileManager fileExistsAtPath:localPath]) { 141 | [[VVTemplateManager sharedManager] loadTemplateFileAsync:localPath forType:templeteType completion:nil]; 142 | [templeteVersionDict tm_safeSetObject:@"1" forKey:@"version"]; 143 | [self.templeteVersionDict tm_safeSetObject:@"1" forKey:templeteType]; 144 | [self.templeteVersionArray tm_safeAddObject:templeteVersionDict]; 145 | //作为预置本地组件,默认已经做过注册,不再注册了 146 | //[TangramDataSourceHelper registElementType:templeteType className:@"TMVVBaseElement"]; 147 | } 148 | } 149 | } 150 | 151 | /** 152 | 当模板发生变化时,需要把所有的模板清空之后重新加载 153 | */ 154 | - (void)reloadTemplete 155 | { 156 | [self loadCachedTemplete]; 157 | TangramEvent *event = [[TangramEvent alloc]initWithTopic:@"reloadDataByHelper" withTangramView:nil posterIdentifier:nil andPoster:self]; 158 | [self.tangramBus postEvent:event]; 159 | } 160 | 161 | - (CGFloat)heightByElementType:(NSString *)elementType 162 | { 163 | return [self.templeteHeightDict tm_floatForKey:elementType]; 164 | } 165 | 166 | - (CGFloat)ratioByElementType:(NSString *)elementType 167 | { 168 | return [self.templeteRatioDict tm_floatForKey:elementType]; 169 | } 170 | 171 | - (NSString *)localVersionByElementType:(NSString *)elementType 172 | { 173 | NSString *version = [self.templeteVersionDict tm_stringForKey:elementType]; 174 | if (version == nil || version.length <= 0) { 175 | //0 其实就是异常了 176 | version = @"0"; 177 | } 178 | return version; 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TangramDemo 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 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "EntryTableViewController.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 14 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 15 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[EntryTableViewController new]]; 16 | self.window.backgroundColor = [UIColor whiteColor]; 17 | [self.window makeKeyAndVisible]; 18 | return YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/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 | } -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/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 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/Element/TangramSimpleTextElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSimpleTextElement.h 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "TangramElementHeightProtocol.h" 10 | #import 11 | #import 12 | 13 | @interface TangramSimpleTextElement : UIView 14 | 15 | @property (nonatomic, strong) NSString *text; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/Element/TangramSimpleTextElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSimpleTextElement.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramSimpleTextElement.h" 9 | #import "TangramDefaultItemModel.h" 10 | 11 | @interface TangramSimpleTextElement() 12 | 13 | @property (nonatomic, strong) UILabel *label; 14 | 15 | @end 16 | 17 | @implementation TangramSimpleTextElement 18 | 19 | - (UILabel *)label 20 | { 21 | if (nil == _label) { 22 | _label = [[UILabel alloc]init]; 23 | [self addSubview:_label]; 24 | _label.font = [UIFont systemFontOfSize:14.f]; 25 | } 26 | return _label; 27 | } 28 | 29 | - (void)mui_afterGetView 30 | { 31 | self.label.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 32 | self.label.text = self.text; 33 | 34 | } 35 | 36 | + (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel 37 | { 38 | return 30.f; 39 | } 40 | @end 41 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/Element/TangramSingleImageElement.h: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSingleImageElement.h 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | #import 8 | #import "TangramElementHeightProtocol.h" 9 | #import "TMLazyScrollView.h" 10 | #import "TangramDefaultItemModel.h" 11 | #import "TangramEasyElementProtocol.h" 12 | #import 13 | 14 | @interface TangramSingleImageElement : UIControl 15 | 16 | @property (nonatomic, strong) NSString *imgUrl; 17 | 18 | @property (nonatomic, strong) NSNumber *number; 19 | 20 | @property (nonatomic, weak) TangramDefaultItemModel *tangramItemModel; 21 | 22 | @property (nonatomic, weak) UIView *atLayout; 23 | 24 | @property (nonatomic, weak) TangramBus *tangramBus; 25 | 26 | @property (nonatomic, strong) NSString *action; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/Element/TangramSingleImageElement.m: -------------------------------------------------------------------------------- 1 | // 2 | // TangramSingleImageElement.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "TangramSingleImageElement.h" 9 | #import 10 | #import "TangramEvent.h" 11 | #import "UIView+Tangram.h" 12 | 13 | @interface TangramSingleImageElement() 14 | 15 | @property (nonatomic, strong) UIImageView *imageView; 16 | 17 | @property (nonatomic, strong) UILabel *titleLabel; 18 | 19 | @end 20 | 21 | 22 | @implementation TangramSingleImageElement 23 | 24 | - (instancetype)init 25 | { 26 | self = [super init]; 27 | if (self) 28 | { 29 | [self addTarget:self action:@selector(clickedOnElement) forControlEvents:UIControlEventTouchUpInside]; 30 | self.clipsToBounds = YES; 31 | } 32 | return self; 33 | } 34 | 35 | 36 | - (UIImageView *)imageView 37 | { 38 | if (_imageView == nil) { 39 | _imageView = [[UIImageView alloc] init]; 40 | _imageView.userInteractionEnabled = NO; 41 | _imageView.contentMode = UIViewContentModeScaleToFill; 42 | _imageView.clipsToBounds = YES; 43 | [self addSubview:_imageView]; 44 | self.backgroundColor = [UIColor grayColor]; 45 | } 46 | return _imageView; 47 | } 48 | 49 | - (UILabel *)titleLabel 50 | { 51 | if (nil == _titleLabel) { 52 | _titleLabel = [[UILabel alloc]init]; 53 | _titleLabel.textColor = [UIColor redColor]; 54 | [self addSubview:_titleLabel]; 55 | } 56 | return _titleLabel; 57 | } 58 | -(void)setImgUrl:(NSString *)imgUrl 59 | { 60 | if (imgUrl.length > 0) { 61 | [self.imageView sd_setImageWithURL:[NSURL URLWithString:imgUrl]]; 62 | } 63 | } 64 | 65 | - (void)setFrame:(CGRect)frame 66 | { 67 | [super setFrame:frame]; 68 | if (frame.size.width > 0 && frame.size.height > 0) { 69 | [self mui_afterGetView]; 70 | } 71 | } 72 | 73 | - (void)mui_afterGetView 74 | { 75 | self.imageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 76 | self.titleLabel.text = [NSString stringWithFormat:@"%ld",[self.number longValue]]; 77 | [self.titleLabel sizeToFit]; 78 | } 79 | 80 | - (void)clickedOnElement 81 | { 82 | TangramEvent *event = [[TangramEvent alloc]initWithTopic:@"jumpAction" withTangramView:self.inTangramView posterIdentifier:@"singleImage" andPoster:self]; 83 | [event setParam:self.action forKey:@"action"]; 84 | [self.tangramBus postEvent:event]; 85 | } 86 | 87 | 88 | + (CGFloat)heightByModel:(TangramDefaultItemModel *)itemModel; 89 | { 90 | return 100.f; 91 | } 92 | @end 93 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/EntryTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // EntryTableViewController.h 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface EntryTableViewController : UITableViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/EntryTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // EntryTableViewController.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import "EntryTableViewController.h" 9 | #import "ViewController.h" 10 | #import "MockViewController.h" 11 | 12 | @interface EntryTableViewController () 13 | @property (nonatomic, strong) NSArray *testArray; 14 | @end 15 | 16 | @implementation EntryTableViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | 21 | // Uncomment the following line to preserve selection between presentations. 22 | // self.clearsSelectionOnViewWillAppear = NO; 23 | 24 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 25 | // self.navigationItem.rightBarButtonItem = self.editButtonItem; 26 | } 27 | 28 | - (void)didReceiveMemoryWarning { 29 | [super didReceiveMemoryWarning]; 30 | // Dispose of any resources that can be recreated. 31 | } 32 | - (NSArray *)testArray 33 | { 34 | return @[ 35 | @{@"name":@"自定义布局-色块Demo(不使用Helper)", @"entry":@"ViewController"}, 36 | @{@"name":@"JSON数据Demo(使用Helper)", @"entry":@"MockViewController"} 37 | ]; 38 | } 39 | #pragma mark - Table view data source 40 | 41 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 42 | return 1; 43 | } 44 | 45 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 46 | return self.testArray.count; 47 | } 48 | 49 | 50 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TEST"]; 52 | if (!cell) { 53 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TEST"]; 54 | } 55 | // Configure the cell... 56 | NSDictionary *dict = [self.testArray objectAtIndex:indexPath.row]; 57 | cell.textLabel.text = [dict objectForKey:@"name"]; 58 | return cell; 59 | } 60 | 61 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 62 | { 63 | //NSDictionary *dict = [self.testArray objectAtIndex:indexPath.row]; 64 | //NSString *controllerName = [dict objectForKey:@"entry"]; 65 | if (indexPath.row == 0) { 66 | [self.navigationController pushViewController:[[ViewController alloc]init] animated:YES]; 67 | } 68 | if (indexPath.row == 1) { 69 | [self.navigationController pushViewController:[[MockViewController alloc]init] animated:YES]; 70 | } 71 | //[self performSelector:NSSelectorFromString(method) withObject:nil]; 72 | } 73 | 74 | 75 | /* 76 | // Override to support conditional editing of the table view. 77 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { 78 | // Return NO if you do not want the specified item to be editable. 79 | return YES; 80 | } 81 | */ 82 | 83 | /* 84 | // Override to support editing the table view. 85 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 86 | if (editingStyle == UITableViewCellEditingStyleDelete) { 87 | // Delete the row from the data source 88 | [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 89 | } else if (editingStyle == UITableViewCellEditingStyleInsert) { 90 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 91 | } 92 | } 93 | */ 94 | 95 | /* 96 | // Override to support rearranging the table view. 97 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { 98 | } 99 | */ 100 | 101 | /* 102 | // Override to support conditional rearranging of the table view. 103 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { 104 | // Return NO if you do not want the item to be re-orderable. 105 | return YES; 106 | } 107 | */ 108 | 109 | 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/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 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/MockViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockViewController.h 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface MockViewController : UIViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/MockViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockViewController.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | #import "MockViewController.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | @interface MockViewController () 18 | 19 | @property (nonatomic, strong) NSMutableArray *layoutModelArray; 20 | 21 | @property (nonatomic, strong) NSMutableArray *modelArray; 22 | 23 | @property (nonatomic, strong) TangramView *tangramView; 24 | 25 | @property (nonatomic, strong) NSArray *layoutArray; 26 | 27 | @property (nonatomic, strong) TangramBus *tangramBus; 28 | 29 | 30 | @end 31 | 32 | @implementation MockViewController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | 37 | [self loadMockContent]; 38 | [self registEvent]; 39 | [self.tangramView reloadData]; 40 | // Do any additional setup after loading the view. 41 | } 42 | 43 | - (TangramBus *)tangramBus 44 | { 45 | if (nil == _tangramBus) { 46 | _tangramBus = [[TangramBus alloc]init]; 47 | } 48 | return _tangramBus; 49 | } 50 | -(NSMutableArray *)modelArray 51 | { 52 | if (nil == _modelArray) { 53 | _modelArray = [[NSMutableArray alloc]init]; 54 | } 55 | return _modelArray; 56 | } 57 | 58 | -(TangramView *)tangramView 59 | { 60 | if (nil == _tangramView) { 61 | _tangramView = [[TangramView alloc]init]; 62 | _tangramView.frame = self.view.bounds; 63 | [_tangramView setDataSource:self]; 64 | _tangramView.backgroundColor = [UIColor whiteColor]; 65 | [self.view addSubview:_tangramView]; 66 | } 67 | return _tangramView; 68 | } 69 | 70 | - (void)didReceiveMemoryWarning { 71 | [super didReceiveMemoryWarning]; 72 | // Dispose of any resources that can be recreated. 73 | } 74 | 75 | -(void)loadMockContent 76 | { 77 | NSString *mockDataString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"TangramMock" ofType:@"json"] encoding:NSUTF8StringEncoding error:nil]; 78 | NSData *data = [mockDataString dataUsingEncoding:NSUTF8StringEncoding]; 79 | NSDictionary *dict = [NSJSONSerialization JSONObjectWithData: data options:NSJSONReadingAllowFragments error:nil]; 80 | self.layoutModelArray = [[dict objectForKey:@"data"] objectForKey:@"cards"]; 81 | [TangramDefaultItemModelFactory registElementType:@"image" className:@"TangramSingleImageElement"]; 82 | [TangramDefaultItemModelFactory registElementType:@"text" className:@"TangramSimpleTextElement"]; 83 | // [TangramDefaultItemModelFactory registElementType:@"110" className:@"TangramSingleImageElement"]; 84 | // [TangramDefaultItemModelFactory registElementType:@"202" className:@"TangramSingleImageElement"]; 85 | // [TangramDefaultItemModelFactory registElementType:@"203" className:@"TangramSingleImageElement"]; 86 | // [TangramDefaultItemModelFactory registElementType:@"204" className:@"TangramSingleImageElement"]; 87 | self.layoutArray = [TangramDefaultDataSourceHelper layoutsWithArray:self.layoutModelArray tangramBus:self.tangramBus]; 88 | } 89 | 90 | - (void)registEvent 91 | { 92 | [self.tangramBus registerAction:@"responseToClickEvent:" ofExecuter:self onEventTopic:@"jumpAction"]; 93 | } 94 | 95 | - (void)responseToClickEvent:(TangramContext *)context 96 | { 97 | NSString *action = [context.event.params tm_stringForKey:@"action"]; 98 | NSLog(@"Click Action: %@",action); 99 | } 100 | - (NSUInteger)numberOfLayoutsInTangramView:(TangramView *)view 101 | { 102 | return self.layoutArray.count; 103 | } 104 | 105 | - (UIView *)layoutInTangramView:(TangramView *)view atIndex:(NSUInteger)index 106 | { 107 | return [self.layoutArray objectAtIndex:index]; 108 | } 109 | - (NSUInteger)numberOfItemsInTangramView:(TangramView *)view forLayout:(UIView *)layout 110 | { 111 | return layout.itemModels.count; 112 | } 113 | 114 | - (NSObject *)itemModelInTangramView:(TangramView *)view forLayout:(UIView *)layout atIndex:(NSUInteger)index 115 | { 116 | return [layout.itemModels objectAtIndex:index];; 117 | } 118 | 119 | - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index 120 | { 121 | UIView *reuseableView = [view dequeueReusableItemWithIdentifier:model.reuseIdentifier ]; 122 | 123 | if (reuseableView) { 124 | reuseableView = [TangramDefaultDataSourceHelper refreshElement:reuseableView byModel:model layout:layout tangramBus:self.tangramBus]; 125 | } 126 | else 127 | { 128 | reuseableView = [TangramDefaultDataSourceHelper elementByModel:model layout:layout tangramBus:self.tangramBus]; 129 | } 130 | return reuseableView; 131 | } 132 | @end 133 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TangramDemo 4 | // 5 | // Copyright (c) 2017-2018 Alibaba. All rights reserved. 6 | // 7 | #define TESTLAYOUT_NUMBER 20 8 | #define TESTCOLUMN 3 9 | #define TESTROW 4 10 | 11 | #import "ViewController.h" 12 | #import "TangramView.h" 13 | #import "TangramLayoutProtocol.h" 14 | #import "TangramSingleAndDoubleLayout.h" 15 | #import "TangramFlowLayout.h" 16 | #import "TangramFixLayout.h" 17 | #import "TangramStickyLayout.h" 18 | #import "TangramFixTopLayout.h" 19 | #import "TangramWaterFlowLayout.h" 20 | #import "TangramDragableLayout.h" 21 | 22 | @interface DemoItemModel : NSObject 23 | 24 | @property (nonatomic, assign) CGRect itemModelFrame; 25 | @property (nonatomic, assign) BOOL isBlock; 26 | @property (nonatomic, assign) NSUInteger indexInLayout; 27 | 28 | @end 29 | 30 | @interface DemoItem : UIView 31 | 32 | @property (nonatomic, strong) DemoItemModel *itemModel; 33 | 34 | @end 35 | 36 | @interface DemoLayout : TangramFlowLayout 37 | 38 | @property (nonatomic, assign) NSUInteger index; 39 | 40 | @end 41 | 42 | @implementation DemoItemModel 43 | 44 | - (void)setItemFrame:(CGRect)itemFrame 45 | { 46 | _itemModelFrame = itemFrame; 47 | } 48 | - (CGRect)itemFrame 49 | { 50 | //return _itemModelFrame; 51 | return CGRectMake(_itemModelFrame.origin.x,_itemModelFrame.origin.y, _itemModelFrame.size.width, _itemModelFrame.size.height); 52 | } 53 | 54 | - (NSString *)display 55 | { 56 | // if (self.isBlock) { 57 | // return @"block"; 58 | // } 59 | return @"inline"; 60 | } 61 | 62 | - (TangramItemType *)itemType 63 | { 64 | return @"demo"; 65 | } 66 | 67 | - (NSString *)reuseIdentifier 68 | { 69 | return @"demo_model_reuse_identifier"; 70 | } 71 | - (CGFloat)marginTop 72 | { 73 | return 5.f; 74 | } 75 | - (CGFloat)marginRight 76 | { 77 | return 5.f; 78 | } 79 | - (CGFloat)marginBottom 80 | { 81 | return 5.f; 82 | } 83 | - (CGFloat)marginLeft 84 | { 85 | return 5.f; 86 | } 87 | @end 88 | 89 | @implementation DemoItem 90 | 91 | - (NSObject *)model 92 | { 93 | return _itemModel; 94 | } 95 | @end 96 | 97 | @implementation DemoLayout 98 | 99 | 100 | - (TangramLayoutType *)layoutType 101 | { 102 | return [NSString stringWithFormat:@"xxxxx_%lu", self.index]; 103 | } 104 | 105 | 106 | 107 | 108 | @end 109 | //普通布局测试区结束 110 | //Fix 布局测试区 111 | 112 | 113 | @interface DemoFixModel : NSObject 114 | 115 | @property (nonatomic, assign) NSUInteger index; 116 | @property (nonatomic, assign) CGRect itemModelFrame; 117 | 118 | @end 119 | 120 | 121 | @implementation DemoFixModel 122 | 123 | - (CGFloat)marginTop 124 | { 125 | return 0.f; 126 | } 127 | - (CGFloat)marginLeft 128 | { 129 | return 0.f; 130 | } 131 | - (CGFloat)marginRight 132 | { 133 | return 0.f; 134 | } 135 | - (CGFloat)marginBottom 136 | { 137 | return 0.f; 138 | } 139 | - (NSString *)display 140 | { 141 | return @"inline"; 142 | } 143 | - (void)setItemFrame:(CGRect)itemFrame 144 | { 145 | _itemModelFrame = itemFrame; 146 | } 147 | - (CGRect)itemFrame 148 | { 149 | return CGRectMake(0,0, 100, 30); 150 | } 151 | - (TangramItemType *)itemType 152 | { 153 | return @"demo"; 154 | } 155 | 156 | - (NSString *)reuseIdentifier 157 | { 158 | return @""; 159 | } 160 | 161 | 162 | @end 163 | 164 | //Fix 固定布局测试区 结束 165 | @interface ViewController () 166 | 167 | @property (nonatomic, assign) NSUInteger totalIndex; 168 | 169 | @end 170 | 171 | @implementation ViewController 172 | 173 | 174 | #pragma mark - TangramViewDatasource 175 | - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index 176 | { 177 | 178 | //首先查找是否有可以复用的Item,是否可以复用是根据它的reuseIdentifier决定的 179 | //layout不复用,复用的是item 180 | DemoItem *item = (DemoItem *)[view dequeueReusableItemWithIdentifier:model.reuseIdentifier]; 181 | if (nil == item) { 182 | item = [[DemoItem alloc] initWithFrame:CGRectMake(0.f, 0.f, 0.f, 0.f) reuseIdentifier:model.reuseIdentifier]; 183 | } 184 | item.backgroundColor = [self randomColor]; 185 | // if ([layout isKindOfClass:[TangramFixLayout class]] || [layout isKindOfClass:[TangramStickyLayout class]] || [layout isKindOfClass:[TangramSingleAndDoubleLayout class]] || [layout isKindOfClass:[TangramWaterFlowLayout class]] || [layout isKindOfClass:[TangramDragableLayout class]]) { 186 | // return item; 187 | // } 188 | UILabel *testLabel = [item viewWithTag:1001]; 189 | if (!testLabel) { 190 | testLabel = [[UILabel alloc]init]; 191 | testLabel.frame =CGRectMake(2, 2, 30,30); 192 | testLabel.textColor = [UIColor whiteColor]; 193 | testLabel.tag = 1001; 194 | [item addSubview:testLabel]; 195 | } 196 | testLabel.text = [NSString stringWithFormat:@"%ld",index]; 197 | item.clipsToBounds = YES; 198 | return item; 199 | } 200 | //Layout不做复用,复用的是Item 201 | - (UIView *)layoutInTangramView:(TangramView *)view atIndex:(NSUInteger)index 202 | { 203 | if (index == 0) { 204 | //固定布局 205 | TangramDragableLayout *fixLayout = [[TangramDragableLayout alloc]init]; 206 | //fixLayout.margin = @[@100,@0,@0,@0]; 207 | fixLayout.alignType = TopRight; 208 | fixLayout.offsetX = 100; 209 | fixLayout.offsetY = 100; 210 | return fixLayout; 211 | } 212 | if (index == 1) { 213 | //一拖N布局 214 | TangramSingleAndDoubleLayout *layout = [[TangramSingleAndDoubleLayout alloc]init]; 215 | layout.rows = @[@40,@60]; 216 | return layout; 217 | } 218 | if (index == 3) { 219 | //吸顶布局 220 | TangramStickyLayout *floatLayout = [[TangramStickyLayout alloc]init]; 221 | return floatLayout; 222 | } 223 | if (index == 6){ 224 | TangramWaterFlowLayout *waterFlowLayout = [[TangramWaterFlowLayout alloc]init]; 225 | return waterFlowLayout; 226 | } 227 | //普通流式布局 228 | DemoLayout *layout = [[DemoLayout alloc] init]; 229 | layout.margin = @[@10,@20,@20,@20]; 230 | //layout.aspectRatio = @"5"; 231 | //控制列数,行数根据Item个数自己算 232 | //在Tangram的FlowLayout里面,行数默认是1 233 | layout.numberOfColumns = index % 5 + 1; 234 | layout.hGap = 3; 235 | layout.vGap = 5; 236 | layout.index = index; 237 | layout.backgroundColor = [self randomColor]; 238 | return layout; 239 | } 240 | 241 | - (NSObject *)itemModelInTangramView:(TangramView *)view forLayout:(UIView *)layout atIndex:(NSUInteger)index 242 | { 243 | if ([layout isKindOfClass:[TangramDragableLayout class]] || [layout isKindOfClass:[TangramStickyLayout class]]) { 244 | DemoFixModel *fixModel = [[DemoFixModel alloc]init]; 245 | return fixModel; 246 | } 247 | DemoItemModel *model = [[DemoItemModel alloc] init]; 248 | model.indexInLayout = index; 249 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x,model.itemFrame.origin.y, model.itemFrame.size.width, 150)]; 250 | if ([layout isKindOfClass:[TangramWaterFlowLayout class]]) { 251 | [model setItemFrame:CGRectMake(model.itemFrame.origin.x,model.itemFrame.origin.y, model.itemFrame.size.width, (arc4random() % 120) + 30)]; 252 | } 253 | return model; 254 | } 255 | 256 | - (NSUInteger)numberOfLayoutsInTangramView:(TangramView *)view 257 | { 258 | return TESTLAYOUT_NUMBER; 259 | } 260 | 261 | - (NSUInteger)numberOfItemsInTangramView:(TangramView *)view forLayout:(UIView *)layout 262 | { 263 | if ([layout isKindOfClass:[TangramFixLayout class]]) { 264 | return 1; 265 | } 266 | if ([layout isKindOfClass:[TangramSingleAndDoubleLayout class]]) { 267 | return 4; 268 | } 269 | if ([layout isKindOfClass:[TangramWaterFlowLayout class]]) { 270 | return 10; 271 | } 272 | if ([layout isKindOfClass:[TangramStickyLayout class]]) { 273 | return 1; 274 | } 275 | return 4; 276 | } 277 | 278 | - (void)viewDidLoad { 279 | [super viewDidLoad]; 280 | self.totalIndex = 0; 281 | TangramView *tangramView = [[TangramView alloc] initWithFrame:self.view.bounds]; 282 | tangramView.dataSource = self; 283 | tangramView.backgroundColor = [UIColor lightGrayColor]; 284 | [self.view addSubview:tangramView]; 285 | tangramView.fixExtraOffset = 64.f; 286 | [tangramView setDelegate:self]; 287 | [tangramView reloadData]; 288 | } 289 | 290 | #pragma mark - Private 291 | 292 | - (UIColor *)randomColor 293 | { 294 | CGFloat hue = ( arc4random() % 256 / 256.0 ); 295 | CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; 296 | CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; 297 | return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 298 | } 299 | 300 | @end 301 | -------------------------------------------------------------------------------- /TangramDemo/TangramDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TangramDemo 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 | -------------------------------------------------------------------------------- /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], 'Tangram'), 'Tangram') 30 | updateHeader(os.path.join(sys.path[0], 'TangramDemo'), 'TangramDemo') 31 | # updateHeader(os.path.join(sys.path[0], 'TangramTest'), 'TangramTest') 32 | print('Header updating is done.') 33 | 34 | --------------------------------------------------------------------------------