文档章节

开发随笔记录

A
 AAAAdler
发布于 2015/10/22 10:24
字数 2720
阅读 98
收藏 2
点赞 0
评论 0

1.

昨天别人给了同事一个简单的demo,问题是UITableViewController上有内有textfield的cell,在textfield被选中,弹出键盘,界面会自动滚动,就和我们平时做textfield输入时不要被键盘挡住那样的滚动。对方是想要不要这个自动滚动,因为那个demo滚动的位置不对,查了一下,发现不知道什么时候起,UITableViewController自动适配了这个需求,完成不滚动的要求只有两个做法:(1)把UITableViewController换成UIViewController;(2)重载viewWillAppear方法,但不要继承[super viewWillAppear]

 

2.

大家都知道字典类NSDictionary和NSMutableDictionary在写入的时候,value对应的key只要实现copy协议就可以。上次碰到要把一个字典的数据存到沙盒的plist表里,发现写入失败。做了些对比后发现,因为字典类中,有一个key是number类型的,而plist表去查看source code,发现它的key都是同种类型,string的,所以写入失败。

 

3.

在CoreText里获取文本被点击位置的文本索引,一般用的是方法CTLineGetStringIndexForPosition,但多次测试会发现,你点某一个字的前半部分,输出是前一个字的索引,点击后半部分才输出正确,看了下官方文档里的解释,大概意思是这个方法是将点击位置转换为最近的字符插入处(其实就是光标),所以才会造成这样。修正的代码应该是这样的:

CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
CGFloat glyphStart;
CTLineGetOffsetForStringIndex(line, idx, &glyphStart);
if (relativePoint.x < glyphStart && idx) {
   --idx;
}

 

4.

CALayer类有个方法,检测某个点是否是在它的区域中

/* Returns true if the bounds of the layer contains point 'p'. */
- (BOOL)containsPoint:(CGPoint)p;

方法上面是文档里的说明,用的是layer的bounds属性来比对点p是否在其中,我们都知道bounds属性的坐标是原点,所以使用这个方法,需要把点p先用以下方法转换到要检测是layer坐标系中,再进行检测:

- (CGPoint)convertPoint:(CGPoint)p fromLayer:(CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(CALayer *)l;

 

5.

关于CALayer,网上一直可以看到这样一段描述:

UIView的layer树形在系统内部,被系统维护着三份copy(这段理解有点吃不准)。
第一份,逻辑树,就是代码里可以操纵的,例如更改layer的属性等等就在这一份。
第二份,动画树,这是一个中间层,系统正在这一层上更改属性,进行各种渲染操作。
第三份,显示树,这棵树的内容是当前正被显示在屏幕上的内容。
这三棵树的逻辑结构都是一样的,区别只有各自的属性。

或者是这样一个很简单的解释:

UIView的layer树形在系统内部,被维护着三份copy。分别是逻辑树,这里是代码可以操纵的;动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;显示树,其内容就是当前正被显示在屏幕上得内容。

具体是怎样的,没明白,自己找资料,发现有这样两个属性:

- (id)presentationLayer;
- (id)modelLayer;

第一个属性是显示树或者呈现树,第二个属性是模型树。叫法有不同,有叫逻辑树,动画树和显示树,也有叫呈现树,模型树和渲染树。对这两个方法获得的layer,修改其属性是无效的。

呈现树是我们在显示在屏幕上所看到的layer,所以如果下面的测试代码在viewDidLoad里输出这一层,会看到是空的,因为这时候还没有显示在屏幕上。

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    _colorLayer = [[CALayer alloc] init];
    _colorLayer.bounds = CGRectMake(0, 0, 10, 10);
    _colorLayer.position = CGPointMake(_centerView.bounds.size.width / 2, _centerView.bounds.size.height / 2);
    _colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    
    [_centerView.layer addSublayer:_colorLayer];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showLogOut) userInfo:nil repeats:YES];
    [_timer fire];
    
}

- (void)showLogOut {
    
    CALayer* layer = _colorLayer.presentationLayer;
    CALayer* layer1 = _colorLayer.modelLayer;
    NSLog(@"present layer %@", layer);
    NSLog(@"%f-------%f", layer.position.x, layer1.position.x);
}

- (IBAction)doAnimation:(id)sender {
    
    CGPoint point = CGPointMake(_centerView.bounds.size.width / 2, _centerView.bounds.size.height / 2);
    
    CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(point.x + 100, point.y)];
    animation.duration = 10;
    [_colorLayer addAnimation:animation forKey:nil];
    
}

