阅读 253

iOS |知识点整理(12)

延续上一篇iOS |知识点整理(11)

使用tintColor来改变按钮中图象的颜色

As of iOS 7, there is a new method on UIImage to specify the rendering mode. Using the rendering mode UIImageRenderingModeAlwaysTemplate will allow the image color to be controlled by the button's tint color.

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [[UIImage imageNamed:@"image_name"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[button setImage:image forState:UIControlStateNormal]; 
button.tintColor = [UIColor redColor];
复制代码

How to delete all cookies of UIWebView?

NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [storage cookies]) {
   [storage deleteCookie:cookie];
}
[[NSUserDefaults standardUserDefaults] synchronize];
复制代码

关于绘图的总结:

  1. 使用Quartz 2D绘图的基本步骤
 1) 获取上下文context(绘制图形的地方)
 2) 设置路径(路径是用来描述形状的)
 3)  将路径添加到上下文
 4)  设置上下文属性(设置颜色,线宽,线性等)
 5)  绘制路径
 6)  释放路径(在Quartz2D中,因为是C语言的框架,所有用Create,Copy字样方法实例化的对象,都需要自行释放)
复制代码
  1. CAShapeLayer设置填充颜色为透明,对于设置遮罩很关键.
// 获取形状的layer
CAShapeLayer *line1 = [CAShapeLayer new];
line1.frame = CGRectMake(0, 0, 200, 200);
line1.fillColor = [UIColor clearColor].CGColor;
line1.strokeColor = [UIColor blackColor].CGColor;
line1.strokeStart = 0.f;
line1.strokeEnd   = 1.f;
line1.lineWidth   = 1.f;
// 从string上获取到CGPath
line1.path = [UIBezierPath pathFromString:@"游贤明"
                                 WithFont:[UIFont fontWithName:CUSTOM_FONT(@"新蒂小丸子体", 0)
                                                          size:60.f]].CGPath;
// 让文字按照正常的顺序显示
line1.bounds = CGPathGetBoundingBox(line1.path);
line1.geometryFlipped = YES;
// 设置颜色渐变layer
CAGradientLayer *colorLayer = [CAGradientLayer layer];
colorLayer.frame = CGRectMake(0, 0, 200, 200);
// 设置遮罩
colorLayer.mask = line1;
复制代码

Check again build settings. Take a look at Debug Information Format, it should have DWARF with dSYM file

aybe, in fact, a dSYM file is generated in other place than the .app? Check environment variables, especially DWARF_DSYM_FOLDER_PATH and DWARF_DSYM_FILE_NAME. To embed the dSYM within the app bundle, just set DWARF_DSYM_FOLDER_PATH to (CONFIGURATIONBUILDDIR)/(CONFIGURATION_BUILD_DIR)/(EXECUTABLE_FOLDER_PATH) and DWARF_DSYM_FILE_NAME to $(EXECUTABLE_NAME).dSYM.

stackoverflow.com/questions/2…

设置UITableViewCell的选中状态

-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{
    [super setHighlighted:highlighted animated:animated];
    if (highlighted) {
        self.contentView.backgroundColor=self.bgColor;
    }
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if(selected){
        self.contentView.backgroundColor=self.bgColor;
        self.contentView.layer.borderWidth=1;
        self.contentView.layer.borderColor=UIColorFromRGB(0x2889c1).CGColor;
    }
    else{
        self.contentView.layer.borderWidth=0;
    }
}
复制代码

给UITableViewCell添加border

CAShapeLayer *shapelayer = [CAShapeLayer layer];
    UIBezierPath *path = [UIBezierPath bezierPath];
    //draw a line
    [path moveToPoint:CGPointMake(0.0, cell.frame.size.height)]; //add yourStartPoint here
    [path addLineToPoint:CGPointMake(cell.frame.size.width, cell.frame.size.height)];// add yourEndPoint here
    UIColor *fill = [UIColor colorWithRed:0.80f green:0.80f blue:0.80f alpha:1.00f];
    shapelayer.strokeStart = 0.0;
    shapelayer.strokeColor = fill.CGColor;
    shapelayer.lineWidth = 1.0;
    shapelayer.lineJoin = kCALineJoinRound;
    shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:3 ], nil];
//    shapelayer.lineDashPhase = 3.0f;
    shapelayer.path = path.CGPath;
    [cell.contentView.layer addSublayer:shapelayer];
复制代码

dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier : forIndexPath

The most important difference is that the forIndexPath: version asserts (crashes) if you didn't register a class or nib for the identifier. The older (non-forIndexPath:) version returns nil in that case.

You register a class for an identifier by sending registerClass:forCellReuseIdentifier: to the table view. You register a nib for an identifier by sending registerNib:forCellReuseIdentifier: to the table view.

If you create your table view and your cell prototypes in a storyboard, the storyboard loader takes care of registering the cell prototypes that you defined in the storyboard.

Session 200 - What's New in Cocoa Touch from WWDC 2012 discusses the (then-new) forIndexPath: version starting around 8m30s. It says that “you will always get an initialized cell” (without mentioning that it will crash if you didn't register a class or nib).

The video also says that “it will be the right size for that index path”. Presumably this means that it will set the cell's size before returning it, by looking at the table view's own width and calling your delegate's tableView:heightForRowAtIndexPath: method (if defined). This is why it needs the index path.

ref: stackoverflow.com/questions/2…

动态的改变UITableViewCell的高度

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    if([self.indexArray containsObject:indexPath]){
        if(self.querytype==nil){
            [self.indexArray removeObject:indexPath];
            [tableView beginUpdates];
            [tableView endUpdates];
        }
    }
    else{
        [self.indexArray addObject:indexPath];
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if([self.indexArray containsObject:indexPath]){
        return self.originalHeight;
    }
    else{
        return _compressHeight;
    }
}
复制代码

关于quartz绘图

  1. 这两个函数CGContextFillPath,CGContextStrokePath使用完之后,直接就把CGPath给清零了,所以如果要既要画边又要填充的话,就要用CGContextDrawPath,如:

CGContextDrawPath(context, kCGPathFillStroke);

Quartz uses the line width and stroke color of the graphics state to paint the path. As a side effect when you call this function, Quartz clears the current path.

  1. CGPathAddArc使用注意方面:
  • 注意角度水平方向为零度
  • 如果想要一个扇形填充,那么要用CGPathMoveToPoint来移动到一个点,再来使用它来画弧.
  1. CGPath表示一系列的绘图动作,它是有方向和前后顺序的,比如说后面添加的CGPath可以覆盖前面添加的CGPath.

由于iOS中的坐标体系是和Quartz坐标体系中Y轴相反的,所以iOS UIView在做Quartz绘图时,Y轴已经做了Scale为-1的转换,因此造成CGPathAddArc函数最后一个是否是顺时针的参数结果正好是相反的,也就是说如果设置最后的参数为YES,根据参数定义应该是顺时针的,但实际绘图结果会是逆时针的!

GPathAddArc(paths, nil, maxX-cornerRadius, miniY+cornerRadius, cornerRadius, -M_PI_2, 0, 0);
//右下角
GPathAddArc(paths, nil, maxX-cornerRadius, maxY-cornerRadius, cornerRadius, 0, M_PI_2, 0);
//左下角
GPathAddArc(paths, nil, miniX+cornerRadius, maxY-cornerRadius, cornerRadius, M_PI_2, M_PI, 0);
//左上角
GPathAddArc(paths, nil, miniX+cornerRadius, miniY+cornerRadius, cornerRadius, M_PI, -M_PI_2, 0);
复制代码

关于使用CoreGraphics来绘图的总结

下面是绘制一个圆形进度条.可以看出,绘图的一般步骤.

