iOS |知识点整理(13)

332 阅读13分钟

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

键盘位置,UITextView高度自适应.

注意下面的的注释.

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
}


-(void)keyboardWillChangeFrame:(NSNotification*)notif{
    NSValue *keyboardBoundsValue = [[notif userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey];
    CGRect keyboardEndRect = [keyboardBoundsValue CGRectValue];
    self.keyboardRect=keyboardEndRect;
    [self adjustKeyboardPosition];
}

-(void)textViewDidChange:(UITextView *)textView{
    if(textView.contentSize.height>36){
        //UITextView在上下左右各有8px的间隔导致。所以要正确计算高度或者宽度,我们可以使用以下的方法。在计算的时候减去16px,然后在计算出来的高度再加上16px。
        float fPadding = 16.0; // 8.0px x 2
        CGSize size = [textView.text boundingRectWithSize:CGSizeMake(textView.frame.size.width-fPadding, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14.0f]} context:nil].size;
        float fHeight = size.height + 16.0+8;
        [self adjustKeyboardPosition];
        self.textHeightCts.constant=fHeight;
    }
}

//这里的思路是,首先计算出当前textview相对于vc.view的rect. 那么下一步,我们计算一下键盘弹出之后,键盘顶端在vc.view的Y轴位置. 然后再将这个新的位置,再转换到textview中的rect. 那么这个rect的origin.y应该为负值来去适应新的位置. 将这scrollview的contentoffset.y加上这个新Y位置的绝对值.就可以了.

-(void)adjustKeyboardPosition{
    CGRect r1=[self.view convertRect:_txtview.bounds fromView:_txtview];
    CGFloat cursorPosition=0;
    if (_txtview.selectedTextRange) {
        cursorPosition = [_txtview caretRectForPosition:_txtview.selectedTextRange.start].origin.y;
    } else {
        cursorPosition = 0;
    }
    //计算求出光标的位置.
    r1.origin.y=self.view.bounds.size.height-self.keyboardRect.size.height-cursorPosition-20;
    CGRect r2=[self.view convertRect:r1 toView:_txtview];
    CGPoint p=self.scrollview.contentOffset;
    NSLog(@"orgin.y:%lf  cursor:%lf",r2.origin.y,cursorPosition);
     //注意这里的值是一个累加的过程,因为每次调用该函数adjustKeyboardPosition的时候,这里求得的origin.y都是相当于当前最新位置的变化.所以这里应该是累积高度变化的. 每次调整之后UITextView都被调整到了一个新的位置,这样能过累加变化,就能实时调整UITextView的位置.
    p.y-=r2.origin.y;
    if(p.y<0) return;
    self.scrollview.contentOffset=p;
    UIEdgeInsets u=self.scrollview.contentInset;
    u.bottom-=r2.origin.y;
    self.scrollview.contentInset=u;
    NSLog(@"contentOffset:%@",NSStringFromCGPoint(self.scrollview.contentOffset));
}
-(void) keyboardWillHide:(NSNotification *)note
{
  //这里的代码是为了当键盘消失之后,调整一下scrollview中contentview的高度
,以便能反应最新的textview高度变化
CGRect r1=[self.conview convertRect:self.workDescriptionFld.bounds fromView:self.workDescriptionFld];
    if(r1.origin.y+r1.size.height>self.scrollview.frame.size.height){
        self.conHeightCts.constant=self.scrollview.frame.size.height-(r1.origin.y+r1.size.height+2);
    }
    self.isKeyboardShow=NO;
}

IOS7中可以用html来实现部分coretext的功能.

