【转】iOS中的一些宏定义,用于进行log输出定位Bug。

转自:http://www.linuxidc.com/Linux/2012-12/76754.htm

我该如何在日志输出信息中添加上下文信息,例如当前方法或者行号。

C预处理器提供了一些标准宏,可以提供当前文件,行号,或者函数的信息。另外,Objective-C有_cmd隐式参数,可以提供当前函数的选择器,以及将选择器和类转换为字符串的功能。你可以在调试或者错误处理时在NSLog语句中提供这些上下文信息。

下面是打印当前方法和行号的例子。

NSMutableArray *someObject = [NSMutableArray array];
NSLog(@"%s:%d someObject=%@", __func__, __LINE__, someObject);
[someObject addObject:@"foo"];
NSLog(@"%s:%d someObject=%@", __func__, __LINE__, someObject);

下面是在日志语句中很有用的非常常见的宏和表达式。

C/C++/Objective-C中用于日志输出的预处理宏.

Macro Format Specifier Description
__func__ %s 当前函数前面
__LINE__ %d 源码文件中的行号
__FILE__ %s 源码文件完整路径
__PRETTY_FUNCTION__ %s 和__func__类似, 但是在 C++ 代码中包含更多的信息.

Objective-C中用于日志输出的表达式

Expression Format Specifier Description
NSStringFromSelector(_cmd) %@ 当前选择器的名字
NSStringFromClass([self class]) %@ 当前对象类的名字
[[NSString stringWithUTF8String:__FILE__] lastPathComponent] %@ 源码文件的名称
[NSThread callStackSymbols] %@  

当前栈信息的刻度字符串数组。仅用于调试,不用向终端用户展示或者在代码中用作任何逻辑。

 

题目:Max Ascending

问题比较简单,就是一个数组a[n] 求max(ai-aj), i<j.

从前往后扫描,记录下当前的最小值,每次计算aj – ai,然后更新这个最大值。扫描完就得到了结果,时间复杂度O(n)

<

pre>
//
// main.cpp
// MaxAscend
//
// Created by Qiu Xiangyu on 12-12-14.
// Copyright (c) 2012年 Qiu Xiangyu. All rights reserved.
//

//一个数组a[n] 求max(ai-aj), i<j

include

include

using namespace std;
int maxAscend(vector nums) {
if (nums.size() <= 1) {
return 0;
}
int ret = nums[1] – nums[0];//the result
int vmin = nums[0];//track the current min
for (int i = 1; i < nums.size(); ++i) {
int v = nums[i];
if (vmin > v) {
vmin = v;
}
if (ret < v – vmin) {
ret = v – vmin;
}
}
return ret;
}

int main(int argc, const char * argv[])
{
vector testarr = {1,4,2,5,7,0};
int md = maxAscend(testarr);
cout<<“Max difference is “<<md<<endl;
return 0;
}

iOS开发中的UISearchBar背景替换,自定义UISearchBar背景

UISearchBar的背景在5.0之前并没有提供SDK来替换。

但是实际应用中又常常需要替换掉默认的背景,于是要么自己重写一个searchbar要么寻求别的方法。好在于在SDK5.0之后,提供了一个简单的方法来做:

@property(nonatomic,retain) UIImage *backgroundImage NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

在5.0之前怎么办呢?可以用下面的办法(未经测试,目测靠谱):

    for (UIView *sub in self.searchBar.subviews) {
        if ([sub isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
            //this is the bkg view, hide it and insert another view
            UIImageView *newbkg = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"newbkg"]];
            newbkg.frame = sub.frame;
            [self.searchBar insertSubview:newbkg belowSubview:sub];
            [newbkg release];
            sub.hidden = YES;
            break;
        }
    }

解决iOS开发中调用UIScrollView或UITableView的setContentOffset方法产生的抖动

现在“下拉刷新”和“上拉加载更多”都是常用的移动端程序的设计了。

实现的话,下拉刷新有现成的开源库:EGOTableViewPullRefresh

上拉加载更多的话,自己研究,写了一个类似的tableViewCell。但是在ScrollViewDidEndDragging方法中修改scrollView的setContentInset时发生了抖动。

花了挺长时间研究,最后发现原因在于当调用setContentInset的时候scrollView的contentOffset会跟着一起变化(由于是设置inset的bottom,所以scrollview会自动到最底端),然后由于是在ScrollViewDidEndDragging,视图还要从以前的位置动画回到scrollview的末尾(bounce效果)。于是修改inset之后造成的当前contentOffset位置就和bounce动画的初值不等,造成了抖动。

