Xcode7 创建自定义模板
在一个多人开发的项目中,相同的代码风格,不仅增强了易读性,而且调理清晰,容易理解,在后期维护过程中,也容易修改。
因此就想写一个模版。
当然,并不是说用了自定义的模版就一定是好的,有些方法可能是我们不需要的,不想暴漏出来,那么我们可以用代码块的形式去处理。
具体使用,可查看如下连接:
http://www.jianshu.com/p/93527682d8d3
虽然有些许的错误,但是整体流程是OK的。
处理过程
接下来,看处理过程:
获取模版路径
系统模版的路径为~/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates,File Templates下包含Apple Watch,Resource,Source,User Interface,四个目录,对应了新增文件时,iOS下的几个分级目录。比如,我们想查看CocoaTouch下对应的内容,我们可以看Cocoa Touch Class.xctemplate这个模版。
通常情况下,我们会在Source下新增新的模版。
将 MSTemplate.xctemplate文件夹放到Source系统模板文件夹中,就会在New File时出现在对应的选项中。 在往里面copy文件时,系统会提示你输入管理员密码,输入密码进行处理。
template内容介绍
MSTemplate.xctemplate 处理的是对UIViewController的模块修改。MSTemplate.xctemplate文件夹包含了
假设已经将MSTemplate.xctemplate放入了Source文件夹中,查看MSTemplate.xctemplate中有TemplateIcon.png,TemplateIcon@2x.png,TemplateInfo.plis以及UIViewControllerXIBObjective-C文件夹,UIViewControllerXIBObjective-C文件夹内包含三个文件
FILEBASENAME.h,FILEBASENAME.m, FILEBASENAME.xib。
(1)UIViewControllerObjective-C 文件夹
用来创建.h 和 .m文件。 其中文件夹的命名规范是[name]+Objective-C. 如果是创建swift修改为swift。
(2)UIViewControllerXibObjective-C文件夹
用来创建.h,.m和.xib文件。其中文件夹的命名规范是[name]+XibObjective-C. swift类似。
(3)TemplateIcon图片
图片是用来显示在New File的菜单上的。任意放一个自己喜欢的图片,像素138*138即可。
(4)TemplateInfo.plist配置文件。下面单独讲讲。
头文件和实现的说明。
___FILEBASENAME___.h代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
___FILEBASENAME___.m 代码如下
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
33
34
35
36
37
38
39
40
41
42
43
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
}
#pragma mark btn event
//back button clicked
- (void)doBack:(UIButton *)sender{
}
@end
xib 文件可参考系统模版去制作一个。
TemplateInfo.plist
查看简书的描述信息。
主要设置三个值,父类,子类的默认名,是否打开xib。
使用。
创建一个子类,使用MSTemplate,可快速创建一个带有自定义注释子类。
新增模版常见问题说明
目录查找错误。
~/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates,注意Platforms而不是Developer下的Libraty。
新建模版是显示错误位置。
可能加入到MAC OS上去了,注意看自己在哪个部分新增模版。
模板新增成功,但是新增子类时新增不上去。
新增成功,但是子类加不到prj上,可能是对应的xib文件没有,或者文件没有书写完成,可从系统的template复制一份,再修改代码来实现。
系统模版的修改。
这个须特别注意,系统提供的模版不能编辑,没有写的权限,不能在原来的基础上修改。
知识点汇总(五)
基于CALayer 的动画实现
需求:
想要动态的画出一个水波纹。
思路:
基于CALayer画出基本的样式,结合动画实现动态水波纹的效果。
问题:
CABasicAnimation 的基本动画只有以下几组:
1 | animationWithKeyPath的值: |
特别注意,frame的设置动画是无效的,它的变动影响了bounds以及center的变化。
那么,我想要给一个自定义的属性添加动画怎么处理?
查看文档,发现一个方法:
1 | /* Method for subclasses to override. Returning true for a given |
这个方法可以预处理CALayer的属性,若不想识别这个属性,可返回NO,否则返回YES,其余默认的属性,可按照父类的返回返回。
具体实现
.h 的实现
1 |
|
.m内的具体实现
1 | #import "Coslayer.h" |
动画调用
1 |
|
CABasicAnimation 初始化实例对象时要求传入keyPath,这个就是对应的路径。
上述例子就是指offsetAngle从0到2π的过渡,过渡值为π,过渡时间为0.6,不自动转回。
repeatCount在api中已经说明,是动画的次数,若想设置为无限次的重复动画值为HUGE_VALF。
知识点汇总(四)
runtime 为系统类新增property
动态为系统类新增属性,以普通属性的方式进行访问。
事例如下,为UIView新增viewHegiht的方法。
1. 新增一个Category,文件名为UIView+Frame.h,UIView+Frame.m.
2. 在.h文件添加属性
1 | @property (nonatomic) CGFloat viewHeight; |
3. 在.m文件添加get,set方法以及实现
导入库:
1 | #import <objc/runtime.h> |
声明静态常量:
1 | static NSString const *playloadObject = @"playloadObject"; |
添加IMP:
1 | - (CGFloat)viewHeight{ |
set方法内调用runtime的方法objc_setAssociatedObject实现值与地址的绑定,注意四个参数,第一个为obj对象,第二个为参数的关联地址,第三个是id类型的对象,最后一个为objc_AssociationPolicy 。
get方法内调用runtime的方法objc_getAssociatedObject获取数值,注意返回值的内容是id类型,可自行转化为自己想要的类型。
4. 结束
以上过程完成了对UIView 的扩展,实现返回view的高度。可参照实现方法实现对类的属性的封装。
知识点汇总(二)
runtime消息转发的处理
代码在执行某一个方法时,代码执行performSelector的动作,在runtime上调用方法objc_msgSend方法(注意类方法的调用是objc_msgSendClass),执行过程如下:
1.objc_msgSend(target, selector)
target为receiver,selector为执行的方法。
Attention!!!
继承自NSObject的类都集成了一个类方法。
+(Bool)resolveInstanceMethod:(SEL)selector的方法,内部实现机制为获取实例对象的方法列表,若有,返回true,若无,返回false。
- 当为true时,进入Event Handle处理事件;
- 当返回为false时,说明没有实现这个方法或者返回为nil,消息进入转发Method Forwarding (消息转发);
2. - (id)forwardingTargetForSelector:(SEL)aSelector
aSelector 待处理的消息,返回一个对象,用于处理消息。
继承自NSObject的类都集成了一个实例方法。
-(id)forwardingTargetForSelector:(SEL)aSelector的方法,当类不识别消息时,执行该方法, 返回一个对象,用于处理aSelector;
- 当为true时,将由返回的object处理事件进入objc_msgSend,继续执行,进入Event Handle处理事件;
- 当返回为false时,说明没有实现这个方法或者返回为nil,使用最后一个挽救的方法,Normal Forwarding(正常转发);
3. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
消息信号,aSelector 为消息内容。
继承自NSObject的类都集成了一个实例方法。
若是类对于方法有IMP,那么调用父类的该方法就有非nil的返回,若是无IMP,则获取为nil,此时可以把把别的已经实现该方法的类的对象的信号返回,即可实现消息转发;
- 当为true时,说明该类可以处理该方法;
- 当为false时,说明无实现,可自行在子类实现该方法,转发消息;
- 当为false时,且并不转发消息,那么Runtime会抛出donotRecogniseSelector的异常,程序终止运行。
4. - (void)forwardInvocation:(NSInvocation *)anInvocation
invoke NSInvocation,让target执行对应的selector.
在子类中可实现该方法,修改anInvocation的target将消息转发出去。
代码示例
为了学习并补充说明该功能,做了一个demo,展示,代码如下:
1 | #import "ViewController.h" |
我在MViewStage 执行[self performSelector:@selector(doSomething)],但是并没有定义或者实现这个方法,因此我是用如下两种解决方法:
第一种:forwardingTargetForSelector 返回了self.parentViewController;
第二种: 注销掉forwardingTargetForSelector的实现,或者返回为nil,实现了methodSignatureForSelector和forwardInvocation方法,分别返回了self.parentViewController的methodSignatureForSelector,并在forwardInvocation执行了切换target.
以上两种均OK,都可以实现消息的转发。
知识点汇总(一)
NSObject的class方法。
1 |
|
如图所示,我想打印出继承自UIViewController的一个子类MViewStage,打印出self.class,super.class,self.superClass,这三个的值,按照之前的认知,第一个和最后一个毋庸置疑,分别是,MViewStage和UIViewController,只有第二个,super,我认为要打印的是super的class,应该是UIViewController。但是打印结果如下:
1 | 2016-07-18 13:35:36.139 RunTimeLearning[6892:79426] self.class = MViewStage |
很明显super.class打印出的内容是当前对象的类MViewStage。
!!!为什么呢?
API中查看可以看到如下描述信息:
1 |
|
可以看出,class返回的类是当前selector的接受者,上例所述的MViewStage的对象,因此返回类型为MViewStage。
bug纪录(一)
问题信息描述
发现时间:2016-07-06
现象:
1 | 2016-07-06 18:18:42.237 Pudding[5035:1874119] _BSMachError: (os/kern) invalid capability (20) |
描述:
在UIViewController的viewWillAppear 和 viewDidAppear 之间 在 console 段抛出如下错误,底层信息,造成耗时,耗时时间约为1秒半。
关于解决
出现原因:
可能由于viewWillAppear、viewDidLayoutSubviews、viewDidAppear方法内代码书写不规范早层。所以我采用逐段log、逐段注销的方法,发现viewWillAppear内的一段代码造成此错误的抛出。通常还是在数据量大,subview过多时抛出错误信息。
处理方法:
查看stack overflow 上弄的帖子,看到的解决方案。
采用如下两种方法均可:
(1)async一个main,代码
1 | dispatch_async(dispatch_get_main_queue(), ^{ |
(2) dispatch_after 0.2秒执行代码
1 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
UIContol的高亮状态
1 | @property(nonatomic,getter=isHighlighted) BOOL highlighted; |
高亮状态,highlighted,比如UIButton的高亮,就是当你手按下button按钮时的状态,可以放置两张不同的照片来看一下,很明显就能看到。
通常在对视觉问题的处理上,用到这个属性,但是我们H领导会有这样的习惯,尽可能的少自己创建属性,使用系统本身提供的属性,这次,在做一个UIcollectionViewCell的选中状态显示事件时,他用到了这个属性,他是在cell最顶部的一个小view上使用了这个属性,但是当我们手指按在cell上时,cell的点击事件就被激活,并将整个高亮显示的状态显示在子view上,导致出现,不该显示的时间显示状态的情况。
最初,我在看到这种使用方法的时候,我还是蛮赞叹的,节省了开发的成本,便捷偷巧的使用系统属性,然而后期还是觉得这种讨巧的处理方法,在界面的显示上并不讨巧,或者会造成视觉上的bug,从这个角度上来说,反而加大了开发力度。
故而,写出这篇文章作为备注。警醒一下那些有这个习惯的码工们,也警示自己。