2015年10月6日星期二

如何把设计图自动转换为iOS代码? 在线等,挺急的! - iOS122

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
如何把设计图自动转换为iOS代码? 在线等,挺急的! - iOS122  阅读原文»

这是一篇可能略显枯燥的技术深度讨论与实践文章.如何把设计图自动转换为对应的iOS代码?作为一个 iOS开发爱好者,这是我很感兴趣的一个话题.最近也确实有了些许灵感,也确实取得了一点小成果,和大家分享一下.欢迎感兴趣的iOS爱好者能和我一起研究讨论!

这是一个可以节省 70% 工作量的话题

我觉得,如果真的能把一张设计图自动转换为代码,任何开发工程师都会感兴趣的.单以 iOS 应用为例, 在一个最常用的MVC架构的APP中,主要的代码,无非就是集中于: M 的网络请求部分, V的数据显示部分, C的逻辑交互部分.对于controller控制器层,往往需要结合业务逻辑去处理,代码量并不算大;对于Model数据模型层,我们有 AFNetworing, RestKit, MJExtension等,可以大大简化网络接口到数据模型的转换;对于View视图层,代码最繁杂,最枯燥无趣,迭代最让人头疼的部分,又有什么可以凭借呢?我没有详实的数据统计来确认各个iOS开发者的日常开发中,MVC各个层面,具体的时间成本如何;单从我个人角度来说, View布局的拆分与转换,占据了我 70% 以上的时间.我们公司通常是按单个完整任务来拆分工作的,单个任务的MVC三层,都是应该由一个人独立完成.每次都把大把时间浪费在"画UI"上,真的感觉好无趣,好浪费生命;临时遇到产品经理改动需求,可能一个对方看似更加"合理"的改动,我这边几乎要大动干戈!我想我对编程本身确实是感兴趣的,但是整天浪费时间在 UI上,真的感觉有点虚度光阴.所以说,在本不充裕的空闲里,我一直在思考的一个命题就是: 如何实现 UI 的自动化与独立化.

过往的尝试: 基于Xib的视图模块化.

尽管作为一名iOS开发人员,我依然对苹果公司提供的开发技术及其发展方向持谨慎和保守态度.前一段时间,尝试使用 Xib来布局视图,遇到一些坑,但是熟悉之后,也确实比原来单纯基于绝对位置的纯代码布局更灵活些,也更快捷些.在此期间,我研究的一个重要话题就是如何实现Xib之间的嵌套复用,即在一个Xib上如何直接嵌入另一个Xib.乍听起来很简单,但是在亲身实践之后,才发现其难度.我不是来吐槽的,个中曲折不再一一赘述,下面是我研究的成果:

XIB效果图

上图,是一个Xib模块,其中的色块部分,嵌套的是另一个Xib模块.最终显示是,色块会自动被对应的Xib模块替代.

核心代码如下:

//
// MCComponent.h
// iOS122
//
// Created by 颜风 on 15/7/5.
// Copyright (c) 2015年 iOS122. All rights reserved.
//

#import "MCConstants.h"

/**
* 可复用组件.用于编写可嵌套的 xib 组件.
*
* 适用场景: 需要静态确定布局的页面内的UI元素的复用性问题.
* 使用方法: 在xib或storyboard中,将某一用于占位的view的 custom class 设为对一个的 component, 则初始化时,会自动使用此component对应的xib文件中的内容去替换对应位置.
* 注意: 对于可动态确定布局的部分,如tableView中的cell,直接自行从xib初始化即可,不必继承于 MCComponent.
*/
@interface MCComponent : UIView

@property (strong, nonatomic) UIView * contentView; //!< 真正的内容视图.
@property (weak, nonatomic, readonly) UIViewController * viewController; //!< 当前视图所在的控制器.
@property (weak, nonatomic, readonly)NSLayoutConstraint * heightContronstraint; //!< 高度的约束.不存在,则返回nil.
@property (strong, nonatomic) id virtualModel; //!< 虚拟model.用于测试.默认返回nil.当不为nil,优先使用它.
@property (strong, nonatomic) id model; //!< 视图数据模型.内部会自动根据virtualModel的值,进行不同的处理.
@property (assign, nonatomic, readonly) BOOL isTest; //!< 是否是测试.如果是,将优先使用 virtualModel来替换model.系统内部处理.默认为NO.

/**
* 初始化.
*
* 子类需要继承此方法,以完成自定义初始化操作. 不要手动调用此方法.
*/
- (void)setup;

/**
* 重新加载数据.
*
* 子类可根据需要,具体实现此方法.
*/
- (void)reloadData;


/**
* 返回上一级.
*/
- (void) back;

/**
* 便利构造器.子类应根据需要重写.
*
* @return 默认返回self.
*/
+ (instancetype)sharedInstance;

/**
* 更新视图.
*
* 子类应根据需要重写此方法.默认不做任何处理.
*/
- (void) updateView;

@end
//
// MCComponent.m
// iOS122
//
// Created by 颜风 on 15/7/5.
// Copyright (c) 2015年 iOS122. All rights reserved.
//

#import "MCComponent.h"

@interface MCComponent ()
@end
@implementation MCComponent
@dynamic virtualModel;
@synthesize model = _model;

