编译kxmovie

KXMovie是基于ffmpeg的iOS播放器代码

下载KXMovie代码

地址:https://github.com/kinglonghuang

 git clone git://github.com/kinglonghuang/kxmovie.git

下载编译FFmpeg

在 https://github.com/FFmpeg/FFmpeg 中下载应该比clone快些,解压拷贝到kxmovie工程目录下的FFmpeg目录中。

下载gas-preprocessor.pl

地址:https://github.com/libav/gas-preprocessor

 sudo cp gas-preprocessor.pl /usr/bin sudo
 sudo chmod 777 /usr/bin/gas-preprocessor.pl

增加x86_64架构

用xcode打开kxmovie工程中的Rakefile文件,按i386的规则对应增加x86_64配置,保存关闭。

下载并编译x264

下载x264代码:http://www.videolan.org/developers/x264.html
下载x264 iOS build script:https://github.com/kewlbear/x264-ios

编译armv7架构库,成功后会在x264/armv7中生成include和lib目录。

./build-x264.sh armv7

如果报“Out of tree builds are impossible with config.h/x264_config.h in source dir.”,则删除x264中的config.h/x264_config.h。

增加x264支持

ffmpeg中只默认支持h264解码,没有支持编码,增强编译出的ffmpeg静态库功能。

以编译armv7为例,打开Rakefile,在FFMPEG_BUILD_ARGS_ARMV7中后面添加

'--enable-libx264',
'--enable-encoder=libx264',
'--enable-gpl',
"--extra-cflags='-I/xxx/workspace/kxmoviex/x264/armv7/include'",
"--extra-ldflags='-L/xxx/workspace/kxmoviex/x264/armv7/lib'"

修改为只执行armv7任务,

task :default => [:check_gas_preprocessor, :build_ffmpeg_armv7]

下载并编译faac

下载faac
http://nchc.dl.sourceforge.net/project/faac/faac-src/faac-1.28/faac-1.28.tar.gz
编译参考下面的链接,注意把编译脚本文件放到faac目录内
http://blog.csdn.net/cjj198561/article/details/38382889
https://github.com/EchoLiao/faac-prebuild/blob/master/build_ios.sh

增加faac支持

ffmpeg中自带的是acc编码

以编译armv7为例,打开Rakefile,在FFMPEG_BUILD_ARGS_ARMV7中后面添加

'--enable-libfaac',
'--enable-encoder=libfaac',
'--enable-nonfree',
"--extra-cflags='-I/xxx/workspace/kxmoviex/faac/build/install/armv7/include'",
"--extra-ldflags='-L/xxx/workspace/kxmoviex/faac/build/install/armv7/lib'"

重新编译ffmpeg

修改sdk版本号

用xcode打开kxmovie工程目录下的Rakefile,修改SDK_VERSION=’9.1’。或进入 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/,查看里面的目录快捷方式名中的版本号,或打开模拟器在关于中查看版本号。

运行

cd kxmovie
rake

执行rake命令

用xcode打开kxmovie工程目录下的Rakefile,修改SDK_VERSION=’9.2’。或进入 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/,查看里面的目录快捷方式名中的版本号,或打开模拟器在关于中查看版本号。

cd kxmovie
rake

如果报“XcodeDefault.xctoolchain/usr/bin/ranlib: archive member, does not match previous archive members cputype ”类似错误,清理对应目录中的.o文件。

在kxmoviex工程中,把x264/armv7中的库,和ffmpeg生成的库都加入到工程中。

附:架构小提示

F104CB3A425C674660FE506EF2B128C2

架构对应的设备:

 armv6 设备: iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch
 armv7 设备: iPhone3GS, iPhone4, iPhone4S;iPad, iPad2, iPad3(The New iPad), iPad mini;iPod Touch 3G, iPod Touch4
 armv7s设备: iPhone5, iPhone5C, iPad4(iPad with Retina Display)
 arm64 设备: iPhone5S, iPad Air, iPad mini2(iPad mini with Retina Display)

