Castie!

正态分布, 优劣伴生

北冥有鱼,其名为鲲(kūn)。鲲之大,不知其几千里也;化而为鸟,其名为鹏。鹏之背,不知其几千里也;怒而飞,其翼若垂天之云。是鸟也,海运则将徙于南冥。南冥者,天池也。


北海若曰:“井鼃不可以语于海者,拘于虚也;夏虫不可以语于冰者,笃于时也;曲士不可以语于道者,束于教也。今尔出于崖涘,观于大海,乃知尔丑,尔将可与语大理矣。

iOS 执行.py脚本生成UI层结构

上篇我们实现了架构代码生成器, 顿时觉得代码生成器这个东西只要掌握了写代码真的就是配置的问题了, 今天我们就来实现我一直以来就想做的功能 – UI界面适配代码生成器, 和之前一样我们通过JSON配置和Python来进行实现, 没看过上一篇的直接看这篇即可, 主要是思想.

参考链接:

上篇有一个内存泄露的问题, 需要将P层的模板的.h属性改为weak即可, 适配UI界面代码生成器的模板是我一直以来的的OC编码习惯, 代码风格无需强求, 直接修改模板即可. 我们先来看UI层的生成器目录结构:

生成器目录结构

├── UITemplate
│   ├── CellAdapterTemplate.h 
│   ├── CollectionViewCellTemplate.h 
│   ├── CollectionViewCellTemplate.m
│   ├── CollectionViewTemplate.h
│   ├── CollectionViewTemplate.m
│   ├── SubmodelTemplate.h
│   ├── SubmodelTemplate.m
│   ├── SubviewTemplate.h
│   ├── SubviewTemplate.m
│   ├── TableViewCellTemplate.h
│   └── TableViewCellTemplate.m
├── builder.py
└── config.json
CellAdapterTemplate.h

#import <Foundation/Foundation.h>

@protocol <#Unit#>CellAdapter <NSObject>
<#ModelInterface#>

@end

  • cell的数据适配协议, 也就是适配器模式, 生成不依赖模型的Cell
TableViewCellTemplate.h
#import <UIKit/UIKit.h>
#import "<#Unit#>CellAdapter.h"

@interface <#Unit#>Cell : UITableViewCell

@property (nonatomic,strong) id<<#Unit#>CellAdapter> adapter;

+ (instancetype)cellWithTableView:(UITableView *)tableView;

+ (CGFloat)cellHeight;

@end

  • 将cell的实现和高度屏蔽在内
TableViewCellTemplate.m
#import "<#Unit#>Cell.h"
<#ViewImport#>
@interface <#Unit#>Cell ()

<#ViewProperty#>
@end

@implementation <#Unit#>Cell

- (void)dealloc {
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}