1) 获取上下文context(绘制图形的地方)
2)  设置上下文属性(设置颜色,线宽,线性等)
3) 设置路径(路径是用来描述形状的)
4)  将路径添加到上下文
5)  绘制路径
6)  释放路径(在Quartz2D中,因为是C语言的框架,所有用Create,Copy字样方法实例化的对象,都需要自行释放)
复制代码
- (void)drawRect:(CGRect)rect {
    CGContextRef context=UIGraphicsGetCurrentContext();
    CGFloat w=self.bounds.size.width;
    CGFloat h=self.bounds.size.height;
    CGMutablePathRef paths=CGPathCreateMutable();
    //先设置一下圆圈路径的线条颜色及填充颜色
    [[UIColor redColor] setStroke];
    [[UIColor whiteColor] setFill];
    CGPathAddArc(paths, nil,w/2.0f ,h/2.0f,w/2.0f-1 , 0,M_PI*2, 0);
    [[UIColor redColor] setFill];
    [[UIColor redColor] setStroke];
    //这里要移动一下开始位置.
    CGPathMoveToPoint(paths, nil, w/2.0f, h/2.0f);
    //先设置一下进度路径的线条颜色及填充颜色
    if(_percent>=0&&_percent<1){
        CGPathAddArc(paths, nil,w/2.0f ,h/2.0f,w/2.0f-1 ,-M_PI_2,-M_PI_2+(2*M_PI)*_percent+0.0001, 1);
    }
    else{
        CGPathAddArc(paths, nil,w/2.0f ,h/2.0f,w/2.0f-1 , 0,M_PI*2, 0);
    }
    CGContextAddPath(context, paths);
    //这里要用drawPath,因为单独使用strokepath 或 fillpath后,会清空路径,造成只能画线或只能填充
    CGContextDrawPath(context, kCGPathFillStroke);
    CGPathRelease(paths);
}
复制代码

关于Path的transform变换

路径是没有所谓的"position"的,它旋转是围绕着它的origin进行旋转的.
A path doesn't have “a position”. A path is a set of points (defined by line and curve segments). Every point has its own position.

Perhaps you want to rotate the path around a particular point, instead of around the origin. The trick is to create a composite transform that combines three individual transforms: 如果想让它围绕着某点旋转:

1.Translate the origin to the rotation point.
2.Rotate.
3.Invert the translation from step 1.
复制代码

下面使用UIBezierPath来画表盘的刻度,注意观察一下UIBezierPath是如何进行transform的:

UIBezierPath* path=[UIBezierPath bezierPathWithOvalInRect:r];
CGFloat angle=0;
//小时刻度路径
for(int i=0;i<12;i++){
    UIBezierPath* tempath=[UIBezierPath bezierPath];
    CGFloat dh=100+100*cos(angle);
    CGFloat dw=100-100*sin(angle);
    NSLog(@"point:%f-%f",dw,dh);
    [tempath moveToPoint:CGPointMake(dw, dh)];
    [tempath addLineToPoint:CGPointMake(dw, dh+6)];
    CGPoint center=CGPointMake(CGRectGetMaxX(tempath.bounds), CGRectGetMinY(tempath.bounds));
    CGAffineTransform transform = CGAffineTransformIdentity;
    transform = CGAffineTransformTranslate(transform, center.x, center.y);
    transform = CGAffineTransformRotate(transform, angle);
    transform = CGAffineTransformTranslate(transform, -center.x, -center.y);
    [tempath applyTransform:transform];
    //        tempath.CGPath=createPathRotatedAroundBoundingBoxCenter(tempath.CGPath, angle);
    [path appendPath:tempath];
    angle+=2*M_PI/12.0f;
}
复制代码

关于自定义控件的注意事项

1. 要注意在构造器方法做哪些事情
比如在UIView子类的构造器中,我可以初始化数据,添加子view等.

2. 在合适的时机做合适的事情:
比如在UIView的子类A中的,我可以在layoutSubviews中,布局子视图,添加子视图,但要注意不要重复添加.
复制代码

set Http header Fields in Objective C

   NSURL *theURL = [NSURL URLWithString:@"yourURL"];
   NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:theURL      cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0f];

   //Specify method of request(Get or Post)
   [theRequest setHTTPMethod:@"GET"];

    //Pass some default parameter(like content-type etc.)
   [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
   [theRequest setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];

   //Now pass your own parameter

   [theRequest setValue:yourValue forHTTPHeaderField:theNameOfThePropertyValue];

   NSURLResponse *theResponse = NULL;
   NSError *theError = NULL;
   NSData *theResponseData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&theResponse error:&theError];
   NSDictionary *dataDictionaryResponse = [NSJSONSerialization JSONObjectWithData:theResponseData options:0 error:&theError];
   NSLog(@"url to send request= %@",theURL);     
   NSLog(@"%@",dataDictionaryResponse);
复制代码

求UILable的当前高度及宽度.

//  UILabel+DynamicHeight.m
//  Reputation
#import "UILabel+DynamicHeight.h"
@implementation UILabel (DynamicHeight)

- (CGFloat)minHeightForWidth:(CGFloat)width
{
  return [self sizeThatFits:CGSizeMake(width, MAXFLOAT)].height;
}

- (CGFloat)minWidthForHeight:(CGFloat)height
{
  return [self sizeThatFits:CGSizeMake(MAXFLOAT, height)].width;
}
@end
复制代码

关于编译器编译关键字 noreturn 的用法.

A few standard library functions, such as abort and exit, cannot return. GCC knows this automatically. The noreturn attribute specifies this for any other function that never returns.

For example, AFNetworking uses the noreturn attribute for its network request thread entry point method. This method is used when spawning the dedicated network NSThread to ensure that the detached thread continues execution for the lifetime of the application.

缩小leftbarbuttonitem的点击区域

If you want to prevent the click other than the button then add the custom button to UIView then set that view as custom view to the barbuttonItem

Your code will become like this :

UIImage *buttonImage = [UIImage imageNamed:@"prefs"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:buttonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button addTarget:self action: @selector(handleBackButton)
forControlEvents:UIControlEventTouchUpInside];

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height)];
[view addSubview:button];

UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:view];
self.navigationItem.leftBarButtonItem = customBarItem;
复制代码

ref:stackoverflow.com/questions/1…

关于KVO

如果要监测到一个对象属性的所有变化,那么就要在对象已初始化,就立即对其addObserver. 归根结底,还是由于KVO的实现本质,是在运行时动态实现的.

如何禁止调用init方法

- (id)init
{
    [selfdoesNotRecognizeSelector:_cmd];
    return nil;
}

或者raise一个exception,前面也是抛出异常(消息不能被调用的异常)
- (id)init
{
    [NSException raise:NSInternalInconsistencyException
                format:@"You must override %@ in a subclass",NSStringFromSelector(_cmd)];
    return nil;
}
复制代码

使用NSRunLoop防止主线程的阻塞

下面的activity不会转动