- (instancetype)init
{
self = [super init];

if (nil != self) {
[self mcSetup: NO];
}

return self;
}

- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame: frame];

if (nil != self) {
[self mcSetup: NO];
}

return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];

if (nil != self) {
[self mcSetup: YES];
}

return self;
}

/**
* 是否从xib初始化此类.
*
* @param isFromXib 是否从xib或sb初始化此类.
*
* 注意: 无论此类是否从xib或sb初始化,组件内部都将从xib文件初始化.
*
* @return 实例对象.
*/
- (instancetype) mcSetup: (BOOL) isFromXib
{
UIView * contentView = [[[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options:nil] firstObject];
self.contentView = contentView;

contentView.translatesAutoresizingMaskIntoConstraints = NO;

// 这一句,是区别初始化方式后的,核心不同.
self.translatesAutoresizingMaskIntoConstraints = ! isFromXib;

[self addSubview: contentView];

self.backgroundColor = contentView.backgroundColor;

if (nil == self.backgroundColor) {
self.backgroundColor = ;
}

[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeLeft multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeRight multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeTop multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeBottom multiplier: 1.0 constant: 0]];

[self setup];

return self;
}


- (void)setup
{
/* 子类需要继承此方法,以完成自定义初始化操作. */
}

- (void)reloadData
{
/* 子类根据需要,自行实现. */
}

- (UIViewController*)viewController {
for (UIView* next = [self superview]; next; next = next.superview) {
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController*)nextResponder;
}
}
return nil;
}


- (void)back
{
if (nil != self.viewController.navigationController) {
[self.viewController.navigationController popViewControllerAnimated: YES];
}
else{
[self.viewController dismissViewControllerAnimated: YES completion:NULL];
}
}

- (NSLayoutConstraint *)heightContronstraint
{
__block NSLayoutConstraint * heightCons = nil;
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint * obj, NSUInteger idx, BOOL *stop) {
if (NSLayoutAttributeHeight == obj.firstAttribute && nil == obj.secondItem && [obj.firstItem isEqual: self]) {
heightCons = obj;

* stop = YES;
}
}];


return heightCons;
}

+ (instancetype)sharedInstance
{
/* 子类应根据需要重写这个方法. */
return nil;
}

- (id)virtualModel
{
return nil;
}

- (void)setModel:(id)model
{
_model = model;

// 更新视图.
[self updateView];
}

- (id)model
{
id model = _model;

if(YES == self.isTest){
model = self.virtualModel;
}

return model;
}

- (void)updateView
{
/*子类应根据需要重写此方法.默认不做任何处理.*/
}


- (BOOL)isTest
{
/* 子类应根据自己需要,重写这个方法. */
return NO;
}
@end

你的Xib视图组件,应该由一个 MCComponent的子类的.h/.m与一个同名的 .xib 文件组成,如MCTextComponent.h, MCTextComponent.m, MCTextComponent.xib.此时应把XIB的File's Owder与自定义的MCComponent关联起来.按照以上步骤,即可实现图示效果.

此策略已经在我们的项目中试用了一段时间,也已经填了些坑,多次优化,感兴趣的可以直接拿过去用.但是,基于XIB的视图模块化,终究还是需要手动的参与,对工作效率的提升也似乎达到了一个极限:因为它终究需要人工深度参与.关于它的讨论,暂时到此为止.

目前的探索: 基于 masonry 的视图模块化

masonry,是一个基于纯代码的AutoLayout库.初次涉及时,只是感觉它很方便,既有Xib的易读性,又有纯代码的灵活性.试用一段时间之后,突然想到: 或许借助masonry,建立一个纯代码的不依赖Xib的AutoLayout视图组件机制.

目前能得到的效果

  • 视图基于 AutoLayout;
  • 视图自动适配不同屏幕尺寸;
  • 视图完全独立于数据与业务逻辑;
  • 视图严肃仅与父视图有位置关系;
  • 可以将视图模块的元素与模块同名属性自动关联;
  • 仅需知道父视图的宽高,模块内某一个UI元素的宽高, UI元素的 bottom 与 right, 就可以唯一确定任意元素的位置.
2015/10/6 iOS 笔记 细节 应用中常见文件 - 传说中的龙哥  阅读原文»

1,工程名-info.plist文件

bundle display name 应用显示的名称(10到12个字符,超过显示...)

bundle identifier 应用的唯一标识 com.xx.hhxx

bundle version 软件版本号

supported interface orientation 屏幕旋转 默认支持三种模式

2,应用中常见文件 工程名-Prefix.pch (新版没有这个文件了)

pch头文件的内容能被项目中得其他所有源文件共享和访问

一般在pch头文件中定义一些全局的宏

在pch文件中添加下列预处理指令,然后在项目中使用log(...)来输出日志信息,

就可以在发布应用的时候,一次性将NSLog语句移除(在调试模式下,才有定义DEBUG)

#ifdef __OBJC__

#import <UIKit/UIKit.h>

#import <Foundation/Foundation.h>

#define age 28

#define serverTel @"13531234234"

#import "HMPerson.h"

//调试模式

#ifdef DEBUG

#define HMLog(...) NSLog(__VA_ARGS__)

#else

//发布模式(release)

#define HMLog(...)

没有评论:

发表评论