NSString *advise = [NSString stringWithFormat:@"您已成功预约<html> <body> <span style=\"font-size:20px;color:green;\">%@ </span> </body></html>请及时就诊",[Utils dateWithTimestamp:info.workdate]];
NSAttributedString *questionStr = [[NSAttributedString alloc] initWithData:[advise dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
labTitle.attributedText = questionStr;

关于setAnimationRepeatAutoreverses

这个属性用来控制,动画控制到某个属性值,再让这个属性值动画返回到初始值. 对于下面的动画来说:

如果[UIView setAnimationRepeatAutoreverses:NO]; 这句话,动画效果是红线从上移动到下方,然后再从上移动到下方,就这样来回反复. 如果[UIView setAnimationRepeatAutoreverses:YES]; 这句话,动画效果是红线从上移动到下方,然后再从下方移动到上方,这样来回反复.

// 扫描时上下移动 动画
        UIView *line = [[UIView alloc] initWithFrame:CGRectMake(scanImgView.frame.origin.x, scanImgView.frame.origin.y-7, scanImgView.frame.size.width, 2)];
        [self.view addSubview:line];
        line.backgroundColor = [UIColor redColor];
        [UIView beginAnimations:@"animationID" context:NULL];
        [UIView setAnimationDuration:2.5];
        [UIView setAnimationCurve:UIViewAnimationCurveLinear];
        [UIView setAnimationRepeatAutoreverses:YES];
        [UIView setAnimationRepeatCount:INT32_MAX];
        [line setFrame:CGRectMake(scanImgView.frame.origin.x,scanImgView.frame.origin.y+scanImgView.frame.size.width-5, scanImgView.frame.size.width, 2)];
        line.alpha = 0.5;
        [UIView commitAnimations];

在UITableViewCell中的UIButton没有高亮状态

解决方法1:

设置 UIScrollView.delaysContentTouches = NO
同时,在UITableView的子类中,覆盖如下方法:

(BOOL)touchesShouldCancelInContentView:(UIView *)view method in your UITableView subclass like so:

- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
    // Because we set delaysContentTouches = NO, we return YES for UIButtons
    // so that scrolling works correctly when the scroll gesture
    // starts in the UIButtons.
    if ([view isKindOfClass:[UIButton class]]) {
        return YES;
    }
    return [super touchesShouldCancelInContentView:view];
}

ref:charlesharley.com/2013/progra…

解决方法2:

self.scrollview.delaysContentTouches=NO;
self.scrollview.panGestureRecognizer.cancelsTouchesInView=NO;

关于NSUserDefaults的用法

You can only store things like NSArray, NSDictionary, NSString, NSData, NSNumber, and NSDate in NSUserDefaults.

You need to convert the object to NSData (like you have in some of the code) and store that NSData in NSUserDefaults. You can even store an NSArray of NSData if you need to.

You can only store property list types (array, data, string, number, date, dictionary) or urls in NSUserDefaults. This means that everything, including any nested dictionary values, must be property list types. You'll want to implement the NSCoding protocol on your ServerModule object and then use NSKeyedArchiver to serialize your data before storing it and and NSKeyedUnarchiver to deflate your data after reading it back out of NSUserDefaults.

For example, given the properties you've shown exist on ServerModule objects, I'd add the following NSCoding protocol methods to your ServerModule implementation:

// NSCoding support
-(void)encodeWithCoder:(NSCoder*)encoder {
    [encoder encodeObject:self.name forKey:@"name"];
    [encoder encodeObject:self.ip forKey:@"ip"];
    [encoder encodeObject:self.port forKey:@"port"];
    [encoder encodeObject:self.username forKey:@"username"];
    [encoder encodeObject:self.password forKey:@"password"];
}

-(id)initWithCoder:(NSCoder*)decoder {
    self.name = [decoder decodeObjectForKey:@"name"];
    self.ip = [decoder decodeObjectForKey:@"ip"];
    self.port = [decoder decodeObjectForKey:@"port"];
    self.username = [decoder decodeObjectForKey:@"username"];
    self.password = [decoder decodeObjectForKey:@"password"];
    return self;
}

And then of course you'll need to serialize:

NSData* archivedServerModules = [NSKeyedArchiver archivedDataWithRootObject:_AllModules];
[_editServerModules setObject:archivedServerModules forKey:@"AllModules"];


and deflate appropriately:
NSData* archivedServerModules = [_editServerModules objectForKey:@"AllModules"];
NSDictionary* serverModules = [NSKeyedUnarchiver unarchiveObjectWithData:archivedServerModules];

对于字典:

If you don't want to bother about ensuring all your dictionary values are the property list types, then you can simply convert the dictionary into NSData,

NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
[def setObject:[NSKeyedArchiver archivedDataWithRootObject:self.myDictionary] forKey:@"MyData"];
[def synchronize];
And to get back into a dictionary from sandbox:

NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSData *data = [def objectForKey:@"MyData"];
NSDictionary *retrievedDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
self.myDictionary = [[NSDictionary alloc] initWithDictionary:retrievedDictionary];

ref:stackoverflow.com/questions/2… stackoverflow.com/questions/1…

关于NSData

NSData方便了对象的序列化. 当将它写入磁盘的时候,它能够保证写入操作的原子性.它在写入的时候,首先建立一个临时文件,当这个临时文件写入成功的时候,才把这个文件移动它应该在的地方.从而写入成功.

NSData creates static data objects, and NSMutableData creates dynamic data objects. NSData and NSMutableData are typically used for data storage and are also useful in Distributed Objects applications, where data contained in data objects can be copied or moved between applications.

The NSData class and its subclasses provide methods to quickly and easily save their contents to disk. To minimize the risk of data loss, these methods provide the option of saving the data atomically. Atomic writes guarantee that the data is either saved in its entirety, or it fails completely. The atomic write begins by writing the data to a temporary file. If this write succeeds, then the method moves the temporary file to its final location.

While atomic write operations minimize the risk of data loss due to corrupt or partially-written files, they may not be appropriate when writing to a temporary directory, the user’s home directory or other publicly accessible directories. Any time you work with a publicly accessible file, you should treat that file as an untrusted and potentially dangerous resource. An attacker may compromise or corrupt these files. The attacker can also replace the files with hard or symbolic links, causing your write operations to overwrite or corrupt other system resources.

NSValue

我们先看看NSValue能做什么:

一个NSValue对象是用来存储一个C或者Objective-C数据的简单容器。它可以保存任意类型的数据,比如 int,float,char,当然也可以是指pointer, structure, and object id。NSValue类的目标就是允许以上数据类型的数据结构能够被添加到集合里,例如那些需要其元素是对象的数据结构,如NSArray或者NSSet 的实例。需要注意的是NSValue对象一直是不可枚举的。

1、+ (NSValue *)valueWithBytes:(const void *)value objCType:(const char *)type;

创建一个NSValue。 传递的参数是你想要包装的数值的地址(如一个NSSize或你自己的struct),通常得到的是你想要存储的变量的地址(在C语言中使用操作 符&)。你也可以提供一个用来描述这个数据类型的字符串(参数objCType),通常用来说明struct中实体的类型和大小。不需要自己写这 个字符串,@encode编译器指令可以接收数据类型的名称并生成合适的字符串。

例如:

typedef struct testTag
{
    int  a;
    char *b;
}TEST_DATA_ST;
--  定义数据结构
TEST_DATA_ST stValue = {0};
stValue.a = 58;
--  生成数据结构,并赋初值
NSValue *value = [NSValue valueWithBytes:&stValue objCType:@encode(TEST_DATA_ST)];
--  封装,注意type类型使用@encode来包装
TEST_DATA_ST testValue = {0};
[value getValue:&testValue];
--  解封,获得数据

2、- (void)getValue:(void *)value(出参);
从NSValue对象中提取数值。
提取的数值,存放在value这个指针所指向的内存块里。

3、Cocoa提供了常用struct数据类型转换成NSValue的便捷方法:

+ (NSValue *)valueWithPoint:(NSPoint)point;
+ (NSValue *)valueWithSize:(NSSize)size;
+ (NSValue *)valueWithRect:(NSRect)rect;
- (NSPoint)pointValue;
- (NSSize)sizeValue;
- (NSRect)rectValue;
NSRect是一个矩形数据种类,NSPoint是起始点,NSSize存储长度和宽度,这些常用的数据种类是C的struct而不是对象。

What exactly willMoveToParentViewController: and didMoveToParentViewController: do?

In addition to what has been said, they do call some delegate methods: addChildViewController 调用 [child willMoveToParentViewController:self] and removeFromParentViewController: 调用 [child didMoveToParentViewController:nil] Also, they modify the childViewControllers property, which holds an array of child view controllers.

测试代码:

TestViewController* t=[[TestViewController alloc] init];
[self addChildViewController:t];
[self.view addSubview:t.view];
对于下面的代码,TestViewController将依次调用:
 willMoveToParentViewController
 viewDidAppear

ref: stackoverflow.com/questions/1…

关于addChildViewController方法

苹果新的API增加了addChildViewController方法,并且希望我们在使用addSubview时,同时调用[self addChildViewController:child]方法将subview对应的viewController也加到当前ViewController的管理中。对于那些当前暂时不需要显示的subview,只通过addChildViewController把subViewController加进去。需要显示时再调用transitionFromViewController:toViewController:duration:options:animations:completion方法。

另外,当收到系统的Memory Warning的时候,系统也会自动把当前没有显示的subview unload掉,以节省内存。

###透明的UIButton可以响应touch事件的

self.handerView = [UIButton buttonWithType:UIButtonTypeCustom];
[_handerView setFrame:[UIScreen mainScreen].bounds];
[_handerView setBackgroundColor:[UIColor clearColor]];
[_handerView addTarget:self action:@selector(dismissPopoverView:) forControlEvents:UIControlEventTouchUpInside];
[_handerView addSubview:self];

关于-[NSISEngine ]: unrecognized selector sent to instance错误的处理

出现这样的错误,其实和NSISEngine无关.而是内存泄露了. 也就是说原来的内存被释放了,然后又将这边内存分配给了NSISEngine对象,这才导致了这样的错误. 解决方法:就是开启 zombies 来查看一下实际的内存泄露情况. Another possibility is that your GetStartedView has already been deallocated and the memory used for a NSISEngine object. Try turning on zombies to see which is true. (Or, check whether your GetStartedView is being held with a strong reference.)

判断当前viewcontroller是否是present来的.

Since modalViewController has been deprecated in iOS 6, here's a version that works for iOS 5+ and that compiles without warnings.

Objective-C:

- (BOOL)isModal {
    return self.presentingViewController.presentedViewController == self
      || (self.navigationController != nil && self.navigationController.presentingViewController.presentedViewController == self.navigationController)
      || [self.tabBarController.presentingViewController isKindOfClass:[UITabBarController class]];
}

Swift:

var isModal: Bool {
    return self.presentingViewController?.presentedViewController == self
        || (self.navigationController != nil && self.navigationController?.presentingViewController?.presentedViewController == self.navigationController)
        || self.tabBarController?.presentingViewController is UITabBarController
}

presentingViewController:

If the view controller that received this message is presented by another view controller, this property holds the view controller that is presenting it. If the view controller is not presented, but one of its ancestors is being presented, this property holds the view controller presenting the nearest ancestor. If neither the view controller nor any of its ancestors are being presented, this property holds nil.

presentedViewController:

The view controller that is presented by this view controller, or one of its ancestors in the view controller hierarchy. (read-only)

ref:stackoverflow.com/questions/2…

UIView some tips

1.将一个view的事件响应转到其父view上.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return self.superview;
}