- (void)viewDidLoad {
    [super viewDidLoad];
    [_activity startAnimating];
    [self loadURL];
}
-(void)loadURL{
    NSURLResponse* response=nil;
    NSError* error=nil;
    NSURLRequest* r=[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
    [NSURLConnection sendSynchronousRequest:r returningResponse:&response error:&error];
    [_activity stopAnimating];
    NSLog(@"response:%@  error:%@",response,error);
}
复制代码

下面的activity会转动,就是让他在下一个RunLoop里运行startAnimating.

- (void)viewDidLoad {
    [super viewDidLoad];
    [_activity startAnimating];
    [self performSelector:@selector(loadURL) withObject:self afterDelay:0];
}
-(void)loadURL{
    NSURLResponse* response=nil;
    NSError* error=nil;
    NSURLRequest* r=[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
    [NSURLConnection sendSynchronousRequest:r returningResponse:&response error:&error];
    [_activity stopAnimating];
    NSLog(@"response:%@  error:%@",response,error);
}
复制代码

MRC下的Block的copy、retain、release操作

不同于NSObjec的copy、retain、release操作:

  1. Block_copy与copy等效,Block_release与release等效;
  2. 对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
  3. NSGlobalBlock:retain、copy、release操作都无效;
  4. NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是

[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。 NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain; 尽量不要对Block使用retain操作。

注意:MRC中__block是不会引起retain;但在ARC中__block则会引起retain。ARC中应该使用__weak或__unsafe_unretained弱引用。__weak只能在iOS5以后使用. MRC下__block不会增加引用计数.

撤消前面的performSelector调用

//当我前面有如下调用:
[[self shareInstance] performSelector:@selector(dismiss) withObject:nil afterDelay:delay];

//当我再次执行这个方法的时候,我希望撤消上一次的调用,可以这么来.
- (void)dismiss
{
    [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(dismiss) target:self argument:nil];
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismiss) object:nil];
}
复制代码

引用:开源库JKNotifier

dispatch_source_set_timer引起的内存泄露

当我们前台引用下面的PathAnimiViewController pop了之后,timer依旧存在内存中.

@interface PathAnimiViewController ()
@property(nonatomic,strong) GCDTimer* timer;
@end


@implementation GCDTimer {
    dispatch_source_t timer;
}

- (instancetype) initScheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats queue:(dispatch_queue_t)queue block:(dispatch_block_t)block
{
    NSAssert(queue != NULL, @"queue can't be NULL");
    if ((self = [super init]))
    {
        timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_source_set_timer(timer,
                                  dispatch_time(DISPATCH_TIME_NOW, 0),
                                  interval * NSEC_PER_SEC,
                                  0);
        dispatch_source_set_event_handler(timer, ^
        {
            if (block) {
                block();
            }
            if (!repeats) {
                dispatch_source_cancel(timer);
            }
        });
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC), queue, ^{
            dispatch_resume(timer);
        });
    }
    return self;
}

- (instancetype) initScheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(dispatch_block_t)block
{
    return self = [self initScheduledTimerWithTimeInterval:interval repeats:repeats queue:dispatch_get_main_queue() block:block];
}

- (void) dealloc
{
    dispatch_source_cancel(timer);
    GCDTIMER_DISPATCH_RELEASE(timer);
}

- (void) invalidate
{
    dispatch_source_cancel(timer);
}
}
@end
复制代码

把UIWebview的高度设置成其内容的高度

I usually use these methods, to set UIWebview frame as it's content size:

- (void)webViewDidStartLoad:(UIWebView *)webView {
    CGRect frame = webView.frame;
    frame.size.height = 5.0f;
    webView.frame = frame;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    CGSize mWebViewTextSize = [webView sizeThatFits:CGSizeMake(1.0f, 1.0f)]; // Pass about any size
    CGRect mWebViewFrame = webView.frame;
    mWebViewFrame.size.height = mWebViewTextSize.height;
    webView.frame = mWebViewFrame;

    //Disable bouncing in webview
    for (id subview in webView.subviews) {
        if ([[subview class] isSubclassOfClass: [UIScrollView class]]) {
            [subview setBounces:NO];
        }
    }
}
复制代码