参考文章:
http://stackoverflow.com/questions/5056600/how-to-install-gas-preprocessor
http://www.cnblogs.com/sunminmin/p/4463741.html
http://blog.sina.com.cn/s/blog_864456e50101emmx.html

UITabBarController

创建TabBarItem示例

    [[UITabBar appearance] setTintColor:ThemeColor];
    
    UIViewController* viewController = [[PreferentialViewController alloc] initWithNibName:nil bundle:nil];
    UINavigationController* nav1 = [[UINavigationController alloc] initWithRootViewController:viewController];
    UIImage* selectedImage=[UIImage imageNamed:@"main_selected.png"];
    //使selectedImage不受着色影响
    selectedImage=[selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    UITabBarItem* tabBarItem = [[UITabBarItem alloc] initWithTitle:@"title" image:[UIImage imageNamed:@"main.png"] selectedImage:selectedImage];
    [tabBarItem setTitlePositionAdjustment:UIOffsetMake(0, -3)];
    nav1.tabBarItem = tabBarItem;

UIScrollView

选中UIButton后滑动UIScrollView无效

重写touchesShouldCancelInContentView方法

-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
    return YES;
}

循环滚动效果

实现思路:内部封装一个UIScrollView,它的contentSize占3个页面,动态控制这3个页面对应的view。即内部适配了一个scrollView。

特殊地,只有一个页面时,不需要显示页码指示,也不需要循环滚动。有2个页面时,也不需要循环滚动,因为向左或向右拖动时的耗费是相同的。

KeyChain

iOS系统有一个KeyChain,每个程序都可以往KeyChain中记录数据,而且只能读取到自己程序记录在KeyChain中的数据。iOS中Security.framework框架提供了四个主要的方法来操作KeyChain

 // 查询
 OSStatus SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result);
 // 添加
 OSStatus SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result);
 // 更新
 OSStatus SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
 // 删除
 OSStatus SecItemDelete(CFDictionaryRef query)

苹果还允许同一个开发商的各APP之间互访数据,即在调SecItemAdd添加数据的时候指定AccessGroup访问组,一个APP可以同时属于多个组。

UIView

设置亮度

通过修改alpha可以调节亮度,但会出现透明问题。还可以通过添加一个子view来调节亮度。

-(void)setAdView:(UIView*)adView opacity:(float)opacity
{
    if(opacity<1.0)
    {
        UIView* v=[adView viewWithTag:KMaskViewTag];
        if(!v)
        {
            v=[[[UIView alloc] initWithFrame:adView.bounds] autorelease];
            v.backgroundColor=[[UIColor blackColor] colorWithAlphaComponent:opacity];
            v.tag=KMaskViewTag;
            v.userInteractionEnabled=NO;
            [adView addSubview:v];
            
            v.alpha=0.0;
            [UIView animateWithDuration:0.30 animations:^{
                v.alpha=1.0;
            }];
        }
    }
    else
    {
        UIView* v=[adView viewWithTag:KMaskViewTag];

        [UIView animateWithDuration:0.30 animations:^{
            v.alpha=0.0;
        } completion:^(BOOL finished) {
            [v removeFromSuperview];
        }];
    }
}

viewWithTag

viewWithTag方法会在所有子view中递归查询,返回找到的第一个视图。

侧移效果实现过程

对框架view照相,把要显示的新view添加到框架view中,把照相view添加到框架view中,对照相view执行侧移动画,即动画中改变它的frame。相反的过程恢复到原状态。

    UIView* mainView = self.navigationController.view;
    UIImage* captureImage = [mainView captureView];

    [mainView addSubview:newView];
    UIImageView* theCaptureView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, mainView.frame.size.width, mainView.frame.size.height)];
    theCaptureView.backgroundColor = [UIColor whiteColor];
    [theCaptureView setImage:captureImage];
    [mainView addSubview:theCaptureView];
    [UIView animateWithDuration:0.25f animations:^{
        [theCaptureView setFrame:CGRectMake(-245, 44, mainView.frame.size.width, mainView.frame.size.height-44*2)]; //320-245=75
    } completion:^(BOOL finished) {
        theCaptureView.userInteractionEnabled = YES;
        UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(recoveryView:)];
        [theCaptureView addGestureRecognizer:tap];
    }];