2.在UITableviewCell重用的时候,还原其默认的状态

- (void)prepareForReuse
{
    [super prepareForReuse];
    [self.avatarView reset];//重置头像
    self.checkBox.image = [UIImage imageNamed:@"ico_pick_uncheck"]; //重置checkbox的状态.
}

Plist文件的读写操作

1.读操作

//创建主束  
NSBundle *bundle=[NSBundle mainBundle];
//读取plist文件路径
NSString *path=[bundle pathForResource:@"citys" ofType:@"plist"];
//读取数据到 NsDictionary字典中 
NSDictionary *dictionary=[[NSDictionary alloc]initWithContentsOfFile:path];

2.写操作

    NSMutableDictionary *data=[[NSMutableDictionary alloc]init];
    [data setObject:@"one" forKey:@"1"];
    [data setObject:@"two" forKey:@"2"];
    [data setObject:@"three" forKey:@"3"];
    [data setObject:@"four" forKey:@"4"];
    //要往沙盒中写数据当然要先取的沙盒目录啦,也就是Document目录
    NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *plistPaht=[paths objectAtIndex:0];
    //取得完整的文件名
    NSString *fileName=[plistPaht stringByAppendingPathComponent:@"textdeom.plist"];
    NSLog(@"fileName is%@",fileName);
    //创建并写入文件
    [data writeToFile:fileName atomically:YES];