They are automatically called (if you set webView's delegate to this class), when WebView has finished loading it's content.

ref:stackoverflow.com/questions/1…

设置 UINavigationBar's 的背景图片

In the navigation controller's root view controller, add this to the viewDidLoad method:

[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar.png"] forBarMetrics:UIBarMetricsDefault];

在使用右滑返回拖动到一半时,有时会在导航栏上看到三个排成一行的小蓝点。

手势返回拖动一半,放手,navigationBar上会出现三个小蓝点,而且位置不规律,可以肯定这个不是项目代码或者图片搞出来的东西,一定是SDK自己蹦出來的。 后台尝试发现UIBarButtonItem的title如果是nil的话,就会有这个问题。 解决方案: 把[self.navigationItem setHidesBackButton:YES]; 去掉,然後把假装成返回按钮的UIBarButtonItem的title设置成@""。

unrecognized selector sent to instance 0xaxxxx…快速定位

有backtrace又无法明确说明错误在哪行代码,如何快速定位BUG呢?

有时读代码一下很难找到是哪个instance出的问题,这时定制有效的DEBUG断点是最好的办法,方法如下:

在Debug菜单中选择 Breakpoints -> Create Symbolic Breakpoint…
在Symbol中填写如下方法签名:
-[NSObject(NSObject) doesNotRecognizeSelector:]
复制代码

直接使用UIBezierPath在drawRect中来绘图

- (void)drawRect:(CGRect)rect{
    UIBezierPath  *round = [UIBezierPath bezierPathWithRoundedRect:
    CGRectMake((CGRectGetWidth(self.frame)-35)/2, CGRectGetHeight(self.frame)-12, 35, 5) byRoundingCorners:(UIRectCornerAllCorners) cornerRadii:CGSizeMake(10, 10)];
    
    [[UIColor lightGrayColor] setFill];
    [round fill];
}
复制代码

使用NSString来用重复字符组成串

比如,我要生成由6个空格符组成的字符串.

NSString *pad = [[NSString string] stringByPaddingToLength:6 withString:@" " startingAtIndex:0];
复制代码

给CABasicAnimition添加completion block

方法1:

[CATransaction begin];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
[pathAnimation setDuration:1];
[pathAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];    
[pathAnimation setToValue:[NSNumber numberWithFloat:1.0f]];
[CATransaction setCompletionBlock:^{
_lastPoint = _currentPoint; 
_currentPoint = CGPointMake(_lastPoint.x + _wormStepHorizontalValue, _wormStepVerticalValue);}]; 
[_pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
[CATransaction commit];
复制代码

注意: setCompletionBlock要在addAnimation前面添加.

方法2:直接设置CABasicAnimation的代理

pathAnimation.delegate=self;
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    NSLog(@"aaa-bbb-ccc");
}
复制代码

给CABasicAnimition添加fillmode

From the CAMediaTiming protocol documentation:

Determines if the receiver’s presentation is frozen or removed once its active duration has completed. This determines what happens at the end of your animation. By default, it is set to kCAFillModeRemoved, which means that the animation changes are undone when the animation is completed. If you switch it to kCAFillModeForwards, the changes caused by the animation will hang around.

Note that you will also probably need to set the removedOnCompletion property to NO to prevent your animation from being removed, which would make this setting ineffective.

difference between fillmode and removeOnCompletion removeOnCompletion removes the animation from the layer when it has run to completion. fillMode removes the EFFECT of the animation from the layer when it has run to completion. If you set removeOnCompletion=false and leave fillMode set to remove, the animation will still exist (although stopped), but the effect will be reversed (the property will be set back to it's original value).

关于过渡

属性动画只对图层的可动画属性起作用,所以如果要改变一个不能动画的属性(比如图片),或者从层级关系中添加或者移除图层,属性动画将不起作用。

于是就有了过渡的概念。过渡并不像属性动画那样平滑地在两个值之间做动画,而是影响到整个图层的变化。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。 方案1:

AppointRecordTableViewCell* cell=(AppointRecordTableViewCell*)[_tableview cellForRowAtIndexPath:[NSIndexPath indexPathForRow:btn.tag-9000 inSection:0]];
CATransition *transition = [CATransition animation];
transition.type=kCATransitionReveal;
transition.subtype = kCATransitionFromTop;
transition.duration=1;
cell.statusLbl.text=@"已取消";
cell.statusLbl.textColor=UIColorFromRGB(0xc1c1c1);
[cell.statusLbl.layer addAnimation:transition forKey:nil];
CATransition *transition1 = [CATransition animation];
transition1.type=kCATransitionReveal;
transition1.subtype = kCATransitionFromTop;
transition1.duration=1;
cell.appointCancelBtn.hidden=YES;
[cell.appointCancelBtn.layer addAnimation:transition1 forKey:nil];
复制代码

方案2:

self.lab.text=@"aaaaaaaaa";
[UIView transitionWithView:self.lab duration:2.0
   options:UIViewAnimationOptionTransitionFlipFromBottom
animations:^{
   self.lab.text=@"8888888888";
}
completion:NULL];
复制代码

注意:在测试在将过渡代码写在viewDidLoad中没有过渡效果,写在viewDidAppear会产生过渡效果.

CALayer的隐式动画

对于CALayer来说大部分的属性都可以触发隐式动画,当然除非你明确禁止了这个功能。比如,当我们使用

[CATransaction setDisableActions:YES]

那么问题来了,当我改变一个属性的时候,Core Animation是如何判断动画类型和动画持续时间的?实际上动画的执行时间取决于当前事务的设置,动画类型取决于图层行为。

事务是通过CATransaction类来做管理的,但是有一点要知道,它提供的都是静态方法,但是可以用+begin和+commit分别来入栈或者出栈。

[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
self.colorLayer.backgroundColor = [UIColor grayColor].CGColor;
[CATransaction commit];
setAnimationDuration: 来修改时间
animationDuration: 获取值
setCompletionBlock: 运行在动画结束后再做某些事情
复制代码

关于backedLayer的隐式动画

什么叫backedlayer呢?backedlayer指的是一个View在创建好的时候就已经帮你创建好了一个与View大小一致的CALayer了,而且,这个CALayer的所有属性值的改变都不会引起动画反应,除非写在+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations这个方法里面.认识到这一点是很关键的.

ref: www.cnblogs.com/YouXianMing…

给UIView的某一边添加border

方法1:添加子layer

#import <QuartzCore/QuartzCore.h>
- (void)viewDidLoad
{
    CALayer *TopBorder = [CALayer layer];
    TopBorder.frame = CGRectMake(0.0f, 0.0f, myview.frame.size.width, 3.0f);
    TopBorder.backgroundColor = [UIColor redColor].CGColor;
    [myview.layer addSublayer:TopBorder];
    [super viewDidLoad];
}
复制代码

方法2:添加子view

CGSize mainViewSize = self.view.bounds.size;
CGFloat borderWidth = 1;
UIColor *borderColor = [UIColor colorWithRed:37.0/255 green:38.0/255 blue:39.0/255 alpha:1.0];
UIView *topView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, mainViewSize.width, borderWidth)];
topView.opaque = YES;
topView.backgroundColor = borderColor;
topView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin;        
[self.view addSubview:topView];
复制代码

如何给TableView中的cell添加间距.

方案1: 直接在设计的时候,就在cell中留好间距

方案2: 根据单双行来添加空白行,这样来设置间距.

方案3: 每个行占距一个section,然后设置这些section的headerview的高度.

使用UITextKit来得出最后一个字符的位置

- (CGRect)boundingRectForCharacterRange:(NSRange)range
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
    textContainer.lineFragmentPadding = 0;
    [layoutManager addTextContainer:textContainer];

    NSRange glyphRange;

    // Convert the range for glyphs.
    [layoutManager characterRangeForGlyphRange:range actualGlyphRange:&glyphRange];

    return [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
}
复制代码

ref: stackoverflow.com/questions/1…

获取表格分隔线的颜色

UITableView* TV = [[UITableView alloc] init];
UIColor* C = [TV separatorColor];
CGColorRef CGC = [C CGColor];
复制代码

系统还有哪些在后台运行的thread

Thread 2Queue : com.apple.libdispatch-manager (serial)

使用NSLayoutAttributeNotAnAttribute时的注意事项.

在约束中使用NSLayoutAttributeNotAnAttribute时,toItem选项要设置为nil,否则运行时崩溃

NSLayoutConstraint* heightCts=[NSLayoutConstraint constraintWithItem:_topImgView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:44];
复制代码

使用xib做为main Interface

0.创建合适的xib: 创建文件--> 在userinterface -->选择 Window 模板 -->创建xib. 1.首先要设置要该xib的owner为UIApplication. 2.然后,在该xib内拖入一个object,将该object的类设置为你的appDelegate. 并且设置owner的delegate为该object. 3.再往xib内拖入一个UIWindow,并在IB中选中 Full Screen at Launch option in the attributes inspector设,然后将该window设置为appdelegate的IBOutle. 4.在appDelegate中代码设置其rootViewcontroller,然后将该window设置为makeKeyAndVisible.

关于launchscreen Image

用于iPhone6+的1242x2208,或者1080x1920,也就是Retina HD 5.5 用于iPhone6的750x1334,也就是Retina HD 4.7 用于4寸屏(iPhone5/5s)的640x1136,就是Retina 4 用于3.5寸(iPhone4/4s)的640x960,就是2x

1.使用LaunchImage,可以兼容iOS7和iOS8。由于iOS8也会调用LaunchScreen.xib,所以我的做法是直接删除它.

2.要创建一个LaunchImage,可以使用下面两种方案:
    A.Images.xcassets中创建一个LaunchImage,然后按上面的标记配置图片.
    B.不创建Images.xcassets,直接放置将相应的启动图片添加到工程里面,然后,将文件名分别改为:
    Default-568h@2x.png  对应着,iphone5及以上的机型,其分辨率最好为1920*1080.
    Default@2x.png  对应着iphone4s有以下的机器,其分辨率为480*960
复制代码

关于创建snv分支-svncopy

直接使用下面的命令:

svn copy svn.zgjkw.cn/deadline.zg… svn.zgjkw.cn/deadline.zg… -m "inital"

However, with Core data you cannot retrieve only certain attributes of an object – you have to retrieve the entire object.

FailedBankCD.momd (or just .mom) – this is the compiled version of the Managed Object Model (FailedBankCD.xdatamodeld)

UIScrollerView 原理

其实这个原理道理网上已经讲的很明白,我就引用下上面那位高手的原文吧: UIScrollView的工作原理,当手指touch的时候,UIScrollView会拦截Event,会等待一段时间,在这段时间内,如果没有手指 没有移动,当时间结束时,UIScrollView会发送tracking events到子视图上。在时间结束前,手指发生了移动,那么UIScrollView就会进行移动,从而取消发送tracking。

setDelaysContentTouches 这个函数主要时判断是否延迟执行tracking 一般情况下是yes 即会延迟执行,就是先等待一会儿看scrollview 是否有touch 事件发生,如果没有则转而执行子视图的 的touch 事件。

关于dismissViewController

A界面present到B界面,B界面push到C界面,C界面点击返回,怎么直接返回到A界面? 方法是直接在C界面的viewWillDisappear方法里面执行

dismissViewControllerAnimated:YES completion:就可以返回到A界面. 而不用先返回到B界面,再返回到A. 这个方法的作用就是dismiss当前presentViewController出来的模态窗口.

关于只读属性

@property(nonatomic,strong,readonly)UIImage *image;是readonly的,那它还会生成带下划线的成员变量吗?就是_image。如果不能,那下面的这段代码怎么办?
-(UIImage*)image
{
    if(_image == nil){
        _image = [UIImage imageNamed:self.icon];

    }
    return _image;
}
复制代码

解答: 对于只读属性,是不会合成对应的实例变量的,所以上面的代码,直接就编译错误了.如果想让上面的代码有效,可以在对应的匿名分类中添加如下代码:

@property(nonatomic,strong)UIImage *image;
复制代码

这样就可以这样就能在类内可读写,在类外只读了.

关于自定义属性

如果你对一个可读写的property,同时实现了getter和setter方法,那么编译器就不会合成对应的实例变量.

同样,如果你声明的了只读的property,那么编译器也不会合成对应的实例变量.

Note: The compiler will automatically synthesize an instance variable in all situations where it’s also synthesizing at least one accessor method. If you implement both a getter and a setter for a readwrite property, or a getter for a readonly property, the compiler will assume that you are taking control over the property implementation and won’t synthesize an instance variable automatically. If you still need an instance variable, you’ll need to request that one be synthesized

关于为分类添加属性

通过运行时的方法,为分类添加属性.

@interface NSObject (StatePattern)
@property (nonatomic, strong, readonly) NSMutableDictionary  *stateDictionary;
@end
@implementation NSObject (StatePattern)
//runtime属性
@dynamic stateDictionary;
NSString * const _recognizerStateDictionary = @"_recognizerStateDictionary";
- (void)setStateDictionary:(NSMutableDictionary *)stateDictionary {
    objc_setAssociatedObject(self, (__bridge const void *)(_recognizerStateDictionary), stateDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)stateDictionary {
    return objc_getAssociatedObject(self, (__bridge const void *)(_recognizerStateDictionary));
}
复制代码

@end

Dynamic UIWebView height for UITableViewCell

After the UIWebView has finished loading you should call

[tableView beginUpdates];
[tableView endUpdates];
复制代码

This will call -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath again will resize your UITableViewCell. In that method you calculate the new height for the cell based on the size of your UIWebView.

关于frame

一个普遍错误的概念:视图的区域是由它的 frame 定义的。实际上 frame 是一个派生属性,是由 center 和 bounds 合成而来。不使用 Auto Layout 时,大多数人使用 frame 来改变视图的位置和大小。小心些,官方文档特别详细说明了一个注意事项:

如果 transform 属性不是 identity transform 的话,那么这个属性的值是未定义的,因此应该将其忽略

另一个允许向视图添加交互的方法是使用手势识别。注意它们对 responders 并不起作用,而只对视图及其子类奏效。

关于子控制器切换时的页面frame控制.

//我先添加到子控制器,在对应的xib中的添加了约束.
_hChartController  = [[HChartViewController alloc] init];
[self addChildViewController:_hChartController];

//在适当的时候,我会从别的控制器切换到这个控制器,那么这时,我得重新设置一下这个控制器的frame以适应当前的页面的大小. 否则会得到不正确的尺寸.
[[GraphModel getInstance] setupHealthType:(999+index)];
    [self transitionFromViewController:_hpController toViewController:_hChartController duration:1 options:UIViewAnimationOptionTransitionNone animations:^{
    }  completion:^(BOOL finished) {
_hChartController.view.frame=self.view.bounds;
        //currentViewController = oldViewController;
    }];
复制代码

上面说的区别是针对调用者是dictionary而言的。

setObject:forKey:方法NSMutabledictionary特有的,而
setValue:forKey:方法是KVC(键-值编码)的主要方法。
复制代码

在下面的方法中,一般可以使用Core Graphics来绘制,不过使用UIGraphicsPushContext方法的话,也可以通过UIKit做到。

而UIView在drawLayer:inContext:方法中又会调用自己的drawRect:方法。平时在drawRect:中通过UIGraphicsGetCurrentContext()获取的就是以上传入的CGContextRef对象,在drawRect:中完成的所有绘图都会填入层的CGContextRef中,然后被拷贝至屏幕。

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
    UIGraphicsPushContext(ctx);
复制代码

initWithNibName 和 loadNibNamed 的区别和联系

联系:可以使用此方法加载用户界面(xib文件)到我们的代码中,这样,可以通过操作这个加载进来的(xib)对象,来操作xib文件内容。 区别:

1. ShowViewController的initWithNibName方法
ShowViewController * showMessage = [[ShowViewController alloc]
                                                initWithNibName:@"ShowViewController" bundle:nil];
            self.showViewController = showMessage;
            [showMessage release];
2.VideoCellController的loadNibNamed方法
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:@"Save3ViewController" 
                                                          owner:self options:nil] ;
self.showViewController = [nib lastObject];
[nib objectAtIndex:0];
复制代码

总结: 只看他们初始化,那可能感觉是一样的。但是如果,打开分别看xib的关系的时候,才恍然大悟,原来他们的集成类都不一样。

1. initWithNibName要加载的xib的类为我们定义的视图控制器类 
  loadNibNamed要加载的xib的类为NSOjbect。
(比如:甲,乙都买了一个iPhone,但是,甲的是自己的钱,而乙用的是某某的钱)
2.加载方式不同
initWithNibName方法:是延迟加载,这个View上的控件是 nil 的,只有到 需要显示时,才会不是 nil.

loadNibNamed方法:即时加载,用该方法加载的xib对象中的各个元素都已经存在。
(认真理解这句帮规:when using loadNibNamed:owner:options:, the File's Owner should be NSObject, the main view should be your class type, and all outlets should be hooked up to the view, not the File's Owner.)
复制代码

关于UIViewController中的initWithCoder

In UIViewController subclasses -initWithCoder is only called when loading from a storyboard. As -awakeFromNib is called whether you use storyboards or not it might make more sense to use that.

如果使用StoryBoard进行视图管理,程序不会直接初始化一个UIViewController,StoryBoard会自动初始化或在segue被触发时自动初始化,因此方法initWithNibName:bundle:不会被调用。

如果在代码里面使用instantiateViewControllerWithIdentifier:方法显示初始化一个UIViewController,则initWithCoder方法会被调用。

loadView

当访问UIViewController的view属性时,view如果此时是nil,那么VC会自动调用loadView方法来初始化一个UIView并赋值给view属性。此方法用在初始化关键view,需要注意的是,在view初始化之前,不能先调用view的getter方法,否则将导致死循环(除非先调用了[supper loadView];)

ref: stackoverflow.com/questions/1…

关于viewWillLayoutSubviews和viewDidLayoutSubviews:

这两个函数分别在viewWillAppear,viewWillDisappear之后各调用一次.

关于性能.

如果你真的选择了重写 drawRect:,确保检查内容模式。默认的模式是将内容缩放以填充视图的范围,这在当视图的 frame 改变时并不会重新绘制。

鼓励各位读者深入了解 CALayer 及其属性,因为你用它能实现的大多数事情会比用 Core Graphics 自己画要快。

向视图添加交互的方法是使用手势识别。注意它们对 responders 并不起作用,而只对视图及其子类奏效。

关于@synthesize关键字

在xcode5中@synthesize 不是必须的了,编译器会帮你生成. 但是如果你的类实现的协议中有属性

@protocol someProtocol <NSObject>
@property (nonatomic, strong) NSString *name;
@end
复制代码

那么你就必须在类的实现文件中添加一条:

@synthesize name=_name; //roperties defined in protocols will not be auto-synthesized (instead you will get a compiler warning). 
复制代码

否则编译器会警告.

在声明property属性后,有2种实现选择

@synthesize:

编译器期间,让编译器自动生成getter/setter方法。 当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

@dynamic:

告诉编译器,不自动生成getter/setter方法,避免编译期间产生警告 然后由自己实现存取方法或存取方法在运行时动态创建绑定:主要使用在CoreData的实现NSManagedObject子类时使用,由Core Data框架在程序运行的时动态生成子类属性

代码分析:

由于我在子类中无法通过_image来引用父类的属性. 所以下面,我就让编译器重写一下image方法. 来覆盖父类的属性. 前提是我得知道父类的setImage方法是怎么实现,然后在子类中实现同样的功能.并在其中添加我自己的逻辑.

@interface ImgView : UIImageView
@end
@implementation ImgView
//重写image的setter与getter方法
@synthesize image=_image; 
-(void)setImage:(UIImage *)image{
  if(![image isEqual:_image]){
    CABasicAnimation* animi=[CABasicAnimation animationWithKeyPath:@"contents"];
    animi.fromValue=(id)_image.CGImage;
    animi.toValue=(id)image.CGImage;
    animi.duration=0.5;
    self.layer.contents=(id)image.CGImage;
    [self.layer addAnimation:animi forKey:@""];
    _image=image;
  }
}
@end
复制代码

当然上面的代码,也可以这样写:

@implementation ImgView
-(void)setImage:(UIImage *)image{
    if(![image isEqual:self.image]){
        CABasicAnimation* animi=[CABasicAnimation animationWithKeyPath:@"contents"];
        animi.fromValue=(id)self.image.CGImage;
        animi.toValue=(id)image.CGImage;
        animi.duration=0.5;
        self.layer.contents=(id)image.CGImage;
        [self.layer addAnimation:animi forKey:@""];
        [super setImage:image];
    }
}
@end
复制代码

How do you add multi-line text to a UIButton?

For iOS 6 and above, use the following to allow multiple lines:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
// you probably want to center it

button.titleLabel.textAlignment = NSTextAlignmentCenter; // if you want to 
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];
复制代码

For iOS 5 and below use the following to allow multiple lines:

button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
// you probably want to center it
button.titleLabel.textAlignment = UITextAlignmentCenter;
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];
复制代码