+ (instancetype)cellWithTableView:(UITableView *)tableView {
    
    NSString * identifier = NSStringFromClass([<#Unit#>Cell class]);
    <#Unit#>Cell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[<#Unit#>Cell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    return cell;
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

<#ViewLazyLoad#>

- (void)setupSubviews {
<#ViewSetup#>
}

- (void)setAdapter:(id<<#Unit#>CellAdapter>)adapter {
    _adapter = adapter;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
<#ViewLayout#>
}

+ (CGFloat)cellHeight {
    return <#cellHeight#>;
}

@end

  • cell的基本实现, 没有什么好说的.
SubviewTemplate.h
#import <UIKit/UIKit.h>

<#ClassImport#>
@interface <#Unit#>View : UIView

<#ViewProperty#>
@end

  • 将View的属性暴露在外供外部修改
SubviewTemplate.m
#import "<#Unit#>View.h"
<#ViewImport#>
@implementation <#Unit#>View

- (void)dealloc {
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder  {
    
    self = [super initWithCoder:coder];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

<#ViewLazyLoad#>
- (void)setupSubviews {
<#ViewSetup#>
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
<#ViewLayout#>
}


@end
  • 自定义View的基本实现
CollectionViewTemplate.h
#import <UIKit/UIKit.h>

@interface <#Unit#>View : UIView

@property (nonatomic,strong) NSArray * dataSourceArr;

@end
  • 传入可用data数组, 不在意是否为模型或字典.
CollectionViewTemplate.m
#import "<#Unit#>View.h"
#import "<#Unit#>Cell.h"

@interface <#Unit#>View () <UICollectionViewDataSource, UICollectionViewDelegate>

@property (nonatomic,strong) UICollectionView * collectionView;
@property (nonatomic,strong) UICollectionViewFlowLayout * collectionViewLayout;

@end

@implementation <#Unit#>View

- (void)dealloc {
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder  {
    
    self = [super initWithCoder:coder];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (UICollectionView *)collectionView {
    
    if (!_collectionView) {
        _collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:self.collectionViewLayout];;
        _collectionView.dataSource = self;
        _collectionView.delegate = self;
        _collectionView.backgroundColor = [UIColor whiteColor];
        _collectionView.pagingEnabled = YES;
        _collectionView.showsHorizontalScrollIndicator = NO;
        [_collectionView registerClass:[<#Unit#>Cell class] forCellWithReuseIdentifier:NSStringFromClass([<#Unit#>Cell class])];
    }
    return _collectionView;
}

- (UICollectionViewFlowLayout *)collectionViewLayout {
    
    if (!_collectionViewLayout) {
        _collectionViewLayout = [UICollectionViewFlowLayout new];
        _collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        _collectionViewLayout.minimumInteritemSpacing = 0.0f;
        _collectionViewLayout.minimumLineSpacing = 0.0f;
    }
    return _collectionViewLayout;
}

- (void)setupSubviews {
    [self addSubview:self.collectionView];
}

- (void)setDataSourceArr:(NSArray *)dataSourceArr {
    _dataSourceArr = dataSourceArr;
    [_collectionView reloadData];
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return _dataSourceArr.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    <#Unit#>Cell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([<#Unit#>Cell class]) forIndexPath:indexPath];
    return cell;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    _collectionView.frame = self.bounds;
    _collectionViewLayout.itemSize = <#size#>;

}

@end

  • collectionView的基本实现, 实现最基本的功能.
CollectionViewCellTemplate.h
#import <UIKit/UIKit.h>
#import "<#Unit#>CellAdapter.h"

@interface <#Unit#>Cell : UICollectionViewCell

@property (nonatomic,strong) id<<#Unit#>CellAdapter> adapter;

@end

  • 使用适配器加载数据.
CollectionViewCellTemplate.m
#import "<#Unit#>Cell.h"
<#ViewImport#>
@interface <#Unit#>Cell ()

<#ViewProperty#>
@end

@implementation <#Unit#>Cell

- (void)dealloc {
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder  {
    
    self = [super initWithCoder:coder];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

<#ViewLazyLoad#>

- (void)setupSubviews {
<#ViewSetup#>
}

- (void)setAdapter:(id<<#Unit#>CellAdapter>)adapter {
    _adapter = adapter;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
<#ViewLayout#>
}

@end

  • 一样的基本实现, 不多说.
SubmodelTemplate.h
#import <Foundation/Foundation.h>

@interface <#SubUnit#>Model : NSObject 
<#ModelInterface#>

- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
+ (<#SubUnit#>Model *)modelWithDictionary:(NSDictionary *)dictionary;

@end
SubmodelTemplate.m
#import "<#SubUnit#>Model.h"

@implementation <#SubUnit#>Model

- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
    
    self = [super init];
    if (self) {
        [self setValuesForKeysWithDictionary:dictionary];
    }
    return self;
}

+ (<#SubUnit#>Model *)modelWithDictionary:(NSDictionary *)dictionary {
    return [[<#SubUnit#>Model alloc]initWithDictionary:dictionary];
}

@end

  • 和上篇的model相同, 这篇为自动生成子模型. 为了灵活性单独生成.

说实话, 上述的UI层的界面重复的代码写的都快吐了, 开发过程中大多数时间就是花费在搭建UI界面上, 重复劳动, 真是醉了, 还好现在有了代码生成器这种神器, 写界面真的是分分钟搞定, 省下大把时间学习新东西.

config.json
{
	"tableViewCell" : "Yes", //是否生成TableViewCell
	"collectionViewCell" : "No", //是否生成CollectionViewCell
	"subview" : "No", //是否生成自定义View
	"submodel" : "No", //是否生成子Model
	"isImport" : "Yes", //是否需要导入自定义View

	"unit" : "HYBlockFive", //生成单元的名称
	"bean" : "Goods", //生成子模型的名称
	
	"layout" : { //文件布局的配置
		"blockFiveView" : "HYBlockFiveView",
        "textLabel" : "UILabel"
	},
	
	"model" : { //子模型和上篇模型相同
		"text" : "String",
		"detailText" : "String",
		"imageUrl" : "String",
		"models" : "Array",
		"price" : "Float"
	}
}

生成器的实现

builder.py
import os, sys, json

def cur_file_dir():
   path = sys.path[0]
   if os.path.isdir(path):
        return path
   elif os.path.isfile(path):
        return os.path.dirname(path)

file_json = open(cur_file_dir() + "/config.json", "r")
data_json = json.loads(file_json.read())

bean = str(data_json["bean"])
unit = str(data_json["unit"])
isImport = str(data_json["isImport"])

submodel = data_json["submodel"]
subview = data_json["subview"]
tableViewCell = data_json["tableViewCell"]
collectionViewCell = data_json["collectionViewCell"]

viewProperty = '' //view的属性
viewLazyLoad = '' //懒加载
viewSetup = '' //添加到superview
viewLayout = '' //view的布局
viewImport = '' //引入头文件
classImport = '' //引入class
for key in data_json["layout"]: //进行布局代码的生成
   value = data_json["layout"][key]
   viewProperty += "@property (nonatomic,strong) " + str(value) + " * " + str(key) + ";\n"
   viewLazyLoad += "- (" + str(value) + " *)" + str(key) + " {\n\n    if (!_" + str(key) + ") {\n        _" + str(key) + " = [" + str(value) + " new];\n    }\n    return _" + str(key) + ";\n}\n\n"
   viewSetup += "\n    [self addSubview:self." + str(key) + "];"
   viewLayout += "    CGFloat " + str(key) + "X = <#length#>;\n    CGFloat " + str(key) + "Y = <#length#>;\n    CGFloat " + str(key) + "W = <#length#>;\n    CGFloat " + str(key) + "H = <#length#>;\n    _" + str(key) + ".frame = CGRectMake(" + str(key) + "X, " + str(key) + "Y, " + str(key) + "W, " + str(key) + "H);\n\n"
   if isImport == 'Yes':
      viewImport += '#import "' + str(value) + '.h"\n'
      classImport += '@class ' + str(value) + ';\n'

model = ''
for key in data_json["model"]:
   type = ''
   modified = ''
   value = data_json["model"][key]
   if value == 'String':
      type = 'NSString * ' 
      modified = 'copy'
   elif value == 'Int':
   	  type = 'NSInteger '
   	  modified = 'assign'
   elif value == 'Float':
   	  type = 'CGFloat '
   	  modified = 'assign'
   elif value == 'Bool':
   	  type = 'BOOL '
   	  modified = 'assign'
   elif value == 'Array':
   	  type = 'NSArray * '
   	  modified = 'strong'
   elif value == 'Dictionary':
   	  type = 'NSDictionary * '
   	  modified = 'strong'
   else:
   	  type = value
   	  modified = 'strong'
   model += '\n@property (nonatomic,' + modified + ') ' + type + str(key) + ';'

file_json.close()

def builder(path, file, isbean):
   r = open(cur_file_dir() + '/UITemplate/' + path, 'r')
   d = r.read()
   r.close()
   if isbean == 'Yes': //判断输出的文件名是模型还是其他
      w = open(cur_file_dir() + '/' + bean + file, 'w')
   else:
      w = open(cur_file_dir() + '/' + unit + file, 'w')
   w.write(d.replace("<#ViewProperty#>", viewProperty).replace("<#Unit#>", unit).replace("<#ViewLazyLoad#>", viewLazyLoad).replace("<#ViewSetup#>",viewSetup).replace("<#ViewLayout#>",viewLayout).replace("<#SubUnit#>", bean).replace("<#ViewImport#>", viewImport).replace("<#ModelInterface#>", model).replace("<#ClassImport#>", classImport))
   w.close()

if subview == "Yes": //生成自定义View
   builder("SubviewTemplate.h", "View.h", "No")
   builder("SubviewTemplate.m", "View.m", "No")

if tableViewCell == "Yes": //生成TableViewCell
   builder("TableViewCellTemplate.h", "Cell.h", "No")
   builder("TableViewCellTemplate.m", "Cell.m", "No")
   builder("CellAdapterTemplate.h", "CellAdapter.h", "No")

if submodel == 'Yes': //生成子模型
   builder("SubmodelTemplate.h", "Model.h", "Yes")
   builder("SubmodelTemplate.m", "Model.m", "Yes")

if collectionViewCell == 'Yes': //生成CollectionViewCell
   builder("CollectionViewTemplate.h", "View.h", "No")
   builder("CollectionViewTemplate.m", "View.m", "No")
   builder("CollectionViewCellTemplate.h", "Cell.h", "No")
   builder("CollectionViewCellTemplate.m", "Cell.m", "No")
   builder("CellAdapterTemplate.h", "CellAdapter.h", "No")

  • 这样通过在终端中 python builder.py, 就能根据JSON表进行UI界面的的生成了.
彩蛋:

最近需要进行首页的改版, 我就使用这套生成器来练练手, 先贴下产品原型:

实测完成专题页一共生成53个文件, 仅需修改布局和导入, 完成用时30分钟左右.

代码库

当然我们写代码的时候也不能过分依赖代码生成工具, 我们还需要一些常用的代码库, 将我常用的分享与你.

//UIView
- (void)dealloc {
#if DEBUG
    NSLog(@"--------");
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
    NSLog(@"--------");
#endif
}

- (instancetype)initWithFrame:(CGRect)frame {
   
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder  {
   
    self = [super initWithCoder:coder];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (void)setupSubviews {
   
}

- (void)loadSubviews:(id)dataSource {
   
}

- (void)layoutSubviews {
    [super layoutSubviews];
   
}

//UITableViewCell
- (void)dealloc {
#if DEBUG
    NSLog(@"--------");
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
    NSLog(@"--------");
#endif
}

+ (instancetype)cellWithTableView:(UITableView *)tableView {
   
    NSString * identifier = NSStringFromClass([<#class#> class]);
    <#class#> * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[<#class#> alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    return cell;
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
   
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupSubviews];
    }
    return self;
}

- (void)setupSubviews {
   
}

- (void)loadSubviews:(id)dataSource {
   
}

- (void)layoutSubviews {
    [super layoutSubviews];
   
}

+ (CGFloat)cellHeight {
    return <#height#>
}

//Define Peoperty 
@property (nonatomic,<#modify#>) <#statement#>;

//Weak Self
__weak typeof(self) _self = self;

//Lazy Load
- (<#class#> *)<#name#> {
   
    if (!_<#name#>) {
        _<#name#> = <#initialize#>;
    }
    return _<#name#>;
}

//Frame Layout
CGFloat <#name#>X = <#length#>;
CGFloat <#name#>Y = <#length#>;
CGFloat <#name#>W = <#length#>;
CGFloat <#name#>H = <#length#>;
_<#name#>.frame = CGRectMake(<#name#>X, <#name#>Y, <#name#>W, <#name#>H);

//Debug
#if DEBUG
    NSLog(@"--------");
    NSLog(@“<#string#>  - %<#encode#>",<#param#>);
    NSLog(@"--------");
#endif

//KeyBoard Events
- (void)setupNotificationCenter {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillChangeFrame:)
                                                 name:UIKeyboardWillChangeFrameNotification object:nil];
}

- (void)removeNotificationCenter {
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

- (void)keyboardWillChangeFrame:(NSNotification *)notification {
   
    <#view#>.backgroundColor = self.backgroundColor;
    CGFloat duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGFloat transformY = keyboardFrame.origin.y - <#view#>.frame.size.height;
    [UIView animateWithDuration:duration animations:^{
        <#view#>.transform = CGAffineTransformMakeTranslation(0, transformY * 0.4f);
    }];
}

可以发现, 这次的UI界面生成器就是根据我常用的代码库制作的, 以后就可以以生成为主, 代码库为辅了, 哈哈!

github 下载地址!!!

具体源码及SQTemplates Demo 请到github上进行下载! 喜欢的朋友送下小星星哟!!

最近的文章

SpriteKit 通过检测掩码进行物理识别

上周忙着写代码生成器了, 回到我们的学习中来, 今天我们来继续学习SpriteKit, 上篇我们学习了一些2D游戏场景, 精灵, 摄像头, 行动的基本概念, 今天我们就来进入物理的世界, 感受碰撞所带来的乐趣.代码见:githubiOS7有出过UIDynamic, 用于UIVIew的物理动画效果, 同期Apple也出了SpriteKit框架, 到现在也已经3个年头了, 我们学习SpriteKit的目的也在于更好的对UIKit进行了解. 也算是一条技术进阶的路吧, 我们先来看一下学习物理...…

移动开发继续阅读
更早的文章

iOS 执行.py脚本生成解耦架构

刚刚学习完SpriteKit的第一章, 项目的需求就压过来了, 诶, 时间都去哪啦, 接着之前的热修复架构, 这次为了更加好的进行代码规范, 我们将之间的架构模式进行模板代码生成, 这里会用到一些简单的python, 没接触过的同学们可以先去了解下, 今天我就将零耦合代码生成工具分享与你.参考链接: Hybird 搭建零耦合架构从MVC开始 Hybird 搭建后端Koa.js并过度到MVVM Hybird 搭建前端Vue.js并升级至MVP Hybird 搭建路由Router实...…

移动开发继续阅读