长宽随父视图的大小变化

self.popoverView.frame = self.presentingViewController.view.bounds;
self.popoverView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.popoverView.translatesAutoresizingMaskIntoConstraints = YES;

运行时间的度量

CFTimeInterval startTime = CACurrentMediaTime();
{
    for (size_t i = 0; i < iterations; i++) {
        @autoreleasepool {
            NSMutableArray *mutableArray = [NSMutableArray array];
            for (size_t j = 0; j < count; j++) {
                [mutableArray addObject:object];
            }
        }
    }
}
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);

矩阵变换的补偿

CGAffineTransform transform=CGAffineTransformIdentity;
transform=CGAffineTransformTranslate(transform, 262.5, 0)
transform=CGAffineTransformScale(transform, 0.8,0.8 );
myview.transform=transform

由于我对view进行双重变换,那么最后移动的位置在247.5 ,问题是,我要对translation做怎样的补偿,才能让位置移动到262.5??

解答:

  1. CGAffineTransform变换函数构造的矩阵在左边,如:
  • t' = CGAffineTransformTranslate(t,tx,ty)
  • 结果为:t' = [ 1 0 0 1 tx ty ] * t
  1. 对于上面的补偿是w*(1-scale)/2.0f

What you need to do is to apply additional translation to compensate for the difference due to scaling. In the above example, I need to adjust x coordinate by reducing it by w1 * (1 - scaling factor) / 2. w1 is the View’s original width. Since the new width is w1 * scaling factor, the light blue region on both sides combined is w1 * (1 - scaling factor). In my example, that’s w1 * 0.4. However, since scaling is done with respect to the center, on either side the adjustment is just half. So you adjust the x coordinate by w1 * (1 - scaling factor) / 2.