使用hitTest来实现事件的转发

@implementation MaskView
//让当前view的子view响应事件,子view无法处理的话,将事件转发给referenceView
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    NSArray* viewArray=self.subviews;
    for(UIView* view in viewArray){
        CGPoint p=[self convertPoint:point toView:view];
        if([view pointInside:p withEvent:event]){
            return [super hitTest:point withEvent:event];
        }
    }
    return self.referenceView;
}
- (IBAction)btnClick:(id)sender {
    NSLog(@"click");
}
@end
复制代码

UIView's -hitTest:withEvent: called three times?

The number of event responses you receive depends on the view hierarchy.

This method traverses the view hierarchy by sending the pointInside:withEvent: message to each subview to determine which subview should receive a touch event. If pointInside:withEvent: returns YES, then the subview’s hierarchy is traversed; otherwise, its branch of the view hierarchy is ignored. You rarely need to call this method yourself, but you might override it to hide touch events from subviews.

This method ignores view objects that are hidden, that have disabled user interaction, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.

Points that lie outside the receiver’s bounds are never reported as hits, even if they actually lie within one of the receiver’s subviews. This can occur if the current view’s clipsToBounds property is set to NO and the affected subview extends beyond the view’s bounds. From the UIView Class Reference.

I had the same problem and was able to solve it with this code. Even though pointInside and hitTest get called 3 times, touchesBegan (or touchesEnded) of the UIView that was touched only gets called once.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{   
    if (event.type == UIEventTypeTouches)
        NSLog(@"%@", self);
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ([self pointInside:point withEvent:event])
        return self;
    return [super hitTest:point withEvent:event];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    if (CGRectContainsPoint([self bounds], point))
    {
        if (event.type == UIEventTypeTouches)
        {           
            return YES;
        }
    }
    return NO;
  }