解决办法也很简单,就是在setContentInset之后,立马重设一下contentOffset。

        float contentTop = scrollView.contentOffset.y;
        [scrollView setContentInset:oriIns];
        [scrollView setContentOffset:CGPointMake(0, contentTop) animated:NO];

iOS开发中利用MFMessageComposeViewController发送短信

从SDK4.0开始,就可以在程序内使用MFMessageComposeViewController来发送短信了(如果设备支持的话)。

废话少说,代码如下:

self.msgCtrl = [[MFMessageComposeViewController alloc] init];
            [self.msgCtrl release];
            if (![MFMessageComposeViewController canSendText]) {
                UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"设备不支持短信发送" message:nil delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:nil];
                [av show];
                [av release];
                return;
            }
            [self.msgCtrl setBody:@"短信内容"];
            [self.msgCtrl setRecipients:[self.selectedContactPhoneDict allKeys]];
!!!:            self.msgCtrl.messageComposeDelegate = self;
            [self presentModalViewController:self.msgCtrl animated:YES];

特别是标注了!!!的这一行,坑爹的delegate名字是messageComposeDelegate,这样才会触发下面的回调:

-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
    switch (result) {
        case MessageComposeResultCancelled:
            [[TKAlertCenter defaultCenter] postAlertWithMessage:@"已取消短信发送"];
            break;
        case MessageComposeResultSent:
            [[TKAlertCenter defaultCenter] postAlertWithMessage:@"成功发送短信"];
            break;
        case MessageComposeResultFailed:
            [[TKAlertCenter defaultCenter] postAlertWithMessage:@"短信发送失败"];
            break;
        default:
            break;
    }
    [self dismissModalViewControllerAnimated:YES];
}

iOS开发中的NSDate时区问题

iOS设备上获取的时间,呵呵,不好说,有时候是GMT时间(比如[NSDate date]),有时候是本地时间,比如用位置传感器传回来的时间戳。而且现在的应用国际化了,还是要考虑时区问题。

在iOS中,处理时区问题是用NSTimeZone来进行的,下面是一个简单的例子。

-(void)testTime{
    NSDate *now = [NSDate date];//根据当前系统的时区产生当前的时间,绝对时间,所以同为中午12点,不同的时区,这个时间是不同的。
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    df.timeZone = [NSTimeZone systemTimeZone];//系统所在时区
    df.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    NSString *systemTimeZoneStr =  [df stringFromDate:now];
    df.timeZone = [NSTimeZone defaultTimeZone];//默认时区,貌似和上一个没什么区别
    NSString *defaultTimeZoneStr = [df stringFromDate:now];
    df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:8 * 3600];//直接指定时区
    NSString *plus8TZStr = [df stringFromDate:now];
    df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];//这就是GMT+0时区了
    NSString *gmtTZStr = [df stringFromDate: now];
    NSLog(@"Test Time\nSys:%@\nDefault:%@\n+8:%@\nGMT:%@",systemTimeZoneStr,defaultTimeZoneStr,plus8TZStr,gmtTZStr);
}

该函数运行的结果是:(在+8区,当地时间19:06:07运行)

Test Time
Sys:2013-06-30 19:06:07
Default:2013-06-30 19:06:07
+8:2013-06-30 19:06:07
GMT:2013-06-30 11:06:07

利用XCode中Interface Builder的Runtime Attribute来设置运行时参数,比如圆角

要设置一个view的圆角,可以通过在代码里面写上:

aView.layer.cornerRadius = 10

这样的内容来将其圆角设为10px。



其实在interface builder中,完全可以设置圆角,不用写一行代码:

选择要设置圆角的view之后,在这个页面下可以设置其runtime attribute,只需要添加一个keypath,设置好它的类型和值就可以了。
这里例子中,keypath是“layer.cornerRadius”,类型是“Number”,值是“10”。
不过要记得将view的clipSubviews设为true哦。

旧项目在iOS6中链接不过的问题,提示缺armv7s

升级了xcode之后,支持iOS6和iPhone5,不过Build项目的时候,出现错误提示信息:

ld: file is universal (3 slices) but does not contain a(n) armv7s slice

实际上是引用的第三方库导致了这个链接错误。

解决办法有三个,随便哪种都能解决:
1.升级涉及到的.a文件
2.在target的Build Settings里面,将Build Active Architecture Only改成YES
3.在target的Build Settings里面,找到Valid Architectures,删除其中的armv7s