3.上面的矩阵变换等价于:

CGAffineTransformMake(0.8, 0.f, 0.f, 0.8, 262.5, 0);

在动画运行过程中改变动画

你大概知道 UIView 实例,以及 layer-backed 的 NSView,修改它们的 layer 来委托强大的 Core Graphics 框架来进行渲染。然而,你务必要理解,当把动画添加到一个 layer 时,是不直接修改它的属性的。

取而代之,Core Animation 维护了两个平行 layer 层次结构: model layer tree(模型层树) 和 presentation layer tree(表示层树)。前者中的 layers 反映了我们能直接看到的 layers 的状态,而后者的 layers 则是动画正在表现的值的近似。

实际上还有所谓的第三个 layer 树,叫做 rendering tree(渲染树)。因为它对 Core Animation 而言是私有的,所以我们在这里不讨论它。

考虑在 view 上增加一个渐出动画。如果在动画中的任意时刻,查看 layer 的 opacity 值,你是得不到与屏幕内容对应的透明度的。取而代之,你需要查看 presentation layer 以获得正确的结果。

虽然你可能不会去直接设置 presentation layer 的属性,但是使用它的当前值来创建新的动画或者在动画发生时与 layers 交互是非常有用的。

通过使用 -[CALayer presentationLayer] 和 -[CALayer modelLayer],你可以在两个 layer 之间轻松切换。