复制代码

iPhone CGContext: drawing two lines with two different colors

Thats what you need.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetLineWidth(context, 2.0);

CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor orangeColor].CGColor);
CGContextMoveToPoint(context, 1, 1);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context); // and draw orange line}

CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context, 100, 100);
CGContextAddLineToPoint(context, 200, 100);     
CGContextStrokePath(context); // draw blue line


Insert this code just before you set the stroke color the second time:
CGContextStrokePath(bluecontext);
CGContextBeginPath(bluecontext);
All the AddLine and AddOther calls are building a path. The path is drawn with a call like StrokePath, using the most recently set colors and other attributes. You are trying to draw two separate paths, so you must call Begin and Stroke for each path. Begin is sort of implicit when you start drawing, although it does not hurt to call it yourself. The basic flow of drawing is:

CGContextBeginPath(bluecontext); // clears any previous path
// add lines, curves, rectangles, etc...
CGContextStrokePath(bluecontext); // renders the path
复制代码

设置导航栏控制器背景色

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window                 = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    ViewController         *VC = [[ViewController alloc] init];
    UINavigationController *NC = [[UINavigationController alloc] initWithRootViewController:VC];

    [NC.navigationBar setBackgroundImage:[[[UIColor redColor] colorWithAlphaComponent:0.5f] imageWithFrame:CGRectMake(0, 0, 10, 10)]
                          forBarPosition:UIBarPositionAny
                              barMetrics:UIBarMetricsDefault];
    [NC.navigationBar setShadowImage:[[UIColor clearColor] imageWithFrame:CGRectMake(0, 0, 10, 10)]];
    self.window.rootViewController = NC;
    [self.window makeKeyAndVisible];
    return YES;
}
复制代码

由颜色生成图片的

//  UIColor+CreateImage.m
//  UITabBarControllerIcon
#import "UIColor+CreateImage.h"
@implementation UIColor (CreateImage)
- (UIImage *)imageWithFrame:(CGRect)frame {
    UIImage *image = nil;
    if (self) {
        UIView *view         = [[UIView alloc] initWithFrame:frame];
        view.backgroundColor = self;
        UIGraphicsBeginImageContext(view.frame.size);
        [[view layer] renderInContext:UIGraphicsGetCurrentContext()];
        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    return image;
}
@end
复制代码

reactiveCococa中的宏

@weakify(self)宏生成的代码如下:

@autoreleasepool {} __attribute__((objc_ownership(weak))) __typeof__(self) self_weak_ = (self);;
复制代码

@strongify(self)宏生成的代码如下:

 __attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_;
复制代码

测试一下下面的约束,子视图的尺寸发生了变化,父视图会不会发生变化.

 [tempcon addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[img]|" options:0 metrics:nil views:vd]];
 [tempcon addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[img]|" options:0 metrics:nil views:vd]];
复制代码

关于分类对子类影响.

首先我给父类BaseViewController添加一个分类,那么BaseViewController的子类中,还是可以调用该方法,就算没有头文件引用这个分类.

@implementation BaseViewController (aa)
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
    UIEdgeInsets inset=UIEdgeInsetsMake(0, 5, 0, 5);
    if ([tableView respondsToSelector:@selector(setSeparatorInset:)]) {
        [tableView setSeparatorInset:inset];
    }
    if ([tableView respondsToSelector:@selector(setLayoutMargins:)]) {
        [tableView setLayoutMargins:inset];
    } 
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:inset];
    }
}
@end
复制代码

注意要把UIButton的type改为custom,下面代码才起作用