所以还是觉得,如果是开源库,直接把源代码包含进项目比较靠谱。

viewWillAppear不执行的问题

今天遇到了viewWillAppear函数不执行的问题,最后找到了症结所在。
<br>
那就是使用了UINavigationControllerDelegate,但是没有在相应的函数中显示调用即将显示的viewController的viewWillAppear方法。
系统的UINavigationControllerDelegate会自动调用这4个方法,但是如果是自己重载了delegate的话,就需要手动去调用。

iOS中的文件存放法则

Apple对应用程序放在沙盒中的文件有严格要求,主要有:

    存放位置要求

  1. 用户创建的文件,(程序不能自动生成的),需要放在Documents\
  2. 缓存文件,需要放在Library\Caches\
  3. 临时文件,放在tmp\,而且要注意清空
    文件备份
    这个可以通过设置文件的一个属性来控制,具体见下面代码

  1. 除了用户创建和编辑的文件,不允许保存到iTunes和iCloud
  2. 用户升级程序之后,所有Documents\和Library\的文件会自动复制到新的bundle中去



下面的代码是如何设置属性,让apple在备份的时候,不会包含这个文件。
对于iOS版本5.1之前和之后的处理方式是不一样的。

+(BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    if (![[NSFileManager defaultManager] fileExistsAtPath: [URL path]]) {
        return NO;
    }
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 5.1) {
        NSError *error = nil;
        BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
                                      forKey: NSURLIsExcludedFromBackupKey error: &error];
        if(!success){
            NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
        }
        return success;
    } else {
        const char* filePath = [[URL path] fileSystemRepresentation];
        const char* attrName = "com.apple.MobileBackup";
        u_int8_t attrValue = 1;
        int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
        return result == 0;
    }
}

UITableView的背景颜色以及层次研究

UITableView的背景颜色,并不是UITableView的backgroundColor。
其内部实际上创建了一个UIView来专门呈现设定的背景颜色。
感觉它的subview层次从下往上是这样的:

  1. 这个呈现backgroundColor的view
  2. 放置其中的cell们
  3. scroll indicators

而且每次添加一个cell,tableView会自动将这个cell放置在backgroundView的上面一层,而且UITableView的sendViewToBack和bringToFront函数应该是重新定义过的,并不会按照普通UIView的方式去执行。

iOS开发中混合使用ARC和非ARC项目

SDK5.0引入了ARC,到现在已经一年了,开始发现有很多项目会混合使用这两个方案。比如:

1.自己的旧项目没有使用ARC,但是引入的第三方库却是使用了ARC的。
2.自己的新项目使用了ARC,但是引入的第三方库或者以前写的代码却没有使用ARC。

这两种情况下,直接肯定是通不过编译的。可以通过升级旧项目,让其使用ARC来解决,但这个办法有时候会很麻烦。
有一个简单的办法就是,可以指定单个文件是否采用ARC来进行编译。
方法就是在Build Phase里面的Compile Source里面找到需要特殊处理的文件,加上编译选项(Compiler Flags),具体针对上面两种情况有所区别。

1.对于第一个情况,给采用了ARC的源文件,添加-fobjc-arc选项
2.对于第二种情况,添加-fno-objc-arc

此外,xcode貌似有点问题,在点击某个源文件的Compiler Flags条目的时候,应该显示光标的地方却什么也没有提示,输入字符也没有echo,只有敲完之后,选择其它文件才能看到添加的编译选项···这真无语。。

<br>
对于新写的各种插件,可以这么干:

-(void)dealloc{
    //do something in common
#if !__has_feature(objc_arc)
    [super dealloc];
#else
    //nothing
#endif
}

让它在arc和非arc都可用。

如何解决iOS瀑布流(UIScrollView或UITableView)运行不流畅

写的一个程序中用到了瀑布流的展现方式,但是发现当图片数量太大的时候,在iPhone4上会不流畅,这点很不爽。

写代码之初是做了一些优化的,比如cell重用,异步加载,但是还是很卡。

终于后来发现了症结所在,那就是,如果滑动太快,可能同时就发出了比如10个图片请求。这些请求虽然都在后台运行,但是它们可能在同一个时间点返回UI线程。这个时候如果加载图片到UIImageView太频繁,就会造成UI卡得严重。(虽然在new iPad和iPhone4s上看不出来)