在默认情况下,动画不会在超出其持续时间后还修改 presentation layer。实际上,在结束时它甚至会被彻底移除。

一旦动画被移除,presentation layer 将回到 model layer 的值,并且因为我们从未修改该 layer 的 position 属性,所以我们的飞船将重新出现在它开始的地方。

- (void)viewDidLoad
{
    [super viewDidLoad];
    touchView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
    touchView.backgroundColor = [UIColor redColor];
    [self.view addSubview:touchView];
    UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc]
                                  initWithTarget:self action:@selector(tap:)];
    [self.view addGestureRecognizer:gr];
}

//这种方法,不能实现在动画运行过程中,改变动画
//- (void)tap:(UITapGestureRecognizer*)gr
//{
//    [UIView animateWithDuration:1.f animations:
//     ^{touchView.center = [gr locationInView:self.view];}];
//}

//下面这种方法是可以的.
- (void)tap:(UITapGestureRecognizer*)gr
{
    [touchView.layer removeAnimationForKey:@"aaa"];
    CGPoint newPos = [gr locationInView:self.view];
    CGPoint oldPos = [touchView.layer.presentationLayer position];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [NSValue valueWithCGPoint:oldPos];
    animation.toValue = [NSValue valueWithCGPoint:newPos];
    animation.duration=2.0f*fabs((newPos.y-oldPos.y)/self.view.bounds.size.height);
    touchView.layer.position = newPos;
    [touchView.layer addAnimation:animation forKey:@"aaa"];
}

ref:blog.spacemanlabs.com/2011/08/cal…

IOS UIKit对CGPoint以及CGSize应用矩阵变换

CG_INLINE CGPoint
__CGPointApplyAffineTransform(CGPoint point, CGAffineTransform t)
{
  CGPoint p;
  p.x = (CGFloat)((double)t.a * point.x + (double)t.c * point.y + t.tx);
  p.y = (CGFloat)((double)t.b * point.x + (double)t.d * point.y + t.ty);
  return p;
}
 #define CGPointApplyAffineTransform __CGPointApplyAffineTransform

CG_INLINE CGSize
__CGSizeApplyAffineTransform(CGSize size, CGAffineTransform t)
{
  CGSize s;
  s.width = (CGFloat)((double)t.a * size.width + (double)t.c * size.height);
  s.height = (CGFloat)((double)t.b * size.width + (double)t.d * size.height);
  return s;
}
 #define CGSizeApplyAffineTransform __CGSizeApplyAffineTransform

UIGraphicsPushContext 和CGContextSaveGState 的区别

UIGraphicsPushContext(context) pushes context onto a stack of CGContextRefs (making context the current drawing context), whereas CGContextSaveGState(context) pushes the current graphics state onto the stack of graphics states maintained by context. You should use UIGraphicsPushContext if you need to make a new CGContextRef the current drawing context, and you should use CGContextSaveGState when you're working with one graphics context and just want to save, for example: the current transform state, fill or stroke colors, etc.

CoreGraphics for retina display

CGImages don't take into account the Retina-ness of your device, so you have to do so yourself.

To do that, you need to multiply all of your coordinates and sizes that use in CoreGraphics routines by the input image's scale property (which will be 2.0 on Retina devices), to ensure you do all your manipulation at double resolution.

Then you need to change the initialization of resultImage to use initWithCGImage:scale:orientation: and input the same scale factor. This is what makes Retina devices render the output at native resolution rather than pixel-doubled resolution.

+ (UIImage*)croppedImageWithImage:(UIImage *)image zoom:(CGFloat)zoom
{
    CGFloat zoomReciprocal = 1.0f / zoom;
    CGPoint offset = CGPointMake(image.size.width * ((1.0f - zoomReciprocal) / 2.0f), image.size.height * ((1.0f - zoomReciprocal) / 2.0f));
    CGRect croppedRect = CGRectMake(offset.x, offset.y, image.size.width * zoomReciprocal, image.size.height * zoomReciprocal);
    CGImageRef croppedImageRef = CGImageCreateWithImageInRect([image CGImage], croppedRect);
    UIImage* croppedImage = [[UIImage alloc] initWithCGImage:croppedImageRef scale:[image scale] orientation:[image imageOrientation]];
    CGImageRelease(croppedImageRef);
    return croppedImage;
}