[_pinfoBtn setTitleColor:[UIColor colorWithRed:0.63 green:0.63 blue:0.61 alpha:1] forState:UIControlStateNormal];
[_pinfoBtn setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
[_pinfoBtn setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
复制代码

给UILabel加个padding

@implementation TDPaddingLabel
- (void)drawTextInRect:(CGRect)rect {
    UIEdgeInsets insets = {5, 5, 5, 5};
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
}
-(CGSize)intrinsicContentSize{
    CGSize s=[super intrinsicContentSize];
    return CGSizeMake(s.width+10, s.height+10);
}
@end
复制代码

stackoverflow.com/questions/3… stackoverflow.com/questions/1…

关于UITextField适应键盘位置的计算

关于键盘通知与UITextField那几个代理方法的调用顺序.

关于addChildViewController相关那几个方法与viewWillAppear,和viewDidAppear的关系

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <Mantle.h>
@interface TDAppliactionMainMessage :MTLModel<MTLJSONSerializing>
@property(assign,nonatomic) NSInteger status;
@property(assign,nonatomic) NSInteger uid;
@property(assign,nonatomic) long long createdate;
@property(strong,nonatomic) NSString* content;
@property(strong,nonatomic) NSString* diccode;
@property(assign,nonatomic) NSString* msg_id;
@property(assign,nonatomic) BOOL isread;
+(NSArray*)getPropertyNameArr;
@end

#import "TDAppliactionMainMessage.h"
@interface TDAppliactionMainMessage()
@end
@implementation TDAppliactionMainMessage


+(NSDictionary*)JSONKeyPathsByPropertyKey{
    NSMutableDictionary* dic= [[NSDictionary mtl_identityPropertyMapWithModel:[TDAppliactionMainMessage class]] mutableCopy];

        [dic setValue:@"id" forKey:@"msg_id"];
    return [dic copy];
}

+(NSArray*)getPropertyNameArr{
    NSDictionary* dic= [NSDictionary mtl_identityPropertyMapWithModel:[TDAppliactionMainMessage class]];
    return [dic allKeys];
}

+(NSValueTransformer*)msg_idJSONTransformer{
//    NSLog(@"%@",[[self class] getPropertyNameArr]);
    return [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError *__autoreleasing *error){
        if([value isKindOfClass:[NSString class]])
           return value;
        if([value isKindOfClass:[NSNumber class]])
            return [NSString stringWithFormat:@"%@",value];
        return value;
    }];
}
@end
复制代码
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if(selected){
        self.textLabel.textColor=[UIColor colorWithRed:0.21 green:0.52 blue:0.17 alpha:1];
        self.textLabel.font=[UIFont boldSystemFontOfSize:15.0f];
       if(self.selectLayer!=nil)
           return;
        UIColor* highColor=[[UIColor colorWithRed:0.46 green:0.46 blue:0.46 alpha:1] colorWithAlphaComponent:0.1];
        UIColor* midColor=[[UIColor colorWithRed:0.46 green:0.46 blue:0.46 alpha:1] colorWithAlphaComponent:0.25];
        CAGradientLayer * gradient = [CAGradientLayer layer];
        [gradient setFrame:[self.contentView bounds]];
        [gradient setColors:[NSArray arrayWithObjects:(id)[highColor CGColor], (id)[midColor CGColor],(id)[highColor CGColor], nil]];
        CALayer * roundRect = [CALayer layer];
        [roundRect setFrame:self.contentView.bounds];
        [roundRect setCornerRadius:1.0f];
        [roundRect setMasksToBounds:YES];
        [roundRect addSublayer:gradient];
        self.selectLayer=roundRect;
        [[self.contentView layer] insertSublayer:roundRect atIndex:0];
    }
    else{
        self.textLabel.textColor=[UIColor blackColor];
        self.textLabel.font=[UIFont systemFontOfSize:13.0f];
        [self.selectLayer removeFromSuperlayer];
        self.selectLayer=nil;
    }
}
复制代码

推送来的字典不是给你完全解析成字典的,只是首层是字典,其它的都还是字符串,还得自己解析成字典.

 NSDictionary* msgDic=[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
{
    aps =     {
        alert = "\U7eff\U8272\U901a\U9053";
        badge = 1;
        sound = default;
    };
    data = "{\"id\":\"8092\",\"content\":\"您有一条绿色通道请求消息!\",\"diccode\":\"0301\"}"
}
复制代码

推送而来的三种方式的解析

reused cell不会再调用layoutsubview

-(void)layoutSubviews{
    [super layoutSubviews];
    NSLog(@"-----layoutSubviews----");
    if(self.itemDescriptionLbl.hidden==NO){
        CGRect r=self.statusImgview.frame;
        if(self.frame.size.height>65){
            r.origin.y=self.itemDescriptionLbl.frame.origin.y;
            self.statusImgview.frame=r;
        }
    }
    else{
        CGRect r=self.statusImgview.frame;
        if(self.frame.size.height>40){
            r.origin.y=self.itemNameLbl.frame.origin.y;
            self.statusImgview.frame=r;
        }
    }
}
复制代码

layoutsubview最好是单纯的做布局工作,因为调用它的时候,可能会需要二次才能得到正常的尺寸. 也就是说,在外部添加好子view,在layoutsubviews里再对这些view做布局.

关于UITableViewHeaderFooterView

  1. 这个UITableViewHeaderFooterView和UITableCell有很多的相似之处,它有自己的contentView和一些预定义的textLabel.

it has a lot in common with brethren like UITableViewCell and UICollectionViewCell. There’s a content view for adding our own content to, background view for configuring the background, and some pre-defined text labels.

  1. 但是系统并没有为我们预定义这样的组件,使用xib不合适. 那么使用最合适的方法是自定义UITableViewHeaderFooterView 的子类.

NSString的方法containString只能在IOS8下使用. 如果要兼容ios7,可以如下:

compare rangeOfString with NSNotFound
NSRange range = [self rangeOfString:other];
if(range.location != NSNotFound){
    //do something
}
复制代码

滚动到UITableView指定的section

The code above will scroll the tableview so the desired section is visible but not necessarily at the top or bottom of the visible area. If you want to scroll so the section is at the top do this:

CGRect sectionRect = [tableView rectForSection:indexOfSectionToScrollTop];
sectionRect.size.height = tableView.frame.size.height;
[tableView scrollRectToVisible:sectionRect animated:YES];
复制代码

Modify sectionRect as desired to scroll the desired section to the bottom or middle of the visible area.

ref:stackoverflow.com/questions/1…

性能优化技巧:

如果主线程一次要初始化大量的view,可能会使界面卡住.一个解决方法就是先显示等待,再把对应的操作通过dispatch_asyn添加到主队列当中,等相应的任务在主队列中完成后,再取消显示等待.

transitionFromViewController

当下面代码第一次调用时,detailvc会进行viewDidLoad操作,那么当再次运行下面的代码的时候,则只是detailvc的viewWilllAppear等会调用.

[self transitionFromViewController:_resultvc toViewController:_detailvc  duration:0 options:UIViewAnimationOptionTransitionNone animations:^{
}  completion:^(BOOL finished) {
    self.detailvc.view.frame=self.view.bounds;
}];
复制代码

UICollectionViewCell的单选

在自定义Cell:

@implementation WHUTitleCell
-(void)setSelected:(BOOL)selected{
    [super setSelected:selected];
    if(selected){
    self.contentView.backgroundColor=[UIColor colorWithRed:0xfc/255.0 green:0xb2/255.0 blue:0x99/255.0 alpha:1];
        self.titleLbl.textColor=[UIColor whiteColor];
    }
    else{
    self.contentView.backgroundColor=[UIColor colorWithRed:0.93 green:0.93 blue:0.93 alpha:1];
        self.titleLbl.textColor=[UIColor blackColor];
    }
}
@end
复制代码

在UICollectionView中:

-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSArray *selectedItemIndexPaths = [collectionView indexPathsForSelectedItems];
    for(NSIndexPath* p in selectedItemIndexPaths) {
        [collectionView deselectItemAtIndexPath:p animated:NO];
    }
    return YES;
}
复制代码

PS:如果在layoutsubview中进行了与选中相关的操作,那么,最好也要在layoutsubview中进行一下相应的设置. 否则会有微妙的bug

UICollectionViewCell的单选动画

{
        [collectionView.collectionViewLayout invalidateLayout];
        UICollectionViewCell *__weak cell = [collectionView cellForItemAtIndexPath:indexPath]; // Avoid retain cycles
        void (^animateChangeWidth)() = ^()
        {
            CGRect frame = cell.frame;
            frame.origin.x=0;
            frame.origin.y=0;
            cell.frame = frame;
            cell.transform=CGAffineTransformMakeScale(0.1, 0.1);
        };
      // Animate
      [UIView transitionWithView:cell duration:0.5f options: UIViewAnimationOptionCurveLinear animations:animateChangeWidth completion:nil];
    }