在找到这个问题的同时,也发现performSelectorAfterDelay这个方法,会堆积到UI线程空闲的时候执行。而dispatch_after或者dispatch_async都会直接插入UI线程当场执行。所以这个问题其实可以用performSelectorAfterDelay来解决,测试也是非常流畅,感觉不出一点点的卡。但会出现新的问题,那就是在滑动过程中,不会加载任何图片。知道scrollView停止的时候,图片才会出来。当然这不是理想的解决方法了。这个方法也没有解决异步过程集中到达UI线程的问题。然后采用了NSOperationQueue来解决这个问题。

问题本身和UITableView加载不流畅是一样的。

解决办法

    主要要做到一下几个方面:

  1. 除了UI部分,所有的加载操作都在后台完成。
    这一点可以通过dispatch或者performSelectorInBackground或者NSOperationQueue来实现。见:
    在iOS开发中利用GCD进行多线程编程
    iOS开发中使用NSOperationQueue进行多线程操作
  2. 避免后台加载完成多个资源之后集中到达占用UI线程的处理时间太长。
    这一点可以通过NSOperationQueue来实现,将资源到UI的展现过程放在队列中逐个执行,且在每个操作完成之后进行强制等待,可以用usleep(int microSeconds)来解决。
  3. 重用cell。
    创建cell一般是很慢的,一定要重用,甚至为了performance,可以在view创建之初就创建足够多的cell在重用队列中。

iOS中UILabel滚动字幕动画的实现

有时候会遇到UILabel中的内容超出长度,显示不完全的问题。有一种解决方法是通过动画字幕来实现,比如:

  1. 字幕向左或者右滚动
  2. 字幕来回滚动

本文以后者为例来说明吧。这里先介绍UIView的通过Block实现的Animation以及其参数控制,最后是实现滚动字幕的代码。

  1. UIView有方便的动画实现方式,SDK4.0以上,提供了三个Block的动画方式:

    + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0);
    
    + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0); // delay = 0.0, options = 0
    
    + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0); // delay = 0.0, options = 0, completion = NULL
    

    其中第一个最全面,可以设置UIViewAnimationOptions来控制动画的参数,比如重复,自动reverse之类的。

  2. UIViewAnimationOptions具体定义如下:

    enum {
        UIViewAnimationOptionLayoutSubviews            = 1 <<  0,
        UIViewAnimationOptionAllowUserInteraction      = 1 <<  1, // turn on user interaction while animating
        UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2, // start all views from current value, not initial value
        UIViewAnimationOptionRepeat                    = 1 <<  3, // repeat animation indefinitely
        UIViewAnimationOptionAutoreverse               = 1 <<  4, // if repeat, run animation back and forth
        UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5, // ignore nested duration
        UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6, // ignore nested curve
        UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7, // animate contents (applies to transitions only)
        UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8, // flip to/from hidden state instead of adding/removing
        
        UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
        UIViewAnimationOptionCurveEaseIn               = 1 << 16,
        UIViewAnimationOptionCurveEaseOut              = 2 << 16,
        UIViewAnimationOptionCurveLinear               = 3 << 16,
        
        UIViewAnimationOptionTransitionNone            = 0 << 20, // default
        UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
        UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
        UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
        UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
        UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
        UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
        UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
    };
    typedef NSUInteger UIViewAnimationOptions;
    
  3. 本文实现方法就是使用Animation with Block的方式来实现UILabel来回滚动。代码如下:

    -(void)startAnimationIfNeeded{
        //取消、停止所有的动画
        [self.aUILabel.layer removeAllAnimations];
        CGSize textSize = [self.aUILabel.text sizeWithFont:self.aUILabel.font];
        CGRect lframe = self.aUILabel.frame;
        lframe.size.width = textSize.width;
        self.aUILabel.frame = lframe;
        const float oriWidth = 180;
        if (textSize.width > oriWidth) {
            float offset = textSize.width - oriWidth;
            [UIView animateWithDuration:3.0
                                  delay:0
                                options:UIViewAnimationOptionRepeat //动画重复的主开关
             |UIViewAnimationOptionAutoreverse //动画重复自动反向,需要和上面这个一起用
             |UIViewAnimationOptionCurveLinear //动画的时间曲线,滚动字幕线性比较合理
                             animations:^{
                                 self.aUILabel.transform = CGAffineTransformMakeTranslation(-offset, 0);
                             }
                             completion:^(BOOL finished) {
                                 
                             }
             ];
        }
    }
    

xcode换了电脑之后无法在真机调试(无code sign)