- (void)recoveryView:(id)sender
{
    UIView* mainView = [self.navigationController view]; n]
    if (theCaptureView) {
        [UIView animateWithDuration:0.25f animations:^{
            [theCaptureView setFrame:CGRectMake(0, 0, mainView.frame.size.width, mainView.frame.size.height)];
        } completion:^(BOOL finished) {
            [theCaptureView removeFromSuperview];
            [theNewView removeFromSuperview];
        }];
    }
}

- (UIImage*)captureView
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage* screenImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return screenImg;
}

contentMode

按钮中的图片等比适应按钮的尺寸

imageView.clipsToBounds=YES;
button.imageView.contentMode=UIViewContentModeScaleAspectFill;
button.imageView.contentMode=UIViewContentModeScaleAspectFit;

图片全圆角

imageView_.layer.cornerRadius = 15;//width/2
imageView_.layer.masksToBounds = YES;

如果需要全圆角边框可以在父view相同位置插入一个稍大一点的全圆角背景图,或尝试设置border。

 

UIImage

imageName会把image缓存到手机内存里,不适合大量图片浏览会越来越卡。

imageWithContentFile是只显示图片而不加载到手机内存里。所以在出来大量图片浏览的时候要用imagewithcontentfile。

尺寸

父view尺寸小,默认情况下内部的大按钮仍会显示全。

objc动态特性

关联对象

相当于android中的setObject

要引入 <objc/runtime.h>

设置关联:

objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN);

获取关联的对象:

NSString * associatedObject = (NSString *)objc_getAssociatedObject(array, &oveviewKey);

断开关联也是使用objc_setAssociatedObject函数,设置被关联的对象为nil,此时关联策略可任意:

objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN);

生成推送证书

1.生成cer证书文件
点击App IDs,点击应用对应的AppID,如果看到Push Notifications后为Configurable,说明还未正常启用推送功能;
点击Edit,在下方下载证书;或Create Certificate…,需要上传CSR文件。

2、把SSL证书.cer文件转换为.pem文件:

openssl x509 -in reading_development.cer -inform der -out reading_development.pem
openssl x509 -in reading_production.cer -inform der -out reading_production.pem

3、把私钥.p12文件转换为.pem文件:

openssl pkcs12 -nocerts -in wwk_develope.p12 -out reading_development_key.pem
openssl pkcs12 -nocerts -in wwk_distribute.p12 -out reading_production_key.pem

这里需要输入导出.p12文件时的设置的密码。然后,需要对生成的pem文件设置一个密语,这个密语在连接服务器时使用。

4、把前面生成的这两个pem文件(证书和私钥)合并成一个pem文件:

cat reading_development.pem reading_development_key.pem > reading_development_result.pem
cat reading_production.pem reading_production_key.pem > reading_production_result.pem

这3步就完成了推送证书的生成,下面的步骤是测试证书及连接是否可用。

5.测试能否连接到APNS

telnet gateway.sandbox.push.apple.com 2195

发送一个规则的,不加密的连接到APNS服务。要确保防火墙允许2195端口。

6.使用2、3步生成的SSL证书和私钥pem文件测试链接苹果服务器:

openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert reading_development.pem -key reading_development_key.pem

需要输入第2步设置的密语

然后有大段输出。如果链接成功,随便输入一个字符后按下回车,服务器就会断开链接。如果连接异常OpenSSL会返回错误信息。

如果在最后看到下面的内容说明成功了:
CONNECTED(00000003)

Verify return code: 0 (ok)