###在主队列进行异步操作,可以解决一些奇怪的问题
---
}
复制代码
-(void)layoutSubviews{
    [super layoutSubviews];
    if(_hcts==nil){
        WHUTreColFlowLayout* flow=[[WHUTreColFlowLayout alloc] init];
        //在这里对UICollectionView进行约束操作
        [self addConstraint:_hcts];
        [super layoutSubviews];
        //如果将下面单元格选择操作,不进行异步操作的话.单元格的选中状态无背景色.
        dispatch_async(dispatch_get_main_queue(), ^(void){
        //在这里进行异步操作,可以让代码的执行流程先完成相关布局操作,再进行下面的选择操作.那么单元格的选中操作就正常了.
            [_dataView selectItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionNone];
        });

    }
}
复制代码

关于UIBezierPath及CGPath的bounds

UIBezier* mainPath=[UIBezierPath bezierPath];
//对mainPath进行操作......
    CGRect t=mainPath.bounds;
    CAShapeLayer* shape=[CAShapeLayer layer];
    shape.path=mainPath.CGPath;
    shape.lineWidth=1;
    shape.strokeColor=_selectedColor.CGColor;
    shape.strokeStart=0.0f;
    shape.strokeEnd=1.0f;
    //注意这里得到bounds可能形如:(origin = (x = 4.05025244, y = 2.05025244), size = (width = 9.89949417, height = 4.94974756)) ,这里的x,y并不为零
    shape.bounds=mainPath.bounds;
    shape.masksToBounds=YES;
    1. 也可以通过CGPathGetPathBoundingBox(mainPath.CGPath)来得到路径的bounds.
    2. 通常我们进行如下设置的时候,要注意bounds的origin.
               aview.frame=bview.bounds
复制代码

疑问: 对于CGPath的bounds之origin,不为(0,0),这意味着什么?

当内容可以滚动时,闪动一下UIScrolView滚动条.

当UIScrollView第一次加载时,为了让用户知道,里面的内容是可滚动的,那么可以滚动一下 UIScrollview的滚动条.

[_dataView flashScrollIndicators];
[_dataView performSelector:@selector(flashScrollIndicators) withObject:nil afterDelay:0.2];
[_dataView performSelector:@selector(flashScrollIndicators) withObject:nil afterDelay:0.5];
[_dataView performSelector:@selector(flashScrollIndicators) withObject:nil afterDelay:0.9];
复制代码


- (id)initWithView:(UIView *)view {
  // Let's check if the view is nil (this is a common error when using the windw initializer above)
  if (!view) {
    [NSException raise:@"MBProgressHUDViewIsNillException" 
          format:@"The view used in the MBProgressHUD initializer is nil."];
  }
  return [self initWithFrame:view.bounds];
}
复制代码

计算字符串CGSize的方法二种

方法一:

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
  options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)  context:nil];
复制代码

I would encourage you to avoid this method entirely. You can use Core Text directly to estimate the string size, if you can handle the overhead of creating a framesetter for each string you need to draw. It doesn't precisely honor width constraints either, but it seems to get within a few pixels, in my experience.

方法二:

CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attrString);
CGSize targetSize = CGSizeMake(320, CGFLOAT_MAX);
CGSize fitSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, [attrString length]), NULL, targetSize, NULL);
CFRelease(framesetter);
复制代码

ref:stackoverflow.com/questions/1…

计算UITablViewCell的绝对位置

CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];
复制代码

ref:tilomitra.com/get-absolut…

给UITextView添加placeholder

-(void)viewDidLoad
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(15, 30, kScreenWidth - 30, 100)];
    textView.font = [UIFont systemFontOfSize:15];
    _textView = textView;
    textView.text = @"点击这里输入文字...";
    textView.textColor = [UIColor grayColor];
    textView.delegate = self;
    [view addSubview:textView];

    return view;
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
    if ([textView.text isEqualToString:@"点击这里输入文字..."]) {
        textView.text = @"";
        textView.textColor = [UIColor blackColor];
    }
}
- (void)textViewDidEndEditing:(UITextView *)textView {
    if (textView.text.length<1) {
        textView.text = @"点击这里输入文字...";
        textView.textColor = [UIColor grayColor];
    }
}
复制代码

让UIScrollView滚动到底部

测试了,下面的方法,须写在viewDidAppear方法才有效.

You can use the UIScrollView's setContentOffset:animated: function to scroll to any part of the content view. Here's some code that would scroll to the bottom, assuming your scrollView is self.scrollView:

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    if(self.scrollview.contentSize.height-self.scrollview.bounds.size.height>0){
        CGPoint bottomOffset = CGPointMake(0, self.scrollview.contentSize.height - self.scrollview.bounds.size.height);
        [self.scrollview setContentOffset:bottomOffset animated:YES];
    }
}
复制代码

关于UIScrollView的各代理方法

//只要它的contentOffset发生变化,就调用.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//    NSLog(@"%s", __PRETTY_FUNCTION__);
}
复制代码

//用户刚一开始拖动的时候,调用这个方法

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [self logDraggingAndDecelerating];
}
复制代码

//用户拖动结束的时候,调用这个方法,如果decelerate为零,那么滚动会立即停止.

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    NSLog(@"%s --> willDecelerate:%d", __PRETTY_FUNCTION__,decelerate);
    [self logDraggingAndDecelerating];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    NSLog(@"%s velocity: %@, targetContentOffset: %@", __PRETTY_FUNCTION__,
          [NSValue valueWithCGPoint:velocity],
          [NSValue valueWithCGPoint:*targetContentOffset]);
    [self logDraggingAndDecelerating];
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [self logDraggingAndDecelerating];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
    [self logDraggingAndDecelerating];
}
复制代码
  1. 当连续滚动UIScrollView的时候,各代理的调用顺序可能如下,注意下面各代理的调用顺序,先是拖动相关的,然后再说减速相关的.

当一次减速动画尚未结束的时候再次 drag scroll view,didEndDecelerating 不会被调用,并且这时 scroll view 的 dragging 和 decelerating 属性都是 YES。新的 dragging 如果有加速度,那么 willBeginDecelerating 会再一次被调用,然后才是 didEndDecelerating;如果没有加速度,虽然 willBeginDecelerating 不会被调用,但前一次留下的 didEndDecelerating 会被调用.

scrollViewWillBeginDragging: 
scrollViewWillEndDragging: withVelocity: targetContentOffset: 
scrollViewDidEndDragging: willDecelerate: 
scrollViewWillBeginDecelerating: 
scrollViewWillBeginDragging: 
scrollViewWillEndDragging: withVelocity: targetContentOffset: 
scrollViewDidEndDragging: willDecelerate: 
scrollViewWillBeginDecelerating: 
...
scrollViewWillBeginDragging: 
scrollViewWillEndDragging: withVelocity: targetContentOffset: 
scrollViewDidEndDragging: willDecelerate: 
scrollViewWillBeginDecelerating: 
scrollViewDidEndDecelerating:
复制代码
  1. 刚开始拖动的时候,dragging 为 YES,decelerating 为 NO;decelerate 过程中,dragging 和 decelerating 都为 YES;decelerate 未结束时开始下一次拖动,dragging 和 decelerating 依然都为 YES。所以无法简单通过 table view 的 dragging 和 decelerating 判断是在用户拖动还是减速过程。

解决这个问题很简单,添加一个变量如 userDragging,在 willBeginDragging 中设为 YES,didEndDragging 中设为 NO。

文章分类
iOS
文章标签