没有了mac本,只好在台式机的虚拟机内装一个mac来进行开发,但是却遇到一个问题:
以前在mac本上使用好好的developer apple id,在新环境下无法进行开发。xcode会自动去获取各种证书和provisioning文件,但是在codeSign下拉列表中就是不见可用的code sign选项。
苹果开发者网站上的流程走了几遍也没有用,花了好多时间,不过最终在support里面找到了解决方法:
1.删除key chain里面所有和开发账户相关的内容
2.删除xcode organizer里面的所有无效provisioning
3.需要在开发者网站portal里面找到certificate,先点一下”Revoke”
4.点xcode organizer provisioning标签下的”Refresh”(右下角)

到此终于搞定。

关于人人开放平台中的Like.like接口

人人开放平台的Like.like接口,是用于标记用户喜欢的内容,相当于新浪微博的收藏。
发现这个接口总返回成功,确没有“喜欢”成功。
在调试了很久以后,终于发现它的规律:
主要是其url参数:其中需要的是resourceId,ownerID,type,这中间大有猫腻(再次不得不说一下人人的api之混乱)

对于share类型的
包括日志share(代码21),相册share(代码33)需要传送的是source_id,actor_id和@”share”

对于上传照片(30)类型
需要传送的是attachement下的meida_id,owner_id和media_type

对于发布日志(20)类型
需要传送的是source_id,actor_id和@”blog”

这样才能“喜欢”成功,刷新“首页”或者“个人主页”可以看到效果,但是要在”喜欢列表“里面看到内容的话,貌似需要一天以后···额···并且人人没有提供获取这个列表的api···

iOS开发中的键盘高度变化处理

在ios开发中,键盘很常用。在sdk版本5.0以前,键盘高度是固定值216px;5.0出来以后,键盘高度会随着键盘语言变化(中文要高些),在这种情况下一般而言对于界面需要重新布局。方法是利用NSNotificationCenter。

UIKeyboardWillShowNotification;
UIKeyboardDidShowNotification; 
UIKeyboardWillHideNotification; 
UIKeyboardDidHideNotification;

这几个notification是5.0sdk之前就有的,顾名思义就知道意思了。

UIKeyboardWillChangeFrameNotification  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
UIKeyboardDidChangeFrameNotification   __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);

这两个是sdk 5.0以后出来的,用来处理键盘高度的变化。

使用方法是:首先在notification注册观察者,比如:

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0) {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
}

当键盘高度将要变化时,就会收到通知,在通知的参数中可以得到键盘目前的高度和变化的目标高度,比如:

-(void)keyboardWillChangeFrame:(NSNotification*)notif{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2  
    NSValue *keyboardBoundsValue = [[notif userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey];  
#else  
    NSValue *keyboardBoundsValue = [[notif userInfo] objectForKey:UIKeyboardBoundsUserInfoKey];  
#endif
    CGRect keyboardEndRect = [keyboardBoundsValue CGRectValue];
    CGRect inputFrame = self.feedBackTextView.frame;
    //kb 216 vs textFrame 185
    float delta = keyboardEndRect.size.height - 216;
    float originalHeight = inputFrame.size.height;
    inputFrame.size.height = 185 - delta;
    if (inputFrame.size.height != originalHeight) {
        self.feedBackTextView.frame = inputFrame;
        self.feedBackBackgroundView.frame = inputFrame;
    }
}

另外一些从notification.userInfo中可以取得的key如下:

UIKeyboardFrameBeginUserInfoKey        // NSValue of CGRect
UIKeyboardFrameEndUserInfoKey          // NSValue of CGRect
UIKeyboardAnimationDurationUserInfoKey // NSNumber of double
UIKeyboardAnimationCurveUserInfoKey    // NSNumber of double



notif中userInfo的完整信息如下

keyboardChange:{
    UIKeyboardAnimationCurveUserInfoKey = 0;
    UIKeyboardAnimationDurationUserInfoKey = "0.25";
    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}";
    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}";
    UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 588}";
    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}";
    UIKeyboardFrameChangedByUserInteraction = 0;
    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}";
}



下面是一个完整的解决方案,用户需要知道键盘高度的细致变化