用CALayer添加背景以及文字

-(void)showTodayInfo{
//先添加图形背景
    if(_infolayer!=nil){
        [_infolayer removefromsuperlayer];
    }
    cgfloat w=1/([uiscreen mainscreen].scale);
    cgfloat midx=cgrectgetmidx(self.bounds)-3;
    cgfloat midy=cgrectgetmidy(self.bounds)+3;
    cgfloat maxx=cgrectgetmaxx(self.bounds);
    uibezierpath* path=[uibezierpath bezierpath];
    [path movetopoint:cgpointmake(midx, w)];
    [path addlinetopoint:cgpointmake(maxx, w)];
    [path addlinetopoint:cgpointmake(maxx, midy)];
    [path closepath];
    cashapelayer* layer=[cashapelayer layer];
    layer.contentsscale=w;
    layer.fillcolor=[uicolor colorwithred:0.25 green:0.65 blue:0.2 alpha:0.7].cgcolor;
    layer.path=path.cgpath;
    [self.layer addsublayer:layer];
    self.infolayer=layer;
//再添加文字layer
    CATextLayer *label = [[CATextLayer alloc] init];
    [label setFontSize:10];
    [label setFrame:CGRectMake(midx+midx/2.0f-2, 0, 10, 10)];
    [label setString:@"今"];
    label.contentsScale=[[UIScreen mainScreen] scale];
    [label setAlignmentMode:kCAAlignmentCenter];
    [label setForegroundColor:[[UIColor whiteColor] CGColor]];
    [_infoLayer addSublayer:label];
    CATextLayer *label1 = [[CATextLayer alloc] init];
    [label1 setFontSize:10];
    [label1 setFrame:CGRectMake(midx+midx/2.0f+6, midy/2.0-6, 10, 10)];
    [label1 setString:@"天"];
    label1.contentsScale=[[UIScreen mainScreen] scale];
    [label1 setAlignmentMode:kCAAlignmentCenter];
    [label1 setForegroundColor:[[UIColor whiteColor] CGColor]];
    [_infoLayer addSublayer:label1];
}

对CALayer做标记的方法

You can also use the name property of the CALayer.

[layer setName:@"myKey"]; To look it up,

- (CALayer *)myLayer {
    for (CALayer *layer in [superLayerOfMyLayer sublayers]) {
            if ([[layer name] isEqualToString:LabelLayerName]) {
                return layer;
            }
    }
    return nil;
}

关于IOS7下的UIPickerView的高度问题

You are able to change the heights of the UIPickerView / UIDatePickerView, but only to the following values: 162 180 216

关于UIKeyboardFrameBeginUserInfoKey和UIKeyboardFrameEndUserInfoKey的区别

The UIKeyboardFrameBeginUserInfoKey will return the frame of the keyboard before the animation begins. The UIKeyboardFrameEndUserInfoKey will return the frame of the keyboard after the animation has completed. As an example, take the following snippet of code:

NSDictionary* info = [notification userInfo];
CGRect beginFrame = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

If you were to print the values of beginFrame and endFrame in the debug window, you might find something like this:

(gdb) print beginFrame
$1 = {
   origin = {
     x = 0, 
     y = 480
   }, 
   size = {
     width = 320, 
     height = 216
   }
 }
 (gdb) print endFrame
 $2 = {
   origin = {
     x = 0, 
     y = 264
   }, 
   size = {
     width = 320, 
     height = 216
   }
 }

So on an iPhone, this is showing that the keyboard will animate in from the bottom of the screen. The size of the keyboard doesn't change (as expected), but the y co-ordinates show the beginning and ending position of the keyboard.

ref:stackoverflow.com/questions/8…