或者看到像这样的提示:verify error:num=20:unable to get local issuercertificate verify return:0 其实也是没问题的。

URL Schema

配置URL Schema的作用

在本应用程序的.plist文件中配置URL Schema目的是供其它应用通过这个schema启动本应用程序。

实现分享功能配置一堆URL Schema

在应用中实现分享内容到微博,微信,qq等一堆开放平台中功能,需要根据各开放平台的规则要求,配置一堆schema,其schema规则一般是固定标识+在其平台上注册到的应用id,分享完成后,这些社交软件会询问是留在这里还是回到原应用,通过这个schema回调就会回到我们的应用程序。

Block

Block简介

  • Block 是 iOS 4.0 和 Mac OSX 10.6 引入的一个新特性。
  • 没有 Block 时, 一般会用代理或者 NSNotificationCenter回调一个方法,在一个地方开启了一个任务, 然后在另外一个地方处理它的结果。Block能将和一个任务相关的所有代码都放在一个地方。
  • 在其它语言中 Block 又叫做闭包, 因为他们包含了定义时的状态。 Block 会为所有和它在同一作用范围内的局部变量创建一个常量拷贝。
  • Block 是一个标准 Objective-C 对象, 可以作为参数传递, 作为方法或函数的返回值, 赋值给变量。

Block 的声明格式

return_type (^block_name)(param_type, param_type, ...)

^符号表示定义的是一个 Block。
代码示例:

 int (^add)(int,int)

Block 的定义格式:

 ^return_type(param_type param_name, ...) { ... return return_type; }

定义 Block 时, 返回值类型可选,可以继承它里面代码的返回值类型
示例:

 ^(int number1, int number2){ return number1+number2; }

声明并定义Block

示例:

 int (^add)(int,int) = ^(int number1, int number2)
 {
 return number1+number2;
 };

循环引用(retain-cycle)问题

如果这个block没有使用当前实例的任何成员,那么当前实例不会被增加引用计数。
如果objc类实例方法中的block如果被复制至heap,那么当前实例会被增加引用计数,当这个block被释放时,此实例会被减少引用计数。但若block和当前实例互相直接或间接retain,则会出现循环引用问题。

解决方法:不直接使用self而先将self赋值给一个__block临时变量,然后再使用这个临时变量。示例:

__block DetailViewController* tempSelf = self;
//或者 __block typeof(self) tempSelf = self;
 [replyViewController_ setFinishBlock:^{
 [tempSelf method];
 tempSelf->member=0;
 }];

__block关键字的作用是让block取消对tempSelf的强参照,以避免循环参照。在ARC下,要使用__weak不是 __block来避免block循环引用;

若出现typeof(self) Expected ‘;’ at end of declaration编译错误,需要修改设置 Apple LLVM 5.1 Language C Language Dialect 值为 GNU99 [-std=gnu99]

You do not have to jump through this hoop for every block you make because most blocks have a very short lifespan. Just be aware that if you have a block that has the same lifetime as its owning object and you are freeing the block at -dealloc time, you can get a retain cycle.

http://www.informit.com/articles/article.aspx?p=1749597&seqNum=10

外部变量问题

若非即执行的block中外部变量是可变的,则注意在每次调用时要重新生成一个block。

 BOOL isFriends=[INSTANCE.contextData isFriendWithUser:userInfo];
 if(!replyViewController_)
 {
 replyViewController_=[[ReplyChatViewController alloc] initWithPlaceholder:@"打个招呼吧"];
 }
 //每次要重新设置block,因为其使用的外部变量值每次不同。
 __block MapViewController* tempself=self;
 [replyViewController_ setFinishBlock:^{
 if(!isFriends && INSTANCE.serverSetting.costMakeFriends>0)
 {
 [tempself performSelector:@selector(submitPayOffer) withObject:nil afterDelay:0.5];
 }
 }];
 [replyViewController_ showAnimated:self.view.window];