#pragma mark Keyboard
-(void)keyboardWillChangeFrame:(NSNotification*)notif{
    NSLog(@"keyboardChange:%@",[notif userInfo]);
    float keyboadHeightBegin = 0;
    float keyboadHeightEnd = 0;
    float animationDuration = [[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
    UIViewAnimationCurve animationCurve = [[[notif userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2
    CGRect keyboardBeginFrame = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardEndFrame = [[[notif userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    keyboadHeightBegin = 480 - keyboardBeginFrame.origin.y;
    keyboadHeightEnd = 480 - keyboardEndFrame.origin.y;
#else
    //these deprecated after iOS 3.2
    CGRect keyboardBounds = [[[notif userInfo] objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue];
    CGPoint keybordCenterBegin = [[[notif userInfo] objectForKey:UIKeyboardCenterBeginUserInfoKey] CGPointValue];
    CGPoint keybordCenterEnd = [[[notif userInfo] objectForKey:UIKeyboardCenterEndUserInfoKey] CGPointValue];
    keyboadHeightBegin = 480 - (keybordCenterBegin.y - keyboardBounds.size.height / 2);
    keyboadHeightEnd = 480 - (keybordCenterEnd.y - keyboardBounds.size.height / 2);
#endif
    NSLog(@"keyboardHeightChangeFrom:%.2f,To:%.2f",keyboadHeightBegin,keyboadHeightEnd);
    return;
    if (keyboadHeightEnd > 0) {
        //keyboard show or change frame
        [UIView animateWithDuration:animationDuration delay:0 options:animationCurve animations:^{
        } completion:^(BOOL finished) {
        }];
    } else {
        //keyboard hide
    }
}
-(void)keyboardDidChangeFrame:(NSNotification*)notif{
    //info like willChangeFrame
}
-(void)keyboardWillShow:(NSNotification*)notif{
    //keyboard height will be 216, on iOS version older than 5.0
    [UIView animateWithDuration:0.3f animations:^{
        self.contentTableView.height = 480 - 44 - 216;
    }];
}
-(void)keyboardWillHide:(NSNotification*)notif{
    [UIView animateWithDuration:0.3f animations:^{
        self.contentTableView.height = 480 - 44 - 28;
    }];
}
-(void)registerKeyboardEvent{
    float systemVer = [[[UIDevice currentDevice] systemVersion] floatValue];
    if(systemVer >= 5.0) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nil];
    } else {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    }
}
-(void)unregisterKeyboardEvent{
    if([[[UIDevice currentDevice] systemVersion] floatValue] > 5.0) {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidChangeFrameNotification object:nil];
    } else {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];        
    }
}



下面这个解决方案就只考虑键盘出现和消失的处理

#pragma mark Keyboard
-(void)keyboardWillShow:(NSNotification*)notif{
    //keyboard height will be 216, on iOS version older than 5.0
    CGRect boxFrame = self.loginBoxView.frame;
    boxFrame.origin.y = 50;
    [UIView animateWithDuration:0.3f animations:^{
        self.loginBoxView.frame = boxFrame;
    }];
}
-(void)keyboardWillHide:(NSNotification*)notif{
    CGRect boxFrame = self.loginBoxView.frame;
    boxFrame.origin.y = 216;
    [UIView animateWithDuration:0.3f animations:^{
        self.loginBoxView.frame = boxFrame;
    }];
}
//在viewdidload中调用
-(void)registerKeyboardEvent{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
//在viewdidunload中调用
-(void)unregisterKeyboardEvent{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}

iOS开发中创建和使用静态链接库(.a)

创建静态库项目之后,Build,然后就能看到Product中的.a文件亮了,然后能把它拷出来加到别的项目中去使用,但是貌似.h文件没有在这个地方出现,还是得手动去找?
反正我是直接在目录下搜.h文件,全部拷贝和.a文件一起加入目标项目来使用是可以的。

另外貌似要区分iphone-simulator和iphone两个不同scheme生成的.a文件,不能混用。
今天(5月18)验证了一下,确实是这样的,需要区别对待,可以在target是iphone下编译一个.a,再在simulator下编译一个.a。两个.a同时拷贝到目标项目才行。不然的话,链接阶段会报错,这个挺烦人。。应该有解决办法,目前没时间找。
合并模拟器和设备的静态库实际很简单,如果一个是somelib.sim.a,一个是somelib.device.a,只需要在命令行运行:
lipo -create somelib.sim.a somelib.device.a -output somelib.a
就可以了

参考:
http://xys289187120.blog.51cto.com/3361352/786930
http://blog.csdn.net/flyhawk007j2me/article/details/6762798

iOS内存Bug

一个bug找了我好长时间····

UIView *view = array.lastObject;
if (view) {
[[view retain] autorelease];
[array removeLastObject];
}
return view;
[[view retain] autorelease];

这一句话不加害死人啊··