上面的测试代码是做了一个向右平移100的动画,输出可以看出,presentationLayer的属性跟随动画的变化而变化,记录各个时期的layer的状态,但modelLayer却一直不变。动画结束后,就是layer平移到了指定位置后,会自动返回原始的位置,这就是modelLayer所记录初始状态的作用。

还有一点需要注意,上面的定时器输出还输出了 presentationLayer ,而 presentationLayer 每次都是不一样的。

上面一直没提到的渲染树(或者是动画树)是私有的,我们无法访问,渲染树对呈现树的数据进行渲染,为了不阻塞主线程,渲染树的行为都是在其他线程上进行的。

 

6.NSTimer注意事项

NSTimer会自动retain一次target和userinfo的参数,所以在NSTimer不用的时候,要先invalidate,再把NSTimer置nil,而这两步操作不能在dealloc,否则dealloc永远不会执行,因为无法释放。

NSTimer在滑动视图的时候,是停止执行的,因为runloop让给了UITrackingRunLoopMode,想要执行需要加上这个

[[NSRunLoop currentRunLoop] addTimer:_myTimer forMode:NSRunLoopCommonModes];

NSTimer在非主线程的线程是不执行的,除非加入下面的代码

[[NSRunLoop currentRunLoop] addTimer:_myTimer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];

 

7.Block

我们都知道,block内部是不能修改外部变量的,如果要修改,需要加上__block。

上面的描述,其实不对,block是不能修改外部的局部变量,但是对于属性和成员变量,是可以修改的。这是为什么呢?

简单来说,普通的局部外部变量,是分配到栈上的,而__block所修饰的变量,会自动复制到堆上。

参考1:http://chun.tips/blog/2014/11/13/hei-mu-bei-hou-de-blockxiu-shi-fu/

参考2:https://www.zhihu.com/question/39980914

 

8.引用计数相关

retainCount 是引用计数,指的是对某一块内存地址的使用情况,如果为0,代表没有使用了,可以释放。

但是比如说 

MyClass* a = [[MyClass alloc] init];

a 是一个指针对象,即 a 这个内存地址里的内容,是存储另一片内存地址,那么 retainCount 是指那一片内存地址的使用情况?

MyClass* aObj = [[MyClass alloc] init];

MyClass* bObj = [aObj copy];

MyClass* cObj = [aObj retain];

NSLog(@"count %d  %d %d", aObj.retainCount, bObj.retainCount, cObj.retainCount);
NSLog(@"%p  %p  %p", aObj, bObj, cObj);
NSLog(@"%p  %p  %p", &aObj, &bObj, &cObj);

跑完上面的测试代码可以得到结果, &aObj, &bObj, &cObj 三者是对应三个对象的内存地址,各不相同,但 aObj, bObj, cObj 是三者的内容,a 和 c 相同, 和 b 不同,所以 retainCount 是指内容的地址。

还有一个情况

NSString* aStr = @"1";
NSString* bStr = [aStr retain];
NSString* cStr = [aStr copy];
NSString* dStr = [aStr mutableCopy];
    
NSLog(@"%p  %p  %p  %p", aStr, bStr, cStr, dStr);
    
NSLog(@"%d  %d  %d  %d", aStr.retainCount, bStr.retainCount, cStr.retainCount, dStr.retainCount);

输出的结果是,a, b, c 都是指向同一片内存,而 d 不同,引用计数上,a, b, c 都是 -1,d 是 1 。

 

9.tableViewHeaderView的高度(tableFooterView同理)

用纯代码设置tableViewHeaderView的话,是没有啥问题的,但是从xib中加载一个view,再指定这个view作为tableHeaderView的话(代码如下),高度展示上就会不合要求。

MYHeaderView* headerView = [[[NSBundle mainBundle] loadNibNamed:@"MYHeaderView" owner:self options:nil] lastObject];
CGRect frame = headerView.frame;
frame.size.height = 100;
headerView.frame = frame;
tableView.tableHeaderView = headerView;

结果是会挡住开头的cell。原因未知,解决的办法有这两种:

1.在外面代码生成一个view做为容器,把从xib加载的加到这个容器中,把容器做为tableHeaderView

UIView* headerContentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, 100)];

MYHeaderView* headerView = [[[NSBundle mainBundle] loadNibNamed:@"MYHeaderView" owner:self options:nil] lastObject];

[headerContentView addSubview:headerView];

tableView.tableHeaderView = headerContentView;

2.在MYHeaderView(xib对应的类)中,重写layoutSubviews方法,指定这个tableHeaderView的宽高

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    self.bounds = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, 100);
}
 
附:这个要配合第一段代码设置好从xib加载出来的view的高度

 

10.textField的KVO监听输入

本来想用KVO来监听UITextField的键盘输入

[textField addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"change");
}

却发现上面的观察,在键盘输入中是不会触发的,除非手动设置textField.text,一时不解是为什么,查了资料,最终得到个提示,用runtime遍历出UITextField的所有成员变量

unsigned int outCount;
Ivar *IvarArray = class_copyIvarList([UITextField class], &outCount);//获取到UITextField中的所有成员变量
for (unsigned int i = 0; i < outCount; i ++) {
    Ivar *ivar = &IvarArray[i];
    NSLog(@"第%d个成员变量:%s,类型是:%s",i,ivar_getName(*ivar),ivar_getTypeEncoding(*ivar));// 依次获取每个成员变量并且打印成员变量名字和类型
}

发现其中没有text对应的成员变量,但是有几个label

第30个成员变量:_displayLabel,类型是:@"UITextFieldLabel"
第31个成员变量:_placeholderLabel,类型是:@"UITextFieldLabel"
第32个成员变量:_dictationLabel,类型是:@"UITextFieldLabel"
第33个成员变量:_suffixLabel,类型是:@"UITextFieldLabel"
第34个成员变量:_prefixLabel,类型是:@"UITextFieldLabel"
第36个成员变量:_label,类型是:@"UILabel"

如果设置过textField的text或者placeholder,用object_getIvar根据成员变量进行输出,会看到_displayLabel_placeholderLabel会有对应的值,但是如果从键盘输入的话,是不会有对应的值的。因此,在不确定被观察者内部构造的情况下,不适宜使用KVO。

 

11.新线程中更新UI

一般我们如果有某些耗时的操作,比如图片合成之类的,我们会把这个操作新开一个线程去做,完成后异步调用主线程去赋值。但如果在不调用主线程去赋值,直接在新线程中赋值,是得不到更新的。这是因为在新线程中,无法读取到当前的图像上下文的原因。

- (void)drawRect:(CGRect)rect {
    dispatch_queue_t colorQueue = dispatch_queue_create("testMyNewColor", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(colorQueue, ^{
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
        CGContextFillRect(context, self.bounds);
    });
}

上面这段测试代码,在运行后,控制台就会报出一些内容,告诉你invalid context,因为在这里的context是空的,去掉外层的新线程就没问题。

ps:当然,对于UI的某些更新是可以的,比如更新Button的title这样的操作,但是这似乎没太多意义。

 

12.hiddenWhenPush

之前只知道这个属性,设置后,在push的时候,会隐藏tabbar,之后push的vc都是隐藏tabbar的。今天调试一个bug发现,由于我把设置hiddenWhenPush = YES的那个vc从viewControllers的堆栈中移除掉,导致后面再push的vc都显示了tabbar。

也就是说每次push出下一个vc的时候,都会去遍历堆栈中,看是否hiddenWhenPush = YES来决定是否隐藏tabbar。

© 著作权归作者所有

共有 人打赏支持
A
粉丝 4
博文 21
码字总数 23250
作品 0
广州
在GridView列表中使用图片显示记录是否包含附件

在我的前面很多文章中,都介绍过通用附件模块的管理,本篇随笔主要介绍在一些应用模块中的列表展示中,包含附件的记录,在GridView列表界面中使用图标来快速显示是否有附件的情况。 1、通用附...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform开发框架中工作流模块之申请单草稿处理

在我们开发工作流模块的时候,有时候填写申请单过程中,暂时不想提交审批,那么可以暂存为草稿,以供下次继续填写或者提交处理,那么这个草稿的功能是比较实用的,否则对于一些填写内容比较多...

walb呀 ⋅ 2017/12/04 ⋅ 0

结合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程

1、bootstrap-fileinpu的简单介绍 在前面的随笔,我介绍了Bootstrap-table表格插件的具体项目应用过程,本篇随笔介绍另外一个Bootstrap FieInput插件的使用,整合两者可以实现我们常规的Web...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示

一直以来,都希望整合一个以客户为中心的平台,有两个方面的考虑:一是实现客户数据、客户关系、客户管理等方面的整合,以便更好利用好客户的相关资源,发挥最大的营销效益;二是整合目前我的...

walb呀 ⋅ 2017/12/04 ⋅ 0

基于Metronic的Bootstrap开发框架经验总结(18)-- 在代码生成工具Database2Sharp中集成对Bootstrap-table插件的分页及排序支持

在我们开发系统界面,包括Web和Winform的都一样,主要的界面就是列表展示主界面,编辑查看界面,以及一些辅助性的如导入界面,选择界面等,其中列表展示主界面是综合性的数据展示界面,一般往...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform开发框架中的内容及文档管理模块功能介绍

在开发项目的时候,我们有一些场景需要编辑一些HTML文档,作为内容发布系统的一部分,有时候也需要对一些文档如WORD文档进行编辑管理,这样需要我们对这些内容及文档进行合适的管理。本文主要...

walb呀 ⋅ 2017/12/04 ⋅ 0

在.NET项目中使用PostSharp,使用CacheManager实现多种缓存框架的处理

在前面几篇随笔中,介绍了PostSharp的使用,以及整合MemoryCache,《在.NET项目中使用PostSharp,实现AOP面向切面编程处理》、《在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理》...

walb呀 ⋅ 2017/12/04 ⋅ 0

Winform界面中实现菜单列表的动态个性化配置管理

在我们一般的应用系统里面,由于系统是面向不同类型的用户,我们所看到的菜单会越来越多,多一点的甚至上百个,但是我们实际工作接触的菜单可能就是那么几个,那么对于这种庞大的菜单体系,寻...

walb呀 ⋅ 2017/12/04 ⋅ 0

评价体系的树结构

1、数据准备 这里,我用文件存储了一些数据,如果在真实开发环境下,可以用数据库存储。 他们的字段分别是:selfId(自己本身ID),praentId(父类ID),childId(子类ID),depth(深度,默认为0...

Java_Coder ⋅ 2015/11/02 ⋅ 0

基于Metronic的Bootstrap开发框架经验总结(17)-- 使用 summernote插件实现HTML文档的编辑和图片插入操作

在很多场合,我们需要在线编辑HTML内容,然后在页面上或者其他终端上(如小程序、APP应用等)显示,编辑HTML内容的插件有很多,本篇介绍基于Bootstrap的 summernote插件实现HTML文档的编辑和...

walb呀 ⋅ 2017/12/04 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

HiSDP —— 高效的C++软件开发平台

目前阿里集团每天有近1000PB的数据是通过LogAgent采集的,为了让LogAgent做到资源占用节省和高效采集,背后是基于HiSDP去构建的。 缘由 当决定采用C++编程语言去开发一个软件时,紧接着所面临...

阿里云云栖社区 ⋅ 刚刚 ⋅ 0

zookeeper-3.4.12 下载与安装教程

一、zookeeper下载地址 http://mirrors.hust.edu.cn/apache/zookeeper/ 二、启动教程 把压缩包放在指定目录下 第三: 进入 conf文件夹底下 zoo_sample.cfg 文件名改成 zoo.cfg 第四步: 进入b...

泉天下 ⋅ 2分钟前 ⋅ 0

Oracle 中文日期转换

SELECT TO_date('2011年11月11日', 'yy"年"mm"月"dd"日"') FROM DUAL; 1. Oracle无法识别中文格式,所以添加双引号。 2. 后面的格式是指字符串在转换前的格式,而不是指转换后的格式。...

名侦探柯南 ⋅ 3分钟前 ⋅ 0

MySell:API Spring Boot

起步 类目 商品 订单

BeanHo ⋅ 5分钟前 ⋅ 0

Spring方法拦截器MethodInterceptor

参考资料 1、Spring方法拦截器MethodInterceptor 2、Sharding JDBC源码分析-JdbcMethodInvocation类的作用

哎小艾 ⋅ 9分钟前 ⋅ 0

正则表达式

元字符 元字符,又叫字符集,就是用一些特殊符号表示特定种类的字符或位置。 匹配字符 . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 匹配...

wangchen1999 ⋅ 9分钟前 ⋅ 0

数据库数据导入Elasticsearch案例分享

基于bboss持久层和bboss elasticsearch客户端实现数据库数据导入es案例分享(支持各种数据库和各种es版本) 1.案例对应的源码 https://gitee.com/bboss/bboss-elastic/blob/master/bboss-el...

bboss ⋅ 9分钟前 ⋅ 0

动手---sbt(2)

参考 https://blog.csdn.net/leishangwen/article/details/46225587 建立一个chisel_max目录,文件内容如后面所述,现在开始执行命令: joe@joe-Aspire-Z3730:/media/sdb4/download/scala$ c......

whoisliang ⋅ 16分钟前 ⋅ 0

纯js实现最简单的文件上传(后台使用MultipartFile)

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>XMLHttpRequest上传文件</title> <script type="text/javascript"> //图片上传 var xhr......

孟飞阳 ⋅ 21分钟前 ⋅ 0

iOS宇宙大战游戏、调试工具、各种动画、AR相册、相机图片编辑等源码

iOS精选源码 日期时间选择器,swift Space Battle 宇宙大战 SpriteKit游戏源码 LLDebugTool - 便捷的IOS调试工具(新增截屏功能) 相机扫描or长按识别二维码、FMDB、键盘动态高度、定位等 动画...

sunnyaigd ⋅ 22分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部