喜欢苹果,痴情技术! 专注于移动互联网,做出好产品改变世界^^! My Github https://github.com/yushuyi
引言: Git 做为当前最流行的分布式版本控制系统,对于我们来说已经不是什么陌生的东东了.博主经历了一段时间学习和使用以后,发现想要玩好Git,还是有很多细节要弄清楚的.得益于SourceTree的强大,博主自以为已经玩转了Git,但是后来才发现,半路出家的我在遇到一些问题时,还真有点无从下手.终归原因是自己没有真正弄懂Git的原理,此时惯用工具的诟病就体现出来了.在此也提醒大家,当你在使用Git工具时,一定得清楚对应的命令和原理.不然,面对一个个的按钮,点起来都会有畏惧感. 正文: 1:Reset 与 Revert 的 理解和使用
引言: 9月9号,苹果产品发布会结束后,同时对开发者发布Xcode 6 GM版本,GM版本虽是测试版的含义,但对于苹果来说,等于95%的正式版。 所以,通过GM版本可以开始进行iOS8的兼容和iPhone 6 和iPhone 6 Plus的适配。本篇想来细数细数在新版本中都有哪些新增和改变 正文: 1:模拟器 模拟器在Xcode 6 以后有什么变化呢? 首先是应用编译安装路径方面的三个变化 变化一:应用编译安装路径变跟成如下目录: /Users/yushuyi/Library/Developer/CoreSimulator 变化二:应用安装包和沙盒文件夹的目录分离,在这里推荐一个小工具,它的名字叫:SimPholders 它可以很迅速帮我们定位某个模拟器应用的沙盒文件夹,并记录我们在开发过程中最近编译的三个App。 变化三:现在,我们的Xcode 6 在每一次编译成功并运行以后,安装包的UUID文件夹会随同变化了。 这很好的模拟了真机的实际覆盖安装环境。(特别注意) 路径情况大概发现上面三个比较重要的改变,接下来Xcode 6 在模拟器方面还提供专门的管理页面,如下图: 通过Window -> Devices 打开 对于 Apple Watch 模拟器 需要单独说明一下,它不通过Devices进行管理,因为 Apple Watch 现在属于iPhone的扩展硬件.需要和iPhone配套才能使用.所以我们在启动相应的模拟器以后,才能够打开Watch 模拟器 . 2:全新编程语音:Swift Objective-C发展至今已经有30年的历史,每隔一段时间,苹果都会对其进行细节上的补充和完善.但毕竟一代新人换旧人,有时候只有跳出这个圈,才能够放眼和大胆的去改变甚至超越,而Swift必然会是苹果编程世界的新宠儿.让我们一起以学习的姿态去拥抱.去接纳! 3:Asset Catalog Asset Catalog在Xcode 6中逐渐增强它的功能 1:支持矢量图: Asset Catalog 从 Xcode 6 开始支持矢量图,达到一图顶多图的目的,而不需要单独准备 @1X @2X @3X 的图片. 2:支持Size Classes: 面对多变的布局,如果需要调整相应的图片可直接通过Asset Catalog进行设置 3:支持JPG: 苹果在图片格式方面一直要求使用PNG,但如果一定需要使用JPG(JPG体积小)并放在Asset Catalog里面进行管理也是可以的.但是在渲染方面需要设置为按原始方式渲染,如图: 4:Interface Builder 从Xcode 6开始,通过IB来开发界面是一件非常方面的事情,让我们来看看都添加了哪些新功能: 1:支持多屏幕实时预览且支持横屏预览 2:Size Classes 支持. 在以往的情况,应用同时支持iPhone,iPad,需要创建两个故事板来实现. Size Classes可以实现iPhone,iPad比较相近的界面通过一个故事板来完成. 如果很复杂的也不是不能,只是现阶段不推荐这样.
引言: 本届(2014)的WWDC着实让开发者们眼前一亮,在介绍完Yosemite和iOS8以后,苹果尽然强力推出全新的编程语言Swift. 这下iOS开发界的童鞋可有的忙活了.不过新语言一出,自然需要讨论一番.有人说好,有人说不好, 众说云云. 不过苹果对于此事已经公开发表看法了,原文如下: objc remains a first-class citizen too 更多细节请参考以下正文和相关链接. 1:评价Swift 1:喵神对Swift的看法: http://onevcat.com/2014/06/my-opinion-about-swift/ 2:知乎上面的精彩讨论: http://www.zhihu.com/question/24002984 3:对苹果“五仁”编程语言Swift的简单分析 http://blog.csdn.net/proteas/article/details/28439601#1536434-tsina-1-90651-66a1f5d8f89e9ad52626f6f40fdeadaa 2:讨论 Swift 1:首个 Swift 中文论坛 http://swift.sh/ 2:Swift FAQ http://swiftchina.com/forum.php?mod=viewthread&tid=48&extra=page%3D1 3:学习 Swift 1:来自苹果的编程语言——Swift简介 http://zh.lucida.me/blog/an-introduction-to-swift/ 2:中文版 Apple 官方 Swift 教程《The Swift Programming Language》 https://github.com/numbbbbb/the-swift-programming-language-in-chinese/ 3:《The Swift Programming Language》 https://itunes.apple.com/cn/book/swift-programming-language/id881256329?mt=11 4:Apple Swift语言基础教程视频 http://www.jikexueyuan.com/course/92.html 5:Swift速查表 http://cdn2.raywenderlich.com/wp-content/uploads/2014/06/RW-Swift-Cheatsheet-0_3.pdf 6:Objective-C开发者眼中的Swift: 那些激动人心的新功能 http://blog.segmentfault.com/jwfing/1190000000573441 7:苹果官方Swift修订历史: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html#//apple_ref/doc/uid/TP40014097-CH40-XID_1504 4:Coding Swift 1:Github search for Swift https://github.com/search?l=Swift&p=1&q=swift&ref=cmdform&type=Repositories 2:FlappySwift https://github.com/fullstackio/FlappySwift 3:Easy-Cal-Swift https://github.com/onevcat/Easy-Cal-Swift 4:2048 https://github.com/austinzheng/swift-2048 5:进阶 Swift 1:Swift之 ? 和 ! http://joeyio.com/ios/2014/06/04/swift---/ 2:来自 txx 的 WWDC 2014 Session学习笔记 http://blog.txx.im/blog/2014/06/07/wwdc14-session-402/ 3:Swift Beta3 Changes ( Swift 在 Beta3 中的变化) http://andelf.github.io/blog/2014/07/08/swift-beta3-changes/ 4:Swift-ARC http://qiyutan.com/blog/2014/07/05/swift-arc/ 6:Swift 专题 1:码农周刊《Swift 特刊》 http://weekly.manong.io/issues/33?ref=swift 2:苹果发布Swift编程语言 - iOS移动开发周报 http://www.infoq.com/cn/news/2014/06/ios-mobile-weekly
引言: Chisel是一个加强LLDB调试能力的小插件.主要特点在于辅助界面开发调试时在控制台以尽可能直观的方式查看界面的元素和情况.为我们梳理视图,控制器以及类关系层级. 以及一些临时的界面调试变动进行快捷响应.它的作者来自于Facebook团队.得益于开源,让我们来观摩和了解一下这个东东到底有什么用处. 在开始之前: 在使用Chisel之前应该对LLDB的常用命令应该有一些了解,如果你还停留在只用控制台看输出日志的阶段,建议看看<LLDB调试命令初探>这篇文章 安装: Chisel的开源地址如下: https://github.com/facebook/chisel 安装方式主要是两个步骤: 通过Brew安装Chisel,启动终端,输入如下命令即可: brew install chisel 顺利的话终端会返回如下图所示的内容: 从反馈的结果中可以看到,我们已经成功的安装好了Chisel. 但是此刻还是无法使用的,我们需要将Chisel和Xcode的LLDB关联起来.注意图中Caveats一栏下的说明: 英文的含义是要求我们需要给 .lldbinit 这个文件 注入一段脚本. 这段脚本的内容是: command script import /usr...... 目的是为了在Xcode运行的时候,能加载我们预设的脚本. 那么,我们只需要在终端执行如下命令就好了: echo command script import /usr/local/Cellar/chisel/1.0.0/libexec/fblldb.py >> ~/.lldbinit 到此,安装完成,重启Xcode. 不过在安装过程中博主我也遇到了不少问题: 1:请以管理员的身份运行安装命令 sudo brew install chisel2:你可能还没有安装 Xcode 的 Command Line Tools,在终端运行以下命令进行安装: xcode-select --install3:在用Brew进行安装之前,你需要更新一下你的Brew以确保存在Chisel, 否则也是无法安装的. 注意也要以管理员的身份去更新(类似于CocoaPods) sudo brew update 4:没有安装Brew? http://blog.csdn.net/chenyi8888/article/details/7345113 开始使用: 明天再写好了. 回家玩去..
引言: Auto Layout是iOS6发布后引入的一个全新的布局特性,其目的是弥补以往autoresizing在布局方面的不足之处,以及未来面对更多尺寸适配时界面布局可以更好的适应.要完全掌握Auto Layout是一件非常消耗精力的事情,需要大量的实践,并且在根本上面,理解其如何使用,如果要全面的介绍Auto Layout和使用场景估计几篇博文都介绍不完,本文希望能将使用Auto Layout的重点和技巧以及注意事项,进行一个介绍.成为学习Auto Layout的一个导航文章. 参考资料: 1:iOS7.0 Xcode5 Auto Layout 备忘录 http://www.cnblogs.com/thefeelingofsimple/p/3316300.html 2:iOS 6 Auto Layout NSLayoutConstraint 界面布局 http://www.devdiv.com/iOS_6_Auto_Layout_NSLayoutConstraint_%E7%95%8C%E9%9D%A2%E5%B8%83%E5%B1%80-weblog-227936-13173.html 3:iOS 6 新特性 Auto Layout http://www.cocoachina.com/bbs/read.php?tid=116558 4:WWDC 2012 Session笔记——202, 228, 232 AutoLayout(自动布局)入门 http://onevcat.com/2012/09/autoayout/ 5:iOS 6 自动布局 入门-1 http://www.raywenderlich.com/zh-hans/22873/ios-6-%E8%87%AA%E5%8A%A8%E5%B8%83%E5%B1%80-%E5%85%A5%E9%97%A8%EF%BC%8D1 6:先进的自动布局工具箱 http://answerhuang.duapp.com/index.php/2013/10/11/%E5%85%88%E8%BF%9B%E7%9A%84%E8%87%AA%E5%8A%A8%E5%B8%83%E5%B1%80%E5%B7%A5%E5%85%B7%E7%AE%B1/ 7:AutoLayout 相关概念介绍和动画demo http://studentdeng.github.io/blog/2014/06/13/auto-layout/ 8:iOS中AutoLayer自动布局流程及相关方法 http://my.oschina.net/w11h22j33/blog/208574 使用: 1:理解概念 Auto Layout中文翻译过来意思是自动布局,通过内定的Constraint(约束)和各项条件来计算出合理的布局.而这个合理的布局,符合我们的的预期和意图. 将我们想象中的结果展现出来.Constraint的设定非常灵活,实现一种布局的方法可以通过多Constraint套来完成. 以下几点是我们在开始使用之前必须弄清楚的事情: 1:我们要抛弃以往旧的布局方式不再去关注View的Frame,Center,和autoresizing. 因为这些坐标和大小的定位都可以通过来Auto Layout完成. 2:理解每一种Constraint的含义,否则,当你去看别人的实现的Constraint时,就会有种看天书的感觉. 3:按意图设计,一切按我们理想中的效果去布局,只要约束设定的合理,就一定能够完成目标布局. 2:开始使用 先从Interface Builder开始吧. 打开某个Xib或者StoryBoard,在右侧Show in file inspector里面找到Ues Autolayout,将其勾选.如下图: 自此,Autolayout便启用成功,autoresizingMask被废弃.其所有以往的功能和特性都被Autolayout取代.现在我们定位控件位置的方式,不再像以前一样,计算好每一个控件具体的位置,x是多少,y是多少.而是思考,这个控件离左边是相隔多少距离,或者离顶部或底部相隔多少距离.而有些规则性的事情还是类似的,比如我们定位一个控制的位置,一定要有x,y两个坐标点同时有值,少一个都不能正常显示.同样Autolayout在创建约束时也一样,在思考完离顶部距离以后,还需要思考离顶部距离,否则控件的显示位置一样无法正常显示.换言之,要让Autolayout计算出合理的位置,需要保证水平距离和垂直距离同时存在. 否则IDE,都会给出警告,提示这样的布局Ambiguous Layout(模凌两可)接下来,让我们来熟悉一下Interface Builder提供哪些实现Autolayout的功能:观察一下界面预览右下角,有一排如下图这样的按钮:这些是Interface Builder用来创建Constraint的主要方式,同时,我们也可以在Xcode的菜单栏中找到这些功能,如下图:这些功能分别如下图中描述的那样: 如果是从代码层面开始使用Autolayout,需要对使用的View的translatesAutoresizingMaskIntoConstraints的属性设置为NO.即可开始通过代码添加Constraint,否则View还是会按照以往的autoresizingMask进行计算.而在Interface Builder中勾选了Ues Autolayout,IB生成的控件的translatesAutoresizingMaskIntoConstraints属性都会被默认设置NO. 3:从旧的IB布局中转换成Auto layout 4:熟练使用Interface Builder 5:通过代码来构建自动布局 代码创建的约束有两种方式: 1:常规约束,写法非常冗长,但能实现所有的约束方式以及非常特殊的约束方式,代码如下: //实例化Button button1 = [[UIButton alloc] initWithFrame:(CGRectZero)];//这里不再需要去刻意指定x.y等坐标. [button1 setTitle:@"yushuyi" forState:UIControlStateNormal]; [button1 setBackgroundColor:[UIColor redColor]]; [button1 sizeToFit]; [button1 setTranslatesAutoresizingMaskIntoConstraints:NO];//将使用AutoLayout的方式来布局 [self.view addSubview:button1]; //创建了一个水平居中父视图的约束 NSLayoutConstraint *constraint = [ NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:00.0f ]; [self.view addConstraint:constraint];//将约束添加到对应的父视图中 //继续创建了一个位于父视图底部相隔20距离的约束 constraint = [ NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-20.0f ]; [self.view addConstraint:constraint]; 值得注意的是,添加约束之前一定要将子视图优先addSubview到父视图中,否则在添加约束时会产生编译器警告.而我们在理解的时候,可以通过这种方式来理解. item1.attribute = multiplier ⨉ item2.attribute + constant 2:可视化格式语言约束 所谓可视化格式语言约束,是一种很直观的理解方式,当然,前提是你已经熟练理解这套语言的规则. 通过可视化语言可以一次性创建多个约束. 这对于第一次方式来说,是相当方面和容易理解的.但可视化语言不是所有约束都能满足. 我们可以用正则表达式的学习方式来学习这项可视化格式语言.举例代码如下: //创建需要参与约束规则的对象字典 <span style="font-family:Arial,Helvetica,sans-serif">表示这三个Button将参与Autolayout的约束处理</span> NSDictionary *viewsDic = NSDictionaryOfVariableBindings(deleteButton,cancelButton,nextButton); NSArray *constraints = nil; constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"H:|-25-[deleteButton(==cancelButton@700)]-(>=8)-[cancelButton(140)]-[nextButton(nextButtonWidth)]-rectY-|"//水平 可视化格式语言 options:NSLayoutFormatAlignAllTop //对齐功能 metrics:@{@"rectY":@5,@"nextButtonWidth":@30}//指标参数 views:viewsDic];//参与约束的对象字典 [self.view addConstraints:constraints]; constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"V:[nextButton]-|" //垂直 可视化格式语言 options:0 //无条件 metrics:nil//不带指标参数 views:viewsDic];//参与约束的对象字典 [self.view addConstraints:constraints]; // [deleteButton setContentHuggingPriority:249 forAxis:UILayoutConstraintAxisHorizontal]; 这简单的十行代码,如果你没有学习过Autolayout也会看出一些猫腻,似乎看懂了.但又似懂非懂.接下来就详细解释一下在解释之前,先看看上面这些代码执行后的效果,竖屏如下图: 横屏: 三个按钮位于视图的底部,有大有小,中间有间隔. 3:通过第三方Auto Layout的增强类别包,来实现约束的创建 https://github.com/smileyborg/UIView-AutoLayout UIView-AutoLayout的出现如作者所说,其实现思路来源于Interface Builder. 所以在其API命名方面可以找到很多Interface Builder的影子, 博主极力推荐这个类库,通过它来创建约束是一件非常愉快的事情,思路清晰,当有个前提是,你已经理解了Auto Layout各项规则. constraintsAffectingLayoutForAxis //约束检查 为什么这个View 这样显示 systemLayoutSizeFittingSize http://stackoverflow.com/questions/19352882/tableviewheightforrowatindexpath-incorrect-reporting-content-view-size-with-au 6:调试: 看懂IB给出的警告: 通过代码来检测 模凌两可的布局: 7:Autolayout 特例场景: 8:Autolayout 布局流程: 总结:
引言: Auto Layout是iOS6发布后引入的一个全新的布局特性,其目的是弥补以往autoresizing在布局方面的不足之处,以及未来应对更多尺寸适配界面时布局可以更好的展示.要完全掌握Auto Layout(以下简称AL)不是一件容易的事情,实践是学习和掌握的根本,并且在根本上面,理解其如何使用,本篇做为AL的入门介绍和功能使用 参考资料: 1:iOS7.0 Xcode5 Auto Layout 备忘录 http://www.cnblogs.com/thefeelingofsimple/p/3316300.html 2:iOS 6 Auto Layout NSLayoutConstraint 界面布局 http://www.devdiv.com/iOS_6_Auto_Layout_NSLayoutConstraint_%E7%95%8C%E9%9D%A2%E5%B8%83%E5%B1%80-weblog-227936-13173.html 3:iOS 6 新特性 Auto Layout http://www.cocoachina.com/bbs/read.php?tid=116558 4:WWDC 2012 Session笔记——202, 228, 232 AutoLayout(自动布局)入门 http://onevcat.com/2012/09/autoayout/ 5:iOS 6 自动布局 入门-1 http://www.raywenderlich.com/zh-hans/22873/ios-6-%E8%87%AA%E5%8A%A8%E5%B8%83%E5%B1%80-%E5%85%A5%E9%97%A8%EF%BC%8D1 6:先进的自动布局工具箱 http://answerhuang.duapp.com/index.php/2013/10/11/%E5%85%88%E8%BF%9B%E7%9A%84%E8%87%AA%E5%8A%A8%E5%B8%83%E5%B1%80%E5%B7%A5%E5%85%B7%E7%AE%B1/ 7:AutoLayout 相关概念介绍和动画demo http://studentdeng.github.io/blog/2014/06/13/auto-layout/ 8:iOS中AutoLayer自动布局流程及相关方法 http://my.oschina.net/w11h22j33/blog/208574
引言: 通过Interface Builder(简称IB)来制作界面一直是iOS开发界饱受争议的方式.主要争议的话题是不太适合团队协作开发,再就是对IB的使用比较生疏,觉得IB只能完成一些很简单的功能.然而Interface Builder的出现并不是偶然,更是苹果一直推崇使用GUI实现技术.只是面对不同的问题,我们需要有相应的解决办法.而不是刻意抵触. 如今,Xcode的第五个版本发布,新版IDE对IB的核心文件Xib进行了一次全面的优化和升级.解决了大家一直争议的团队协作问题. 并且Autolayout(自动布局)的出现,和Xib配合使用简直是天作之合.那么本文的目的就来一点一点揭开IB的神秘面纱,并熟练运用到实际的项目中. 参考资料: 1:Xcode 5中的Interface Builder更有利于团队协作开发 http://beyondvincent.com/blog/2013/09/04/111-xcode-5-finally-makes-interface-builder-a-viable-option-for-teams/ 2:Autosizing缩放规律详解(iPhone5支持以及屏幕旋转支持) http://www.devdiv.com/autosizing_iphone_-blog-1-51978.html 3:提高Interface Builder高效工作的8个技巧 http://beyondvincent.com/blog/2014/03/19/18-tips-for-working-effectively-with-interface-builder/ 4:使用 Swift 和 Xcode 6 制作超棒的 UI 组件 https://github.com/nixzhu/dev-blog/blob/master/2014-06-10-make-awesome-ui-components-ios-8-using-swift-xcode-6.md 5:Size Classes With Xcode 6: One Storyboard For All Sizes http://www.learnswift.io/blog/2014/6/12/size-classes-with-xcode-6-and-swift 使用: 首先来看看通过Xcode我们可以创建哪些Xib文件,如下图: 从图中罗列的可选项中可以判断出,哪些是最常用的,以及苹果推荐我们使用哪种来创建界面. 1:StoryboardStoryboard中文翻译过来的意思是故事版.是苹果在Interface Builder推出的一项新的布局方式,不过Storyboard的本质依旧是Xib,它出现的主要目的是更好的展示Xib与Xib之间的流程和联系.当然,如果要介绍Storyboard估计需要彻底新开一篇博文来专门其如何使用. 2:ViewView便是我们在使用IB实现界面时最常用的方式. 创建后,Xib里面已经默认初始化了一个View 3:Empty,Window,Application 以上三个的使用场景还是比较少的.如果你好奇,不妨也新建一个试试.
引言: 程序调试技巧在开发过程中起着举足轻重的地位,熟练的使用可以加快我们捕捉问题的速度. 毕竟BUG这个词是我们程序员一直要伴随的字眼,最关键的,人不是计算机,总有那么一点点小细节容易在我们慎密的思绪中偷偷溜走,从而导致一个BUG的出现.那么本文就是为了介绍关于在开发iOS程序时有哪些好用的技巧辅助我们迅速的找到错误. 参考资料: 1:Xcode的控制台调试命令 http://blog.csdn.net/likendsl/article/details/7576549 使用: NSLog: 通过NSLog开启了我们程序的调试之旅. 不过NSLog提供的调试信息实在太少.默认只能得到打印时间和工程名称(这个基本没用.) ,就像下面这样: NSLog(@"hello world!"); 输出结果: 2013-05-30 10:01:58.704 DebugDemo[536:c07] hello world! 但在实际项目中,我们需要更多的调试信息,包括这条日志信息来自哪个函数,第几行代码等等来辅助我们梳理程序的流程. 为此,通过一些宏命令辅助,可以达到这方面的效果,代码如下: #ifdef DEBUG # define NSSLog(fmt, ...) {NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);} #else # define NSSLog(...) #endif 上面的代码是一段宏命令,需要在类方法外声明,建议放在全局头文件中以供所有业务类使用. 调用方式如下: NSSLog(@"hello world!"); 输出结果: 2013-05-30 10:24:21.868 DebugDemo[782:c07] -[ViewController strongNSLog:] [Line 44] hello world! 可以看到,打印的信息提供类名称,函数名称,代码在第几行等非常有用的参考信息.当然这些信息的输出是需要消耗系统资源的,也就是说如果频繁的去使用函数打印日志,将很有可能导致UI界面卡住,程序运行不流畅等不利因素.不过呢,通过上面的代码我们已经很好的规避了这个问题,我们只在程序的DEBUG模式下才打印信息,如果不是DEBUG模式就以三个点的方式来表达代码不做任何事情,我们只需要理解这个目的就好了. 到此,我们增强了我们的NSLog,让它可以做更多的事情. Description: description是来自NSObject的一个类成员方法, 通常我们可能需要输出某个类实例的相关信息,就像下面这样(你的自定义类,肯定继承自NSObject吧?): NSLog(@"%@",[[TestClass alloc] init]); 输出结果: 2013-05-30 11:08:56.671 DebugDemo[1042:c07] <TestClass: 0x8a57190> 或者是打印一个继承自UIView的类呢? NSLog(@"%@",[[UICustomView alloc] init]); 输出结果: 2013-05-30 11:13:42.899 DebugDemo[1081:c07] <UIView: 0x71c0fd0; frame = (0 0; 0 0); layer = <CALayer: 0x71c10f0>> 观察输出结果发现这两个类打印出来的信息并不一样, 这是因为UIView本身也是继承自NSObject,但是它自己已经重写了description函数.所以结果不一样. 也就是说,一旦我们重写了description函数,我们再次对类通过 %@的方式输出信息,目标类会直接调用重写后的description函数,并输出结果. 重写代码如下: - (NSString *)description { NSMutableString *mutableString = [[NSMutableString alloc] init]; [mutableString appendFormat:@"\n frame:%@",NSStringFromCGRect(self.frame)]; [mutableString appendFormat:@"\n color:%@",self.backgroundColor]; return mutableString; } 通过重写description函数可以辅助我们在开发调试过程中去获取更多自定义类方面的信息 调试: 为了进入调试状态,我们随时需要在代码中的某一行设定一个断点,当程序执行到这行代码时,大部分运行时的进程都被强制的阻塞(计时器,网络类方面无法阻塞). 将接下来的执行权利交由给开发者.就像下面这样: 为了加快我们的操作速度,我们应该牢记调试相关的快捷键: 清空控制台: command+K 显示隐藏控制台:shift+command+Y Continue/Pause program exeecution: control+command+Y Step over:F6 Step into:F7 Step out:F8 当进入调试状态时,我们有几个常用的调试技巧 1:我们随时可以打印当前类的成员变量和局部变量,就像下面这样操作: 点击后,相当于执行 NSLog(@"%@",object); 会将结果立刻显示到控制台. 2:打印某个View,或者是当前方法体内的局部View的层级,直接在控制台输入如下代码: po [[[[UIApplication sharedApplication] delegate] window] recursiveDescription] 如下图所示: Crash: 我们在开发过程中,总是不可避免的产生你无法预期的Crash.其实拥有了ARC以后,Crash的机会相对少了很多,只不过偶尔还是要来那么几次.最怕的,就像下面这样,产生了Crash,却停留在main.m代码里: 这样的Crash提示对于我们来说没有任何帮助,当然有经验的开发者会去查看控制台自动输出的Crash信息,如下: 通过exception和reason来定位产生Crash的主要原由. 可是在这样的情况,我们只能去猜测错误大概在哪个类,尝试着在可能出现Crash的代码上面设置一个断点,一步一步调试最终定位到真正产生Crash的那一行代码. 这样效率明显是非常低的,那有没有办法可以迅速的定位错误的具体位置呢? 有! 在我们的XCode中找到Show the Breakpoint Navigator,按照下图中来设置一个全局异常断点 当我们再次运行程序并尝试模拟刚刚产生的Crash, 结果发现,XCode准确的定位到了产生Crash的具体位置.这实在太棒了! 以上是在开发人员开发过程中遇到Crash的跟进方式. 那么交付给测试人员测试时遇到Crash呢?此时又应该怎么收集呢? 正因为有这样的需求iConsole诞生了, 详细使用请查阅我的另一篇关于iConsole介绍的博客 又那么产品正式发布了,还是遇到Crash了呢? 所以Crashlytics也诞生了,具体的使用可以参考这篇关于介绍如何使用Crashlytics的博客: 小技巧: 我们每一次编码完成后紧接着便是编译运行起来,看看程序运行的结果是否达到了我们的预期,此时,我们离不开控制台给我们输出必要的信息,为此, 当程序跑起来时,我们的控制台遍自己弹出来,这是不是蛮好的? 又当我们结束调试需要继续编码时控制台自动隐藏是不是更好? 那么,就按如下设置吧: 1:当编译运行起来以后自动显示控制台 2:当结束运行状态时自动隐藏控制台: 总结: 迅速的解决问题是一件非常愉快的事情,每当修复一个BUG时就意味着我们的程序更加健壮了.
转载请注明出处,保留原帖地址及作者署名. Url:http://blog.csdn.net/ysy441088327/article/details/8852304 Author:余书懿 引言: 精华实例是博主我新开设的一个专题,这个专题主要介绍一些在iOS应用中非常值得实践的例子. 欢迎大家转载和传播,但请保留原著出处.因为我随时可能会更新以及纠错. 我希望每一个阅读精华实例的开发者能够从中受益,并且熟练的运用这些技巧. 那么作为你们呢,在阅读完这篇文章以后,请尽量留下你的建议和看法. 这是我持续改进文章的一个主要方式. 精华实例的章节到底会有多少篇,这个我也很难预料,不过只要是我认为值得写的,我都会写出来. 让大家共同学习和成长! 开篇的废话比较多,下面开始进入正文: 什么是App启动插画的自定义过度? 我们都知道,App在启动的时候,会展示一张App启动插画来保证App程序加载的等待体验,让用户欣赏美丽的插画时,App真正默默的努力加载着相关资源, App自动调用main函数,接着是:didFinishLaunchingWithOptions 当UIWindow执行makeKeyAndVisible时,那么这张启动插图就会自动消失了.但却是瞬间消失的. 而所谓的自定义过度就是让启动插图更加友好的消失(而不是瞬间没了),相信在很多主流App产品中已经看到不少这样的效果. 那么这个实现方式就是本文要讨论的重点! 程序原理是? 程序有时也会有点魔术的感觉,在你不知道原理的时候总感觉它很神秘! App启动插画我们通过工程设置-targets-Summary-Launch Images 来设定. 但是这个启动插画的指针我们是获取不到的.(要是获取到了,这篇文章估计很短,甚至没有.) 那怎么办呢? 我们肯定要有这个指针,才能去实现自定义的过度,例如一点一点的消失啊,翻一页书那样的效果等等. 出于我们的目的,我们只有自己alloc一个UIImageView了,其加载的图片就是我们启动插画的图片. 但是要保证一点:启动插画展示的是什么样子,我们自己alloc 的UIImageView也应该是什么样子, 不然,这个体验就大打折扣了 你说呢? 而这个UIImageView我们到底应该addSubview在哪里是本实例的一个技术难点. 那答案是什么? 是UIWindow!却不是keyWindow.而是一个需要我们自己alloc的UIWindow! 但是它的windowLevel要比keyWindow高一级! 也就是说,为了让UIImageView顺利的展示出来并保证与启动插画的样子一摸一样,我们需要有一个UIWindow的容器.代码如下: UIWindow是一个很神奇的类,当你alloc出来并把windowLevel设置比keyWindow的level高以后,此时不再需要额外的执行makeKeyAndVisible或者addSubview来让这个UIWindow显示出来. 但是这个UIWindow的hidden属性却默认是YES, 所以我们需要将hidden设置为NO! 让UIWindow真正显示出来. 那么接下来把我们的UIImageView调用addSubview到这个UIWindow上面. 而alloc UIImageView时 有一点我们需要注意一下: 不要通过imageNamed的方式去获得UIImage, 大家都应该知道imageNamed是会自动把加载过的图片资源缓存起来,如果下次再调用同名称的图片,是直接从内存中获得,而我们的这个UIImageView只使用一次,当插画隐藏以后不再需要重复使用. 那正确的获取方式是什么? 以绝对路径的方式获得这张图片资源,就像下面这样: imageWithContentsOfFile是符合我们现在使用图片资源的需求,因为它在使用完成以后会自动清理掉(ARC) 到此,我们的伪装版启动插图已经部署完成. 何时调用呢? 在第一个启动的ViewController中的viewWillAppear执行这些代码.那么不出意外的话,启动插画永远的显示了出来. 但是我们不是要一直显示啊,差不多它就应该消失了,只是说消失的时候更加友好一些! 因为我们已经有了UIImageView的指针了,接下来我们可以随意控制这个UIImageView消失的方式. 也就达到了本文的目的: App启动插画的自定义过度 而博主我对于这项实例功能封装了一个类来方便调用! 已经上传到Github上. 欢迎大家使用并反馈问题. 它的名字叫:SYAppStart SYAppStart的API很简单, 提供一套默认的显示和隐藏方法 核心在于hideWithCustomBlock来自定义更多隐藏插画的方式. 希望有人能够pull request 很棒的自定义隐藏代码! 小技巧: 1.如何全屏展示启动插画(隐藏状态栏那种)? 勾上即可! 2.如何在展示完成以后又立刻恢复状态栏的显示? 总结: 博主本人已对此功能进行封装,基本上不需要关心核心功能的实现,那为什么还要费一大半劲去阐述原理? 因为该功能在于如何利用UIWindow去实现,或者说我们想把顶部状态栏更好的掌控在手中,那么理解UIWindow是首当其冲! 阅读完本文以后有任何疑问,都务必说出来,不想注册csdn账号的,可以通过微博私信我,这是我持续改进文章内容的主要方式. 谢谢!
引言: PonyDebugger是一个很给力的iOS调试工具,它的监视器安装在Chrome浏览器下做为插件使用,通过监视器和PonyDebugger的iOS SDK相辅相成,可以很好的监视App的运作情况.它的突出的亮点功能如下: 1:实时的检测应用与网络的交互情况 2:查看应用内Core Data的数据变化 3:实时反馈UI层的层级情况 参考资料: 1:简单配置PonyDebugger http://iiiyu.com/2013/01/14/simple-configuration-ponydebugger/ 安装: Github托管地址:PonyDebugger PonyDebugger分为iOS端和服务端两个部分 iOS端安装的首先方式是CocoaPods. 服务端PonyDebugger Chrome插件的安装方式如下: 1: Xcode 的Command Line Tools 必须安装 2: 在Shell里面执行下面命令 curl -sk https://cloud.github.com/downloads/square/PonyDebugger/bootstrap-ponyd.py | \ python - --ponyd-symlink=/usr/local/bin/ponyd ~/Library/PonyDebugger 3: 安装成功以后,继续在Shell里面执行(注意:以后每次使用PonyDebugger服务端之前都需要在终端执行这行代码) ponyd serve --listen-interface=127.0.0.1 4: 打开你的浏览器输入地址 http://localhost:9000 没什么意外,显示出来的页面如下,即表示你已经成功安装服务端了 使用: iOS端的PonyDebugger 是单例模式存在,所以初始化方法如下: PDDebugger *debugger = [PDDebugger defaultInstance]; 之后再使用如下方法建立与服务端的连接: [debugger connectToURL:[NSURL URLWithString:@"ws://localhost:9000/device"]]; 以下开始具体功能 1:开启应用视图层级解析 [debugger enableViewHierarchyDebugging]; 开启后,可以在服务端中看到以xml文件形式生成的View层级情况,如图: 其中所看到的frame class等字段可以通过以下方法实现自定义是否需要在层级中显示,是以KVO形式获取的. [debugger setDisplayedViewAttributeKeyPaths:@[@"frame",@"hidden",@"class",@"tag",@"alpha",@"userInteractionEnabled"]]; 2:开启网络请求监听 [debugger enableNetworkTrafficDebugging]; 开启后,可以设置为是否监听所有的网络请求: [debugger forwardAllNetworkTraffic];还是只监听某一个类网络请求: [debugger forwardNetworkTrafficFromDelegateClass:[User class]]; 3:开启Core Data 数据浏览 [debugger enableCoreDataDebugging]; 开启后,继续添加需要检测的CoreData上下文 [debugger addManagedObjectContext:[StackMobManager sharedInstance].keyManagedObjectContext]; 以上三步的所有功能结果都将站在服务端 总结: PonyDebugger为Debug带来了一些辅助,对于博主来说最为实用的是网络检测请求. 至于其他功能可能是习惯原因,还无法很顺手的用起来. 还有待继续探究.
引言: 看腻了清一色默认的App字体,换一个个性化体验也是不错的选择. 参考: http://www.16kan.com/post/218270.html http://blog.csdn.net/frankwun/article/details/7688950 字体在哪? Mac 有一款自带App叫做字体册. 打开这个应用以后,可以看到目前内置在系统中的字体,iOS支持显示的格式有两种,分别是otf 和ttf. 首选是otf. 如何配置? 第一步: 将要使用的字体拖动到项目中. 第二步: 在Info.plist中添加Fonts provided by application项,加入一个item值为刚刚添加的字体文件文件名. 如何使用? 使用时要注意,字体文件名称,并非真正使用时的名称. 真正的名称在字体册的查看某一字体的详细参数时可以看到.如下图: 或者根据字体文件在应用中的路径通过代码来获得(不是特别准,可能偶尔需要手动调整): NSString *fontPath = [[NSBundle mainBundle] pathForResource:fontFileNameArray[i] ofType:nil]; NSURL *url = [NSURL fileURLWithPath:fontPath]; CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)url); if (fontDataProvider == NULL) { break; } CGFontRef newFont = CGFontCreateWithDataProvider(fontDataProvider); CGDataProviderRelease(fontDataProvider); if (newFont == NULL) { break; } CFStringRef fontName = CGFontCopyFullName(newFont); NSString *fontNameString = (__bridge id)(fontName); if ([fontNameString isEqualToString:@"Yuppy SC Regular"]) { fontNameString = @"YuppySC-Regular"; } [fontNameMDic setObject:fontNameString forKey:fontFileNameArray[i]]; CFRelease(fontName); CGFontRelease(newFont); 以下代码可以遍历出目前在iOS系统中可以使用的字体名称 NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyNames]]; NSArray *fontNames; NSInteger indFamily, indFont; for(indFamily=0;indFamily<[familyNames count];++indFamily) { NSLog(@"Family name: %@", [familyNames objectAtIndex:indFamily]); fontNames =[[NSArray alloc]initWithArray:[UIFont fontNamesForFamilyName:[familyNames objectAtIndex:indFamily]]]; for(indFont=0; indFont<[fontNames count]; ++indFont) { NSLog(@" Font name: %@",[fontNames objectAtIndex:indFont]); } } 总结: 唯一不好处是字体的数据包很占用App的体积. 这个问题,有待解决.
引言: Parse一款基于Baas的后端云存储服务平台,为开发者提供云端数据存储和读取的能力,Parse团队在其SDK方面花费了大功夫,实现了很多非常有用的功能.并且,非常的稳定,兼容和灵活性,相对于其他Baas平台,Parse在上手方面是最为容易的. Parse的特性如下: 1:自定义数据字典 2:消息推送 3:地理位置 4:数据缓存 5:离线数据同步 6:云端自定义代码 7:二进制文件读取 参考资料: 1:Parse的主页 https://www.parse.com 2: 安装: 1.首选的安装方式是跟着Parse自家提供的官方起步教程即可完成,非常简单! 2.也可以用CocoPods,只是SDK太肥了,而且装的话,还会有一个Facebook SDK的依赖. 使用: Parse支持的数据类型主要如下: Number: 对应各种类型,包括:整形,浮点型 转换成NSNumber后存储到Parse Cloud Date: 对应NSDate Array:对应NSArray. 万能的数组(Parse的强大之处) String:对应NSString Null: 很少用到 Pointer: 指针:相当于表与表之间的外键关系, 只存储着某个对象的ID Relation:关系:应付多对多关系时的方案,但是可以用Array达到同意的功能. 查询: Parse设定了尽可能多样的查询方式,并且它还支持NSPredicate. 这对于使用过CoreData的人来说,是比较容易上手的. 首先来说说Parse提供哪些查询接口 时间: 查询创建时间大于某个时间的数据行: NSDate *lastQueryDate = [self lastQueryTime]; if (lastQueryDate == nil) { lastQueryDate = [NSDate date]; } [query whereKey:@"createdAt" greaterThan:lastQueryDate]; 查询缓存策略: kPFCachePolicyIgnoreCache 查询不从缓存加载结果或将结果保存到缓存。默认缓存策略为 kPFCachePolicyIgnoreCache。kPFCachePolicyCacheOnly 查询会忽略网络,仅从缓存加载结果。如果没有缓存的结果,则会引发 PFError。kPFCachePolicyNetworkOnly 查询不从缓存加载结果,但会将结果保存到缓存。kPFCachePolicyCacheElseNetwork 查询首先尝试从缓存加载结果,但如果加载失败则从网络加载结果。如果缓存和网络加载都不成功,则会出现 PFError。kPFCachePolicyNetworkElseCache 查询首先尝试从网络加载结果,但如果加载失败则从缓存加载结果。如果网络和缓存加载都不成功,则会出现 PFError。kPFCachePolicyCacheThenNetwork 查询首先从缓存加载结果,然后再从网络加载。在此情况下,实际上会调用两次回调 - 第一次针对缓存的结果,随后针对网络结果。由于会在不同的时间返回两个结果,因此该缓存策略不能与 findObjects 同时使用。 Push: 官方提供了一个详细的消息推送配置流程: https://parse.com/tutorials/ios-push-notifications Parse远程推送教程实在是太详细了,图文并茂. 在这里我简单复述了一下整个过程方便理解: 1.申请苹果的开发者招生计划,并成功激活. 2.通过本地的Mac系统生成一个证书签名请求(Certificate Signing Request), 3:接下去苹果开发者主页的Member Center(会员中心)创建一个新的App IDs. 并通过证书签名请求激活这个App IDs的远程推送服务功能. 4.下载这个App IDs的推送许可服务,并通过钥匙访问串导出密匙(p12). 5.最后生成这个App IDs的描述文件,并应用到工程中. 6.编写Parse所提供的Push的相关API代码. 实现远程消息推送. 其中有一个步骤是当你通过Certificate Signing Request(证书签名请求)激活了苹果的远程推送通知服务以后. 并下载了推送许可证书:aps_development.cer. 教程中要求从钥匙访问串(Keychain Access)中导出.p12后缀的密匙. 在导出时有两点需要注意: 1:请在名称为 Apple Development IOS Push Services : ***** 或 Apple Prodoction IOS Push Services : ***** 的栏位上直接右键,如下图: 图中方框中的三角箭头,请直接无视和忽略他。 里面是证书的创建者信息而已。 虽然也可以导出p12, 但是这个p12是无效(请特别注意) 2:在导出时要求你输入密码,请直接忽视,也就是空密码即可,否则Parse 的 App Settings - Push Notifications里要求上传的p12密匙无法成功. 最后还有一个细节要注意,也就是当你的产品成功发布到应用商店以后,你之前用来测试的开发版本证书,应该替换为发布版本证书(aps_production.cer). 否则从App Store上面下载的应用将无法收到消息推送.. 所以,你需要重新上传发布版本的密匙.跟开发版本的上传方式一样. 成功后如下图所示: 总结: 可能遇到的问题: 1:编译问题. 解决方案: http://stackoverflow.com/questions/15457136/parse-for-ios-errors-when-trying-to-run-the-app
引言: StackMob 是一个轻量级的 Baas 移动后端云存储平台. 为移动App提供了强大的后台云存储能力,其SDK架接在Core Data身上,在不改变Core Data API 使用方式的前提下为Core Data 提供云端存储的能力,此篇专门介绍了 StackMob 在 iOS环境的使用方式. 参考资料: 1:StackMob 的主页 https://www.stackmob.com/ 2:StackMob iOS开发文档 http://stackmob.github.io/stackmob-ios-sdk/ 3:如何为iOS应用及游戏添加后台网络服务系列1 前言 http://blog.sina.com.cn/s/blog_4b55f6860101b991.html 4:StackMob SDK 官方使用教程 https://developer.stackmob.com/tutorials/ios 5:16小时的诱惑大致的介绍了StackMob从注册到使用的整个过程: http://www.csdn.net/article/2013-03-19/2814542-build-an-airbnb-clone/1 安装: StackMob是开源的,托管地址如下: https://github.com/stackmob/stackmob-ios-sdk 安装的首先方式是CocoaPods. 可能遇到的问题: 1:解决AFNetworkKiting 安装后警告部署问题,详见这篇文章 2:解决 Reachability 类 的冲突问题,代码如下: 3:让 StackMob 连接至 Amazon S3 云存储服务 StackMob需要连接 Amazon S3 以后才拥有二进制文件的存储能力. 本篇博文大致的介绍了一下S3的注册流程,可以稍作参考: http://www.xieyidian.com/2692 第一步:前往亚马逊Web Services 注册一个开发者账号: https://aws.amazon.com/cn/ 第二步:注册并激活 S3 https://aws.amazon.com/cn/s3/ 注册并激活S3需要一张支持外币付费的信用卡,建议使用万事达卡. 在索要卡片的界面填写如下: 类型选择:MasterCard 信用卡卡号 持卡人 拼音名称. (按照信用卡上面填写) 成功以后会扣费一美元. 并收到开通S3服务的邮件提醒. 此时便已经开通了S3的服务,拥有了使用的权利,如果此时进入S3的控制台依旧提示你需要注册的话,是浏览器缓存的问题,清空即可. 第三步:进入 S3的管理控制台 创建一个 Bucket 如下图: https://console.aws.amazon.com/s3 Region 选择服务器所在的国家,里咱越紧越好. 日本吧 第四步:摘用StackMob官方的连接教程,基本上就搞定了: http://developer.stackmob.com/tutorials/dashboard/Adding-a-Binary-Field-to-Schemas 在配置最后一步的链接时有一个字段要特别注意,如下图所示: 那就是S3 Path Alias 这个字段的值在设置不对的情况下去使用它生成的访问链接,访问的结果始终是下图中的错误: 如何才能正确设置S3 Path Alias? 首先要了解S3 Path Alias到底是做什么用的,它一个链接地址别名 当我们用StackMob上传一个二进制文件成功以后,StackMob会自动根据S3 Path Alias所给定的值自动生成一个完整的二进制文件下载链接. 如何检验这个下载链接的正确性,我们可以前往S3的控制台,随便找一个已经上传成功的文件,查看其详细信息,如下图所示: 拿图中的Link和StackMob所生成的链接进行对比,看看有哪里不一样了. 如果不一样,那么S3 Path Alias的设置肯定有问题. 从上图来说,正确的S3 Path Alias设置方法应该是: http://s3-ap-northeast-1.amazonaws.com/musicpushtest/ 最后S3的定价情况地址如下:(新用户注册免费12个月): http://aws.amazon.com/cn/pricing/s3/ 使用: 不足之处: 1:没有二进制文件上传进度 原因:求解决 2:多次发起查询请求和保存请求,都有可能产生Crash, 原因: 由于线程调用错乱 导致StackMob停止工作甚至Crash,使用前,请熟悉GCD,可以参考这篇教程. 这篇更全面 3:查询和保存请求发起后,无法立刻撤销,必须等待整个请求完成 原因:求解决 4:分页查询起始索引默认必需设置值大于0 才有效果: 原因:我估计代码内部BUG,注释掉 判断大于0 即可修正 5:查询一对多的对象以后在缓存到本地时,写入指定Plist文件时Crash 原因:NSDictionary的key 不是 NSString 所致, 修改了源代码 替换键值对的方法. 6:利用对象层级展开以后,却无法获取子对象数据 原因:求解决 功能要点: 一:关于二进制文件上传 1.设置Core Data 数据模型的字段为字符串类型. 2.手动设置StackMob 的 Schema 里对应的字段为二进制类型. 3.本地数据模型的字段名称和StackMob 的 Schema 的字段名称 关系如下:本地Core Data: userIcon Schema :user_icon 4.如果本地某个字段 对应着服务器的某个二进制字段时,就在上传服务器时必须通过如下代码赋值: + (NSString *)convertUploadWithPath:(NSString *)aPath withContentType:(NSString *)aContentType { NSData *data = [NSData dataWithContentsOfFile:aPath]; if (data) { NSString *dataString = [SMBinaryDataConversion stringForBinaryData:data name:[aPath lastPathComponent] contentType:aContentType]; return dataString; }else { return @"file?"; } } 注:任何其他方式的赋值都不允许,否则会出现让人无法理解的BUG.(特别注意) 上传成功的二进制文件字段需要通过执行以下代码才能获取二进制文件的下载链接 [[StackMobManager contextForCurrentThread] refreshObject:self.dmSongTyoe mergeChanges:YES]; 但是这个只对新增的对象启效果,如果你是更新对象使用上面的代码刷新的话,会变回你之前修改的值.那又怎么解决呢? 二:数据缓存机制 StackMob的缓存机制默认是关闭的,如果要开启,需要对以下全局变量赋值: SM_CACHE_ENABLED = YES; 设置以后,StackMob会自动创建一个本地Core Data数据库自动管理和缓存来自服务器的数据. 针对查询数据机制有一个枚举来决定其取数方式: typedef enum { SMCachePolicyTryNetworkOnly = 0,//只从网络取 SMCachePolicyTryCacheOnly = 1, //只从缓存取 SMCachePolicyTryNetworkElseCache = 2,//先从网络取,取不到用缓存替代 SMCachePolicyTryCacheElseNetwork = 3,//这个还没研究过 } SMCachePolicy; 如果用户更改了设备的网络环境,可以通过以下Block去修改查询数据机制: SMClient *client = [[SMClient alloc] initWithAPIVersion:@"0" publicKey:@"XXXX"]; SMCoreDataStore *coreDataStore = [client coreDataStoreWithManagedObjectModel:myModel]; [client.session.networkMonitor setNetworkStatusChangeBlockWithCachePolicyReturn:^SMCachePolicy(SMNetworkStatus status) { if (status == Reachable) { return SMCachePolicyTryNetworkElseCache; } else { return SMCachePolicyTryCacheOnly; } }]; 那么在App启动的时候,判断一下当前的网络环境,设置好查询数据机制: if ([Reachability isEnable3G] || [Reachability isEnableWIFI]) { [self.keyCoreDataStore setCachePolicy:SMCachePolicyTryNetworkElseCache]; }else { [self.keyCoreDataStore setCachePolicy:SMCachePolicyTryCacheOnly]; } 三:关于保存(saveOnSuccess)对Object的任何增删改都通过saveOnSuccess 来保持云端的数据同步. 用起来蛮简单,不过也有些小细节要注意,否则也非常纠结. 1.saveOnSuccess因为一些网络原因或者保存重复主键等问题导致保存失败,会进入失败的Block. 如果连续进入三,四次左右的失败Block,它~~~~~ 它~丫的~ App就Crash了. 卧槽... 什么@try @catch 全都挡不住这个崩溃. 解决办法是在失败的Block里面执行Core Data上下文删除刚刚需要保存的对象的操作. [StackMobManager contextForCurrentThread] deleteObject:obj];这样不管你点多少次都不会Crash了,我觉得这是StackMob设计上的一些不足,不够我也是菜鸟,不能够做多评论. 2.Updating passwords via this API is disabled for security reasons. Use the resetPassword API instead 这个提示通过控制台打印过来,大意是说你当前的保存数据请求被云端给限制了. 我是怎么触发这个Error的? 当我发起创建一个新User对象的同时,再多保存了一个File对象.File对象依赖于User对象,也就是说File对象的所有权是User对象的,但是你创建User对象的时候,你并没有登录. 处于授权安全机制,本次保存请求就算是失败,(即使数据已经保存到了云端). 解决办法是不要急着一起次保存到云端,只有等登录成功以后,再去发起File对象的保存请求即可. 四:关于查询(executeFetchRequest) 对Object的查询就靠这个函数了,基本雷同CoreData的查询函数,StackMob提供两种发起查询请求的方式. 一种是内部构建多线程运作工作,不会卡住主线程和影响界面,完成后通过主线程告知: 但是,连续多次发起这种请求方式, 有极高的几率导致整个SDK停止工作. 这是个比较严重的问题,应该是StackMob 的BUG, 另外一种则是不构建多线程,如果直接执行,在没返回结果之前会卡住主线程,影响界面体验,但是我们可以自己使用GCD来避免主线程阻塞的这个问题 用这种方式似乎可以避免多次发起请求所产生的BUG,至少暂时没发现. 在发起查询请求之前有一个参数类要特别的注意一下,这个类也是实现个性化查询的一个重要途径之一, 它是SMRequestOptions 它有什么用: 1:是否要求刷新Token? tryRefreshToken 2:以HTPPS加密的方式安全获取数据? isSecure 3:当请求网络超时时,重试几次呢? numberOfRetries 4:返回的数据结构需要展开几个层次?(最多三级) setExpandDepth 5:只是想返回某几个字段的数据而已 restrictReturnedFieldsTo 总结:
引言: CocoaPods是一个可以帮你集中管理第三方库的开源项目,运行在Ruby的环境下,基于GitHub托管优势,你可以很方便的查阅目前依赖于CocoaPods的资源. 并且,CocoaPods是可靠,稳定,安全的.可以为你在使用第三方库资源时节省大部分的配置和部署时间.更专心的专注于Coding! 部署的环境: 硬件:Retina Macbook Pro 软件:OS X Mountain Lion 10.10.1 Ruby版本:2.2.0 参考文章: 1:如何安装Ruby(如果打不开请使用百度快照) http://www.zikercn.com/node/41 2:如何使用CocoaPods http://blog.devtang.com/blog/2012/12/02/use-cocoapod-to-manage-ios-lib-dependency/ 3:Ruby中国针对2.0.0发布帖子: http://ruby-china.org/topics/8896 4:如果安装失败,请参考下面两贴 http://stackoverflow.com/questions/9626729/how-do-you-install-build-ruby-1-9-3-on-osx-lion http://stackoverflow.com/questions/14592945/cannot-compile-ruby-1-9-3/14594287#14594287 5:Use CocoaPods With Swift (在 Swift 中使用 CocoaPods) http://andelf.github.io/blog/2014/06/23/use-cocoapods-with-swift/ 第一步:安装Brew 详细步骤和介绍请参阅我的另一篇博客: 第二步:更新Ruby: 通过Brew将Ruby更新到最新版本,OS X 已经自带了Ruby不过版本偏低. brew install ruby 更新成功以后重启终端,并执行以下命令来检查一下Ruby和gem当前使用的版本: 第三步:通过gem安装CocoaPods 接下来开始安装CocoaPods.在安装之前,还需要将XCode里的Command line工具安装好.如下图所示: 安装成功后以后回到Shell,输入如下命令: 命令:sudo gem install cocoapods 命令:pod setup pod setup命令执行后原理是将Spec项目复制到当前用户的.cocoapods\master目录下,以后的查找、安装使用都是基于该本地目录的. 安装成功后,就可以尝试使用了,以后更新新版本的Spec项目只需要再次执行pod setup即可 使用Search命令来看看你需要的第三方库是否存在! 命令:pod search SDWebImage 到此,CocoaPods的安装就完成了. 第四步,使用CocoaPods 接下来,你需要建立一个主工程.建立成功以后,再次启动终端, 利用cd命令进入到工程文件夹内,此时需要创建一个特殊的文本文件,命令如下: 命令: touch Podfile 创建 命令: open -e Podfile 打开 此时,Mac应该是自动打开记事本. 接下来就开始加入我们要使用的库了. 编制格式可以参考CocoaPods的官方文档 示例: platform :ios,5.0 pod 'SDWebImage' 上面的代码中:指定了平台最低支持的版本pod通过Search查到的库 如果没有指定版本号,那么配置出来的版本是最新版. 对这个文本文件保存以后,执行如下命令: 命令:pod install 在每次执行前请确保终端所处于的文件夹目录已经是当前目录. 否则将安装失败. 另外本次成功安装以后,如果 SDWebImage 的作者更新了它的库并提交到了CocoaPods里面,此时我们也想在项目中使用新版本的SDWebImage. 这个时候需要执行如下命令: 命令:pod update 和pod install命令一样 需要将终端所处于的文件夹目录是当前Podfile文件所在的目录 执行后,CocoaPods首次会创建一个同工程名的工作空间(xcworkspace). 以后就打开这个工作空间来进行接下来的开发工作. 这里也证明了一点,以后新项目启动时,应该先配置和部署CocoaPods. 另外,如果指定了某个开源库的版本号,此时pod update 会忽略版本号, 所以需要使用pod install来安装指定的版本 总结: CocoaPods的部署与配置还是较为繁琐的,博文流程中记录的每一天都要去执行.不然都有可能产生失败. 可能遇到的问题: Date:2013-05-13 23:24:47 今天用CocaPods的 pod install 命令时 出现如下错误: The `master` repo requires CocoaPods 0.18.1 解决办法见此贴. Date:2014-02-12 17:19:48今天用CocoaPods的pod update命令时 出现如下错误: CocoaPods官方团队对于此问题,专门写了一篇文章来解决这个问题. http://blog.cocoapods.org/Repairing-Our-Broken-Specs-Repository/ Date:2014-03-13 12:33:46 今天用CocoaPods的pod update命令时 出现如下错误:The `master` repo requires CocoaPods 0.29.0解决方案: 更新Gem,在终端输入如下命令: sudo gem update cocoapods参考: http://www.cnblogs.com/so-magic/p/3580100.html Date:2014-04-14 21:47:08 今天在编译真机版本调试时遇到了关于64位架构编译不过去的问题. 参考以下资料解决问题: http://stackoverflow.com/questions/18881986/integration-error-with-cocoapods-and-xcode5 http://cameronspickert.com/2014/01/20/remove-the-arm64-architecture-from-cocoapods-targets.html 题外话: 关于Spec 简单的说,Spec就是每个包在CocoaPods中的配置文件,其中包括Package的名字,版本号,每个版本对应的下载地址,编译时的参数等等。 这是该项目的地址:https://github.com/CocoaPods/Specs 在页面上有介绍如何创新新的包,可以Fork该项目,然后通过pull request提交所建的新包。 附上一篇详细的教程 http://ishalou.com/blog/2012/10/16/how-to-create-a-cocoapods-spec-file/ 关于生成代码文档 详细见我的另外一篇教程: http://blog.csdn.net/ysy441088327/article/details/7661785
引言: Core Data 是 iOS 3.0 以后引入的数据持久化解决方案,其原理是对SQLite的封装,是开发者不需要接触SQL语句,就可以对数据库进行的操作. 其编码方式和原理结构方面较为特殊,本博文主要介绍在使用Core Data时遇到的各种问题以及对其核心原理进行解释. 参考资料: 1: iOS教程:Core Data数据持久性存储基础教程 http://www.dasheyin.com/ios_jiao_cheng_core_data_shu_ju_chi_jiu_xing_cun_chu_ji_chu_jiao_cheng.html 安装: 安装的方式只有一步,引入CoreData.framework 即可. 使用: 使用Core Data起步最先要了解和熟悉的类是以下三个: 1:NSManagedObjectModel 2:NSPersistentStoreCoordinator 3:NSManagedObjectContext 在此也特别的说明一下,如果你没有理解透这三个类分别是做什么的,那么往后看到的代码都有一种非常迷茫的感觉: 接下来分别介绍每一个类的具体功能和用途: 1.NSManagedObjectModel(管理对象模型,以下简称:上下文): 构建整个数据库的表结构,表字段类型,表与表之间的关系(Relationship)等等凡是和数据结构有关的定义都通过此类来管理. 那么使用此类需要一个Data Model(数据模型)文件来配合其一起使用,如下图所示新建出来: 那么我们所有数据结构的定义和设计都用这个Data Model来完成. 在代码方面需要通过文件路径的方式找到它,并初始化NSManagedObjectModel NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Data Model Name" withExtension:@"momd"]; self.keyManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];注:新建Data Model文件以后的文件扩展名称是: Data Model Name.xcdatamodeld但通过XCode编译打包成App以后,其会被转换成一个Data Model Name.momd文件.而我们真正要加的模型文件就是这个Data Model Name.momd文件. 2.NSPersistentStoreCoordinator(持久性数据协调器): NSPersistentStoreCoordinator是真正意义上和SQLite打交道的类,主要根据NSManagedObjectModel 执行表结构的建立,通过NSManagedObjectContext 的命令执行数据交互 . self.keyPersistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: self.keyManagedObjectModel]; // handle db upgrade NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; if (![self.keyPersistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {}注:通过NSManagedObjectModel初始化,一旦初始化成功,SQLite的DB,就已经有了完善的表结构关系,不过这不是我们关心的重点,继续往下. 3.NSManagedObjectContext(管理对象上下文) NSManagedObjectContext是我们在开发中主要交互的类,数据的增删改查都通过上下文去触发命令并返回结果. 根据一个NSPersistentStoreCoordinator 完成初始化 self.keyManagedObjectContext = [[NSManagedObjectContext alloc] init]; [self.keyManagedObjectContext setPersistentStoreCoordinator:self.keyPersistentStoreCoordinator]; 到此,CoreData的准备工作已经完成,其实XCode已经有模版可以直接完成CoreData的准备工作,不过对于新手来说,最好还是一步一步来,加以理解,以便出现BUG时,能够及时找到解决方案,贵在理解!接下来,开始操纵数据! 插入一条?更新一条?删除一条? 熟悉Sql语句的同学:脑子里立刻会想到:insert into table , update table , delete table 那么在CoreData,这三项工作全部通过save函数来完成,一个函数完成三件事,CoreData这么犀利的? NSPredicate(条件适配器) NSPredicate主要为NSFetchRequest而服务,提供查询时的各种条件语句,方面过滤出复合业务需求的数据. 以下先列出 NSPredicate 支持的通配符 1:相等(==) 举例: field == 'value' 2:不相等(!=) 举例: field != 'value' 3:模糊(like) 举例: field like '*value*' 或者 field like '?value?' like 使用?表示一个字符,*表示多个字符 4:比较( > < <= >= ) 举例: field > 6 以上4种通配符都是字符串直接拼接即可,接下来的通配符在拼接字符串方面较为麻烦,但有相关代码可以辅助拼接. 5:范围(between) 举例: field between {"6", "10"} 可以通过如下代码拼接条件命令: NSArray *range = [[NSArray alloc]initWithObjects:@"6",@"10",nil]; NSPredicate *betweenPredicate =[NSPredicate predicateWithFormat:@"field between %@", range]; NSLog(@"%@",betweenPredicate.predicateFormat);6:包含(in) 举例: filed IN {"value1", "value2"} 可以通过如下代码拼接条件命令:NSArray *choice = [[NSArray alloc]initWithObjects:@"value1",@"value2",nil]; NSPredicate *inPredicate =[NSPredicate predicateWithFormat:@"filed in %@", choice]; NSLog(@"%@",inPredicate.predicateFormat);7:复合(or and not) 举例: filed == "value2" OR filed == "value3" 也可以通过如下代码拼接: NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"filed == 'value1' "]; NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"filed == 'value2' "]; NSArray *predicates = [[NSArray alloc]initWithObjects:predicate1,predicate2,nil]; NSPredicate *andCompoundPredicate =[NSCompoundPredicate orPredicateWithSubpredicates:predicates]; 在调用save 函数时我需要注意些什么? 一个对象只属于一个上下文对象,所以不同上下文管辖的对象不允许用一个上下文来调用save 方法,这只会失败,错误提示如下: Illegal attempt to establish a relationship 'xyz' between objects in different contexts 解决办法是(参考:StackOverflow): NSManagedObject *book = // get a book in one MOC NSManagedObject *owner = // get an owner in a different MOC [[owner mutableSetValueForKey:@"books"] addObject:[owner managedObjectContext:objectWithID:[book objectID]]]; Persistent Store Coordinator (持久性数据协调器): 你可以将这个东西看作是数据库连接库,在这里,你将设置数据存储的名字和位置,以及数据存储的时机。 Managed Object Context (管理数据内容):你可以将这一部分看作是数据的实际内容,这也是整个数据库中对我们而言最重要的部分(这还用说),基本上,插入数据,查询数据,删除数据的工作都在这里完成。 NSFetchRequest* request = [[NSFetchRequest alloc] init]; [request setEntity:entity];[request setResultType:NSManagedObjectIDResultType]; [request setFetchBatchSize:20]; NSError* error = nil; NSArray* items = [context executeFetchRequest:request error:&error]; for (NSManagedObjectID* objectID in items) { NSManagedObject* object = [context objectWithID:objectID]; ...} countForFetchRequest:error 1:表与表之间关系建立教程 http://blog.csdn.net/fengsh998/article/details/81233922:针对应用升级和表结构变动时 兼容旧版本的CoreData数据库解决办法. 遇到的问题: 当你将CoreData 加入到工程中,并启动了App一切都运行良好, 可是开发途中修改了CoreData 的 数据结构,比如添加或者删除了某个字段,或者新添加了一张表. 此时,再运行App时,发现App直接Crash. 如何解决: 这说明CoreData无法做到时时的去修改表结构,但CoreData可以以多个副本的形式来处理数据结构变化时的Crash问题. 阐述一下原理: 原理类似SVN 需要打一个 tag 一样, 一担打了Tag 就意味着这个版本的代码将不再允许修改,如果需要修改,需要到新的分枝里去实现. 当利用开发工具新建CoreData 管理文件以后:FEMicroCoopModel.xcdatamodeld 默认是只有一个分枝的. 那么添加分枝方式如下: 1.IDE->Editor->Add Model Version... 2.之后显示如下界面: 两个字段: Version name:版本名称(按你所需来取) Based on model:基础模型(这里选择一个,已经有的分枝,继承的概念) Finish之后就完成了,那么新的数据结构修改,都请在这个文件上面进行操作. 当你修改的差不多以后,需要设置 CoreData管理文件的 (Versioned Core Data Model) 当前使用版本,如下图: 只有这样应用运行时才会按照新版数据结构去迁移数据和修改表结构. 代码方面只有两个地方需要注意一下: 1:添加对数据结构版本自适应的配置,代码如下: // handle db upgrade NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; 2:在实例化 NSManagedObjectModel 对象 需要传入模型名称,这里只要是你当初建立 CoreData管理对象时的名称即可. 3:让控制台拥有输出 Core Data执行的SQL语句的能力. 为Edit Scheme - Run - Arguments - 添加一项值: -com.apple.CoreData.SQLDebug 1如下图所示: 3:警告和错误 1:has no children 警告提示如下图: 解决方法: 将图中的勾勾去掉 编译即可 总结:
搞了一年的iOS开发了,希望在这里记录一些设计师和开发者配合方面的一些经验 主要也是记录图片使用和命名方面的一些经验 App 启动展示图名称: 3GS:Default.png 3.5x-inch Retina:Default@2x.png 4x-inch Retina:Default-568h@2x.png App 图标名称: 3GS:icon.png Retina:icon@2x.png 1:切图的高度,直接决定在iPhone 显示的高度. 这篇帖子介绍了关于如何提到切图效率的技巧和经验 http://kevincao.com/2011/08/prepare-png-for-iphone-app/ 里面有几个要点在这里做一下总结: 1:利用PS的图层可一次性生成多张切图 2:利用Mac自带的Automator 软件设置工作流程之后可以很方便制作小图和添加@2x命名(注意的地方是:高清的px值需要为偶数) 制作 App介绍图时 iPhone 各个尺寸的原型图 http://resolution.im/ 所有命名前缀为英文,其他部位给出合理的英文标识区分 类型:图标说明:比如一个爱心,一个打勾 或者一个叉叉 就是图标了命名: icon_关于_爱心.png icon_设置_星星.png注意: 类型:按钮说明:这个就一个按钮咯,按钮的命名分为两种方式命名:一种是通用按钮,一种是专属按钮命名: button_红色.png button_淡红色.png button_淡绿色左.png button_淡绿色右.png button_淡绿色右_on.png button_淡绿色右_on_5_5_6_7.png button_关于_赞.png button_关于_赞_on.png注意: on是高亮,按下去 _5_5_6_7 是可拉伸式像素值. 一个按钮图标通常和按钮的实际大小有差别,这个时候为了保证让按钮图标适应真实的按钮长宽,需要被拉伸,但在拉伸的同时要有保证不会失真,不会变形. 固有了可拉伸式像素值这么一说. 有四个值,代表着一个图标的四周,而分别代表 上 左 下 右 ,如下图: 通过借定4周的区域, 最终以中间矩形区域来自动填充所需要阔达的区域 类型:背景说明:比如某个版面的背景图,或者文本框的背景图命名: view_关于_文本框_背景.png view_关于_正文内容_背景.png view_关于_正文内容_背景_5_5_6_7.png注意:_5_5_6_7 是可拉式像素值 类型:顶部工具条图标说明:iPhone顶部导航栏专用图标命名: toolbar_添加.png toolbar_发送.png注意: 类型:底部工具条图标说明:iPhone底部分类导航栏专用图标命名: tabbar_主页.png tabbar_更多.png注意: 类型:菜单图标说明:iPhone菜单模块专用图标 一般用于左侧抽屉那种命名: menu_主页.png menu_更多.png注意: 类型:专属控件UI说明:例如一个时间控件,或者一个日历控件,这些UI资源只有在这个控件才会用到,那命名开头就以这个控件来命名命名: 如果是日历控件: calendar_主页.png calendar_更多.png 如果是播放器控件: player_播放.png player_暂停.png注意: 更多待补充
引言: 二维码的普及速度令人咋舌,现在随处的实体广告在边角处都附上一张二维码,提示用户扫描相关信息,了解更多,并且张小龙说,PC的入口在于搜索框,而手机的入口在于二维码. 因为二维码解决了手机输入信息的困难.除了感叹人类的聪明之外,更多的是如何利用这已有的技术去满足我们在应用上面的需求. 当然了,内部算法不在本博文的介绍范围内,主要介绍如何使用它. 参考资料: 1:开博来的第一篇文贴:zxing2.0二维码在xcode4.2中的使用 http://blog.csdn.net/icash/article/details/7727299 2:ios中使用zxing遇到的问题 http://blog.csdn.net/zzfsuiye/article/details/8244917 3:让ZXing 支持条形码扫描 http://hi.baidu.com/397362542/item/a574ce39b267fdd12f8ec23b 如何安装: 1:ZXingWidget的托管地址如下: http://code.google.com/p/zxing/downloads/list 2:关于库里的一些文件 不是所有的文件我们都用的到, 下载zxing2.0后,解压得到zxing-2.0文件夹,里面只保留 cpp 和 iphone 两个文件夹就可以了。 3:我比较用WorkSpace(工作空间)的方式引入第三方资源 那么有一步骤可以省去: 4:frameWorks添加如下几项: libZXingWidget.a AddressBook AddressBookUI AudioToolbox AVFoundation CoreMedia CoreVideo libiconv.dylib 完成后如下图: 5:不要忘记设置一个扫描成功时的音频文件: widController.soundToPlay = [NSURL fileURLWithPath:[mainBundle pathForResource:@"beep-beep" ofType:@"aiff"] isDirectory:NO]; 6:保持项目的C++混编译模式 首先请做如下操作: 1.请更改你的delegate文件的.m文件为.mm (请注意,如果你在项目中重命名,并不会使物理路径中的文件名被更改,所以请更改物理文件名) MyZxingAppDelegate.m 改成 MyZxingAppDelegate.mm 2.请更改你要使用zxing的项目文件的.m文件为.mm MyZxingViewController.m 改成 MyZxing ViewController .mm 使用:7:下载我的Demo 试试看: XCode 4.6 iOS6.1 测试通过 Demo地址:猛击此处! 可能遇到的问题: 1.如果你项目中使用了zxing,但是在xcdoe升级到4.5以后出现Incompatible pointer types sending 'Class' (aka 'Class *') to parameter of type 'id<NSCopying>'错误,解决办法:重新下载更新版本的ZXing, 可以使用命令下载最新版本:svn checkout http://zxing.googlecode.com/svn/trunk/ zxing-read-only。 2.如果出现了问题Undefined symbols for architecture i386"std::string::c_str() const", referenced from。。。。, 将你项目中的Apple LLVM compiler 4.1 - language中的 c Language Dialect、c++ Language Dialect、c++ standard Library设置成下图所选的值。参考http://stackoverflow.com/questions/12665457/zxing-in-xcode-4-5-and-ios-6 3.编译成功往真机上装的时候出现Choose a destination with a supported architecture in order to run on this device ,由于ios设备不支持armv7s,所以必须将Architectures设置为armv6,但是仅仅需要改动valid architectures就行,不要改动architectures,否则容易引起真机不运行。把architectures改为$(ARCHS_STANDARD_32_BIT)就可以撞到手机上了。见http://blog.sina.com.cn/s/blog_90a0ad8d01013uuh.html 4:XCode 手贱升级到了 4.6 ZXingSDK 编译不过去了. 出现什么变量声明了没有使用的编译错误提示(可是明明使用了): Member initializer 'bits_' does not name a non-static data member or base class 谷歌老外牛人多啊,或者说老外也遇到这个问题了,然后提问了,问答地址如下: http://code.google.com/p/zxing/issues/detail?id=1494 里面说到将 ZXingWidget - BuildSettings - Other Warning Flags 里的 -Werror 和 -Wno-unused-parameter 去掉, 但其实只要去掉 -Werror 就可以了, 如果 -Wno-unused-parameter 这个也去掉了会产生很多变量没有使用的警告. 此时可以编译通过了,烦恼的是 也有少许警告. 2014-02-18 16:03:17 番外篇ZXingObjC: ZXingObjC通过名字即可知道它是干什么的,它是二维码扫描开源方面的又一力作,代码采用纯Objective-C实现,内存管理使用ARC,并且作者依旧在持续更新,那么在稳定和代码结构方面应该是优易于ZXing原本的实现.相比ZXing的使用和部署,ZXingObjC就简单很多,最关键的是,它还支持CocoaPods.那么安装起来就非常非常简单了.接下来一起来看看如何使用它. 如何安装: ZXingObjC通过CocoaPods即可快速安装. 如何使用:
源代码地址: AFNetworking的托管地址: https://github.com/AFNetworking/AFNetworking 安装 安装的首选方式使用CocodPods 问题1:安装完毕后,可能遇到问题,如下图: 原因是: MobileCoreServices.framework 框架没有引用到主工程中. 引入即可. 问题2: #import <AFNetworking> 以后编辑时产生警告,如下图: 解决办法是,在 Project-Prefix.pch 文件中添加如下全局引入: #ifdef __OBJC__ #import <Cocoa/Cocoa.h> #import <SystemConfiguration/SystemConfiguration.h> #import <MobileCoreServices/MobileCoreServices.h> #endif参考Stackoverflow的一问 使用: 通过 AFNetworkReachabilityManager 监控网络可达性
按照常例,列一些网址先: 微信开放平台首页: http://open.weixin.qq.com 微信 iOS SDK 官方下载地址: http://open.weixin.qq.com/download/sdk/wechat_sdk_ios.zip 微信 分享好友API调用官方使用教程: http://open.weixin.qq.com/document/gettingstart/ios/?lang=zh_CN 目前来说:微信开放平台主要提供两个API: 1:将信息分享给微信好友 2:将信息直接分享至朋友圈 其实官方教程已经非常详细了,我也是按照官方的一步步来得以实现, 那为什么写这篇教程? 有一个最主要的目的是,减少没必要的时间开销,以及一些官方教程上面提到的一些盲区. 开始: 将数据分享给好友总共5个步骤,注意:不要乱了顺序: 1:在工程Plist文件中添加一个 URL type "URL scheme”为你所注册的应用程序id,既AppID 2:引入头文件,在接口处声明要实现的委托. 3:将申请的AppID 注册, 这个方法不调用,是无法启动微信客户端的. [WXApi registerApp:WeiXinAppID]4:在AppDelegate中实现如下委托来接收来自微信客户端的回调响应: -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { //如果涉及其他应用交互,请做如下判断,例如:还可能和新浪微博进行交互 if ([url.scheme isEqualToString:WeiXinAppID]) { return [WXApi handleOpenURL:url delegate:self]; }else { return YES; } } 5:通过调用如下方法及参数 来 立刻显示微信客户端 SendMessageToWXReq *sendMsg = [[SendMessageToWXReq alloc] init]; sendMsg.text = @"123"; sendMsg.bText = YES; [WXApi sendReq:sendMsg]; 6:当微信界面的返回按钮被点击时,微信客户端会回调之前启用自己的应用,此处触发通用的UIApplicationDelegate 代理,也就是第四点. 当执行 [WXApi handleOpenURL:url delegate:self] 以后 触发微信API自带的委托: -(void)onResp:(BaseResp *)resp { NSLog(@"%@",resp); NSLog(@"errStr %@",[resp errStr]); NSLog(@"errCode %d",[resp errCode]); NSLog(@"type %d",[resp type]); } 自此,整个应用与微信客户端交互就结束了. Date:2013-1-5 21:13 如果微信的SDK在工作空间的环境下使用 并在工程配置文件的 Other Linker Flags 中设置了 -all_load 那么会真机编译时产生编译错误,也不知道是到底是什么文件出了问题,那么这时 就不要使用-all_load了, 使用 -force_load
引言: 官方的简介加少许语义上的修改: iConsole是一个简单的,无依赖的控制台管理类,让iPhone在运行App时记录更多用户使用时的记录. 它可以准确的定位程序所遇到的错误,内置在应用中,而无需连接到XCode调试器即可查询崩溃日志并保存起来. 所以,普通用户可以主动提交在使用时遇到的错误日志. 如何开始使用? 开源项目下载地址:猛击此处 下载以后翻开项目的源代码找到如下两个文件夹,分别是: 将这两个文件夹拖动要使用的工程中,此时无需任何其他依赖项的设置. 如何编写代码? 在使用其功能之前有一点要注意,需要将AppDelegate里初始的UIWindow需要以iConsoleWindow来初始化. 如果项目中没有使用StoryBoard来初始化工程,则进行如下修改即可: - (void)applicationDidFinishLaunching:(UIApplication *)application { _window = [[iConsoleWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; _window.rootViewController = [[HelloWorldViewController alloc] init]; [_window makeKeyAndVisible]; } 如果是通过StoryBoard来初始化工程的话需要进入如下修改: 1:第一步将工程设置里的Main StoryBoard清空 2:applicationDidFinishLaunching里面添加如下代码: //启动iConsonle self.window = [[iConsoleWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = [[UIStoryboard storyboardWithName:MainStoryBoardName bundle:nil] instantiateInitialViewController]; //other load code [self.window makeKeyAndVisible]; 至于其它代码请写在other load code的位置, 不然会Crash.原理是UIWindow. 到此,iConsole的部署及初始化工作已经完成,iConsole会默默的记录App产品的Crash信息. 接下来介绍其强大的功能及细节. iConsole提供了4种记录日程的方式,分别是: 1:信息 2:警告 3:错误 4:崩溃 通过以上4种记录的级别由开发者自行管理其分类,以备App脱离XCode调试器时,辅助开发者通过日志理解错误的具体位置. 代码如下: [iConsole info:@"记录一条普通级"]; [iConsole warn:@"记录一条警告级"]; [iConsole error:@"记录一条错误级"]; [iConsole crash:@"记录一条崩溃级"]; iConsole 默认记录的日志条数是1000条. 如果日志记录到上限时,会删除最早的一条,如此循环,可通过如下属性设置上下限: [[iConsole sharedConsole] setMaxLogItems:2000]; 当我们需要查看iConsole的控制台时,可以通过三种方式来启动控制台: 1:主动调用 [iConsole show]; 2:三跟手指由下往上滑动. 模拟器两根手指, 默认启动 [iConsole sharedConsole].simulatorTouchesToShow = YES; [iConsole sharedConsole].deviceTouchesToShow = YES; 3:摇动手机启动 默认禁用 [iConsole sharedConsole].deviceShakeToShow = YES; 成功进入控制台以后,可以看到有一个输入区域,等待用户输入命令,如下图所示: 此时需要实现iConsole唯一的一个代理,用来捕捉命令行的值: 1:代理赋值 [iConsole sharedConsole].delegate = self; 2:实现接口方法 - (void)handleConsoleCommand:(NSString *)command { if ([command isEqualToString:@"version"]) { [iConsole info:@"%@ version %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"], [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]; } else { [iConsole error:@"unrecognised command, try 'version' instead"]; } } 这个代理到底有什么用? 这里的权利完全交给了开发者,通过自定义一些命令来输出一些有利于调试的日志信息. 比如version命令,一旦成功进入代理以后,将会自动打印当前App的程序版本 更改的辅助命令由开发者根据个性化需求自由定制 另外iConsole的控制台也可以进行少许的样式修改: //branding and feedback @property (nonatomic, copy) NSString *infoString;//控制台简短介绍 @property (nonatomic, copy) NSString *inputPlaceholderString;//命令行空值提示 @property (nonatomic, copy) NSString *logSubmissionEmail;//日志所发送的邮件地址 //styling @property (nonatomic, strong) UIColor *backgroundColor;//背景颜色 @property (nonatomic, strong) UIColor *textColor;//文字颜色 @property (nonatomic, assign) UIScrollViewIndicatorStyle indicatorStyle;//这个呢? 额外的说明: 模拟一个Crash 用于尝试使用iConsole 的功能,代码如下: [[NSException exceptionWithName:@"什么类型的崩溃异常?" reason:@"对崩溃进行些说明?" userInfo:nil] raise]; 总结: iConsole在于对XCode控制台脱离时依旧能保持调试的能力,方面辅助开发者捕捉致命的BUG. 建议阅读本文所有的开发者都是用.
引言: 新浪微博几乎是把全平台数据的API接口都开放了出来,因此,很多优秀的第三方微博客户端在功能方面都非常的全面. 而通过SNS的分享推广方式在App世界里已经非常的普遍,甚至随处可见,本篇主要介绍一下App是如何跟新浪微博关联的. 参考资料: 1.开发平台首页: http://open.weibo.com/?bottomnav=1&wvr=5 2.API文档首页: http://open.weibo.com/wiki/API文档_V2 3.API错误代码说明地址: http://open.weibo.com/wiki/Error_code 4.iOS SDK 地址: https://github.com/mobileresearch/weibo_ios_sdk_sso-oauth 5.授权机制: http://open.weibo.com/wiki/授权机制说明 6.开发者管理中心 http://open.weibo.com/apps 使用: 在管理中心中创建自己的应用以后,会得到AppKey 和 App Secret 这两个值 是初始化新浪微博SDK必须要用到的两个参数. 当执行 login 函数时 可能遇到的错误如下 1:访问出错提示 表示: 微博SDK初始化时设置的 appRedirectURI 和微博开放平台-开发者管理中心-应用信息-高级信息-OAuth2.0 授权设置-授权回调页 所设置的值不一样,才会出现如上错误. 2:调用新浪微博客户端授权以后没有正常返回应用. 1:检查 URL type "URL scheme” 是否设置了名为: sinaweibosso.加AppID 如图: 2:AppDelegate 中 是否实现委托函数: -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { NSLog(@"%@",url.scheme); //如果涉及其他应用交互,请做如下判断,例如:还可能和新浪微博进行交互 if ([url.scheme isEqualToString:Key_weiXinAppID]) { return [WXApi handleOpenURL:url delegate:self]; }else if ([url.scheme isEqualToString:[@"sinaweibosso" stringByAppendingPathExtension:Key_sinaWeiboAppID]]) { return [[SinaWeiBoManage defaultInstance].sinaWeibo handleOpenURL:url]; }else { return YES; } } 以上设置完成以后,不出意外,将会响应授权结果. 接下来就主要开始调用API来进行微博的数据交互了. 举个简单的例子,如何获取授权用户的个人信息: NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; [params setObject:userId forKey:@"uid"]; [sinaWeibo requestWithURL:@"users/show.json" params:params httpMethod:@"GET" delegate:self]; 具体要传入什么参数,请查阅官方API文档. 得到结果后会响应成功或者失败的委托: 此时可以用过链接名称来识别请求类型: - (void)request:(SinaWeiboRequest *)request didFailWithError:(NSError *)error { //获取关注列表 if ([request.url hasSuffix:@"friendships/friends.json"]) { if ([delegate respondsToSelector:@selector(sinaWeiBoManage:withFriendListResult:withRequestDataType:isSuccess:)]) { [delegate sinaWeiBoManage:self withFriendListResult:nil withRequestDataType:self.requestDataType isSuccess:NO]; } } } 关于iOS 6中内置微博功能: 在iOS6中苹果集成了新浪微博的社交环境,所以,如果用户在设置界面中授权了新浪微博账户,我们第三方应用中就可以直接使用,利用其发微博等等 首先引入两个 新的 framework 分别是: Accounts.framework :用于获取系统设置中的 账户信息 Social.framework :用于对第三方开放平台进行数据交互. 流程分为两步: 首先要知道用户有没有在系统的 设置中 授权了对应的账户, 如果拿到对应的账户信息以后就可以开始对第三方开放平台进行数据交互了,代码如下: // Create an account store object. 创建账户集合 ACAccountStore *accountStore = [[ACAccountStore alloc] init]; // Create an account type that ensures Twitter accounts are retrieved. 确定好 账户类型 新浪微博 还是 Facebook 还是 Twitter ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierSinaWeibo]; // Request access from the user to use their Twitter accounts. //异步请求 来得到对应类型的账户信息 [accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) { if(granted) {//如果 granted 返回 NO 表示一个账户都没有设置 // Get the list of Twitter accounts. NSArray *accountsArray = [accountStore accountsWithAccountType:accountType]; //可能存在多个,看你要用哪个,最好让用户选择一下 // For the sake of brevity, we'll assume there is only one Twitter account present. // You would ideally ask the user which account they want to tweet from, if there is more than one Twitter account present. if ([accountsArray count] > 0) { // Grab the initial Twitter account to tweet from. ACAccount *sinaWeiboAccount = [accountsArray objectAtIndex:0]; NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; [params setObject:@"一条新的微博" forKey:@"status"]; SLRequest *slRequest = [SLRequest requestForServiceType:SLServiceTypeSinaWeibo requestMethod:SLRequestMethodPOST URL:[NSURL URLWithString:@"https://open.weibo.cn/2/statuses/update.json"] parameters:params]; slRequest.account = sinaWeiboAccount;//这行代码一定要赋值,负责数据交互一定失败 [slRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) { NSLog(@"%@ %@",urlResponse.URL,error); NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil]; NSLog(@"%@",dic); }]; } } }]; 在拿到每个 ACAccount 以后 自身都有一个 identifier 啊在用户确认选好了使用哪个账户时最好能够保存下来,那么下次可以直接通过如下代码获取到对应的账户 [accountStore accountWithIdentifier:sinaWeiboAccount.identifier]; 总结: SNS做为应用推广的一个主要途径,是必须好好学习一下的,如何勾起和引起用户分享的欲望,让更多的人知道你的应用,那么离成功就不远了.
引言: SDWebImage是我搞iOS以来少数佩服的框架,膜拜一下作者.真的写的非常棒! 这套开源框架还是蛮重要的, 涉及到异步加载图片源和自动缓存. 我们如果能够熟练使用其API 就可以实现很多复杂的需求了. 作者依旧在更新,目前3.0 版本已经非常强大! 简化了更多的API接口.加强了下载类的支持和多个文件同时下载,以及内置了下载进度功能等 安装: 首选的安装方式是CocoaPods Github托管地址如下: https://github.com/rs/SDWebImage SDWebImage API 文档入口(英文的,蛋疼,如果中文的话,估计咱也不写这篇文章了,要么也很短.): http://hackemist.com/SDWebImage/doc/ 如果你只是匆匆忙忙下载了一个framework 就回到了自己工程,可能会遇到无法使用的情况:那么下面的文章来解决你遇到的问题: http://blog.csdn.net/qjlhlh/article/details/8191111 里面提到一句,使用这类代码的时候,最好预先看看Readme(使用说明) 1:引入系统框架 ImageIO.framework. 2:framework 引入头文件的方式如下: #import<SDWebImage/UIImageView+WebCache.h> 3:Build Settings->Other Linker Flags->-ObjC or-all_load 使用: SDWebImageDownloader 这个下载器改版很大,现在里面就一个方法,就是发起前往下载图片的函数: [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:imageUrl options:SDWebImageDownloaderProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) { NSLog(@"%u %lld",receivedSize,expectedSize); } completed:^(UIImage *aImage, NSData *data, NSError *error, BOOL finished) { //self.image = aImage; NSLog(@"成功了:%d",UIImageJPEGRepresentation(aImage, 1).length); }]; 里面大部分参数属于一看就懂的,进度Block返回的信息有两个参数:1:下载进度. 2:文件大小. 我这里解释一下里面的options枚举的作用 SDWebImageDownloaderOptions 1:SDWebImageDownloaderLowPriority 这个属于默认的使用模式了,前往下载,返回进度Block信息,完成时调用completedBlock 2:SDWebImageDownloaderProgressiveDownload 这个是新版本加的功能,设置后,在返回进度Block的同时,返回completedBlock,里面的UIImage就是当前下载时的图片,可以实现将图片一点点显示出来的功能. 人世间最难的莫过于理解了! 开句玩笑^^ 那么SDWebImageDownloader 在新版本的中的分工很明确,只做一件事情,那就是下载,只是下载而已,所以,下载成功以后的图片资源是不会自动缓存的. 如果要缓存,这里需要使用到 SDImageCache SDImageCacheType 当下载请求结束时,通过这个枚举来告知图片的来源 1:SDImageCacheTypeNone 网络 2:SDImageCacheTypeDisk 设备硬盘 3:SDImageCacheTypeMemory 内存 将上面两步骤的需求合并可直接使用 SDWebImageManager 新版本的管理类的改动也相当大,而且就一个方法了,如下: [[SDWebImageManager sharedManager] downloadWithURL:imageUrl options:SDWebImageLowPriority progress:^(NSUInteger receivedSize, long long expectedSize) { NSLog(@"%u %lld",receivedSize,expectedSize); } completed:^(UIImage *aImage, NSError *error, SDImageCacheType cacheType, BOOL finished) { self.image = aImage; NSLog(@"成功了:%d",UIImageJPEGRepresentation(aImage, 1).length); }]; 这样发起的下载请求,就会自动缓存图片资源了. 那么任何发起的下载请求,都会返回一个代理. 这个写法很神奇,值得深究啊. 也就是:SDWebImageOperation 将这个委托缓存一下,做什么用呢?一件事情,取消下载请求. 这边文章介绍了整个SDWebImage的运作原理,你看完之后,一定会说:真TMD 的复杂 http://blog.csdn.net/uxyheaven/article/details/7909373
今天开始使用百度地图的API实现相关的地理位置功能 在根据官方文档: http://developer.baidu.com/map/sdkiosdev-2.htm 进行一系列的引入后,还需要注意以下两个细节,否则编译和运行时都会出错: 1:让XCode 处于 Objective - C++ 混编模式进行编译: 最简单方法就是:随便更改工程文件中的某一个,将.m更改为.mm . 2:由于静态库里面包含类别条目(第四点),所以需要让工程支持类别的编译: Project->Build Settings->Other Linker Flags 添加值: -all_load 3:关于 setPaopaoView 警告 临时解决方案如下: 在 Other Linker Flags新增一个 -w 4:建议合并静态库 接下来开始记录具体用到的功能点: 第一个:成功载入地图后,开启定位功能,确认当前使用设备所在的地理位置,代码如下: 1: 开发定位功能,允许地图应用时时获取地理位置信息,并触发委托. _mapView.showsUserLocation = YES;//开启定位服务所触发的委托: - (void)mapView:(BMKMapView *)mapView didUpdateUserLocation:(BMKUserLocation *)userLocation { }2:如果以上委托被调用,说明位置信息已经成功获取,接下来需要将地图位置,移动到定位所在的位置,代码如下:_mapView.userLocation //记录设备当前所在位置 NSLog(@"!latitude!!! %f",userLocation.location.coordinate.latitude);//经度 NSLog(@"!longtitude!!! %f",userLocation.location.coordinate.longitude);//纬度 //传入经纬度,将baiduMapView 锁定到以当前经纬度为中心点的显示区域和合适的显示范围 - (void)setMapRegionWithCoordinate:(CLLocationCoordinate2D)coordinate { BMKCoordinateRegion region; if (!_isSetMapSpan)//这里用一个变量判断一下,只在第一次锁定显示区域时 设置一下显示范围 Map Region { region = BMKCoordinateRegionMake(coordinate, BMKCoordinateSpanMake(0.05, 0.05));//越小地图显示越详细 _isSetMapSpan = YES; [baiduMapView setRegion:region animated:YES];//执行设定显示范围 } _currentSelectCoordinate = coordinate; [baiduMapView setCenterCoordinate:coordinate animated:YES];//根据提供的经纬度为中心原点 以动画的形式移动到该区域 } 执行 setCenterCoordinate:coordinate 以后开始移动,当移动完成后,会执行以下委托: - (void)mapView:(BMKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { [baiduMapView.annotations enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { BMKPointAnnotation *item = (BMKPointAnnotation *)obj; if (item.coordinate.latitude == _currentSelectCoordinate.latitude && item.coordinate.longitude == _currentSelectCoordinate.longitude ) { [baiduMapView selectAnnotation:obj animated:YES];//执行之后,会让地图中的标注处于弹出气泡框状态 *stop = YES; } }]; } 注:0.05 表示显示区域的详细程度,设定的值最小,其显示的地图区域也就更详细,这个自己试试吧. 几句话也描述不清楚. 另外位置信息获取到以后,会不停的去获取,所以不需要使用的时候,把_mapView.showsUserLocation 设置为NO; 第二个:给指定的位置加入标注. BMKPointAnnotation* item = [[BMKPointAnnotation alloc]init]; item.coordinate = coordinate;//经纬度 item.title = titleString; //标题 item.subtitle = subTitleString;//子标题 [baiduMapView addAnnotation:item]; 注:为地图类引用 添加一个 标注 执行 addAnnotation 以后 baiduMapView为触发以下委托,此委托可以定制化标注视图 //原理类似 UITableView 循环委托加载 CellforRowWithIndexPath - (BMKAnnotationView *)mapView:(BMKMapView *)view viewForAnnotation:(id <BMKAnnotation>)annotation { static NSString *AnnotationViewID = @"annotationViewID"; BMKAnnotationView *annotationView = [view dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID]; if (annotationView == nil) { annotationView = [[BMKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID]; ((BMKPinAnnotationView*)annotationView).animatesDrop = YES; annotationView.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_location.png"]];//气泡框左侧显示的View,可自定义 UIButton *selectButton = [UIButton buttonWithType:UIButtonTypeCustom]; [selectButton setFrame:(CGRect){260,0,50,annotationView.Help_height}]; [selectButton setTitle:@"确定" forState:UIControlStateNormal]; annotationView.rightCalloutAccessoryView =selectButton;//气泡框右侧显示的View 可自定义 [selectButton setBackgroundColor:[UIColor redColor]]; [selectButton setShowsTouchWhenHighlighted:YES]; [selectButton addTarget:self action:@selector(Location_selectPointAnnotation:) forControlEvents:UIControlEventTouchUpInside]; } //以下三行代码用于将自定义视图和标记绑定,一一对应,目的是当点击,右侧自定义视图时,能够知道点击的是那个标记 annotationView.rightCalloutAccessoryView.tag = _cacheAnnotationTag; [_cacheAnnotationMDic setObject:annotation forKey:[NSNumber numberWithInteger:_cacheAnnotationTag]]; _cacheAnnotationTag++; //如果是我的位置标注,则允许用户拖动改标注视图,并赋予绿色样式 处于 if ([annotation.title isEqualToString:String_myLocation]) { ((BMKPinAnnotationView *)annotationView).pinColor = BMKPinAnnotationColorGreen;//标注呈绿色样式 [annotationView setDraggable:YES];//允许用户拖动 [annotationView setSelected:YES animated:YES];//让标注处于弹出气泡框的状态 }else { ((BMKPinAnnotationView *)annotationView).pinColor = BMKPinAnnotationColorRed; } annotationView.centerOffset = CGPointMake(0, -(annotationView.frame.size.height * 0.5));//不知道干什么用的 annotationView.annotation = annotation;//绑定对应的标点经纬度 annotationView.canShowCallout = TRUE;//允许点击弹出气泡框 return annotationView; } 如果用户手动在地图中点击标注视图或者是 为 标注视图 BMKAnnotationView setSelect:YES 那么会触发以下委托: - (void)mapView:(BMKMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view { _currentSelectCoordinate = view.annotation.coordinate; } 再当点击弹出的气泡框时亦会触发以下委托: - (void)mapView:(BMKMapView *)mapView annotationViewForBubble:(BMKAnnotationView *)view; 如果要删除标注: NSMutableArray *annotationMArray = [[NSArray arrayWithArray:baiduMapView.annotations] mutableCopy]; [baiduMapView removeAnnotations:annotationMArray]; 第三个就是开始POI检索 初始化: baiduMapSearch = [[BMKSearch alloc] init]; baiduMapSearch.delegate =self; [BMKSearch setPageCapacity:10]; //设置每次搜索多少页 开始搜索: 提供 搜索 城市 和关键字 BOOL flag = [baiduMapSearch poiSearchInCity:_currentCity withKey:_searchKeywordString pageIndex:_searchPageIndex]; 搜索委托 - (void)onGetPoiResult:(NSArray*)poiResultList searchType:(int)type errorCode:(int)error { //这里判断表示顺利搜索成功 if (error == BMKErrorOk) { BMKPoiResult* result = [poiResultList objectAtIndex:0];//如果有表示搜索到数据 for (int i = 0; i < result.poiInfoList.count; i++) { BMKPoiInfo* poi = [result.poiInfoList objectAtIndex:i]; } } } 注:BMKPoiResult 里面展示当前搜索情况,如下:总共搜索到多少条数据,本次搜索到多少条数据,当前搜索到第几页等 BMKPoiInfo 是每一个搜索结果,里面有电话 ,地址,经纬度 等 最多附上相关资源包: http://www.kuaipan.cn/file/id_10716325655621670.htm密码:QNWNaf
音频方面的知识,相对其他编程还是较为复杂的,特别是要搞清楚框架里具体使用的参数和方法,不然写起代码来非常迷茫. 1:播放简短性质的音频,例如按键声音,等可以这样实现. 一:引入框架: #import <AudioToolbox/AudioToolbox.h> 二:先声明一个声音源ID SystemSoundID _bookSoundID; 三:提供需要播放的音频地址进行声音源的注册. NSURL *bookSoundUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"bookSound" ofType:@"wav"]]; AudioServicesCreateSystemSoundID((__bridge CFURLRef)bookSoundUrl, &_bookSoundID); 四:在需要的时候播放: AudioServicesPlaySystemSound(_bookSoundID); 五:不用的声音源记得释放掉 AudioServicesDisposeSystemSoundID(_bookSoundID); 2: 关于 AVAudioSession 的使用 首先知道 AVAudioSession 是一个单例模式,也就是说,不用开发者自行实例化. 这个类在各种音频环境中起着非常重要的作用 一:首先是设置 AVAudioSession 的 类别 获取输入硬 件 获取输出硬件 与IPOD混合 遵从振铃/静音按键AVAudioSessionCategoryAmbient 否 是 是 是 AVAudioSessionCategorySoloAmbient 否 是 否 是 AVAudioSessionCategoryPlayback 否 是 否 否 AVAudioSessionCategoryRecord 是 否 否 否 AVAudioSessionCategoryPlayAndRecord 是 是 否 否 根据实际的使用情况来设定具体的类别,设置代码如下: AVAudioSession * audioSession = [AVAudioSession sharedInstance]; //得到AVAudioSession单例对象 [audioSession setDelegate:self];//设定代理 [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error: &error];//设置类别,表示该应用同时支持播放和录音 [audioSession setActive:YES error: &error];//启动音频会话管理,此时会阻断后台音乐的播放. 二:在录制完声音或者播放完声音后,可以将音频会话关闭,来延续后台音乐的播放,代码如下: [[AVAudioSession sharedInstance] setActive:NO error: nil]; 三:通过音频会话可以强制的设置应用程序使用指定的输出方式,例如:内声道,扬声器,代码如下: UInt32 audioRouteOverride = hasHeadset ?kAudioSessionOverrideAudioRoute_None:kAudioSessionOverrideAudioRoute_Speaker; AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride); kAudioSessionOverrideAudioRoute_None 内声道,耳机 kAudioSessionOverrideAudioRoute_Speaker 扬声器 四:那么怎么判断用户是否已经插入耳机?代码如下: (参考:http://iandworld.sinaapp.com/?p=184001) - (BOOL)hasHeadset { //模拟器不支持 #if TARGET_IPHONE_SIMULATOR #warning *** Simulator mode: audio session code works only on a device return NO; #else CFStringRef route; UInt32 propertySize = sizeof(CFStringRef); AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route); if((route == NULL) || (CFStringGetLength(route) == 0)){ // Silent Mode NSLog(@"AudioRoute: SILENT, do nothing!"); } else { NSString* routeStr = (__bridge NSString*)route; NSLog(@"AudioRoute: %@", routeStr); /* Known values of route: * "Headset" * "Headphone" * "Speaker" * "SpeakerAndMicrophone" * "HeadphonesAndMicrophone" * "HeadsetInOut" * "ReceiverAndMicrophone" * "Lineout" */ NSRange headphoneRange = [routeStr rangeOfString : @"Headphone"]; NSRange headsetRange = [routeStr rangeOfString : @"Headset"]; if (headphoneRange.location != NSNotFound) { return YES; } else if(headsetRange.location != NSNotFound) { return YES; } } return NO; #endif } 返回YES,表示已经插入耳机,返回NO表示没有插入耳机 五:监听用户拔出插入耳机事件 1:注册监听事件,和回调函数 AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, audioRouteChangeListenerCallback, self);2:实现回调函数进行相关处理: void audioRouteChangeListenerCallback ( void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue ) { if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return; // Determines the reason for the route change, to ensure that it is not // because of a category change. CFDictionaryRef routeChangeDictionary = inPropertyValue; CFNumberRef routeChangeReasonRef = CFDictionaryGetValue (routeChangeDictionary, CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); SInt32 routeChangeReason; CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); NSLog(@" ===================================== RouteChangeReason : %d", routeChangeReason); AudioHelper *_self = (AudioHelper *) inUserData; if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) { [_self resetSettings]; if (![_self hasHeadset]) { [[NSNotificationCenter defaultCenter] postNotificationName:@"ununpluggingHeadse" object:nil]; } } else if (routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable) { [_self resetSettings]; if (![_self hasMicphone]) { [[NSNotificationCenter defaultCenter] postNotificationName:@"pluggInMicrophone" object:nil]; } } else if (routeChangeReason == kAudioSessionRouteChangeReason_NoSuitableRouteForCategory) { [_self resetSettings]; [[NSNotificationCenter defaultCenter] postNotificationName:@"lostMicroPhone" object:nil]; } //else if (routeChangeReason == kAudioSessionRouteChangeReason_CategoryChange ) { // [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; //} [_self printCurrentCategory]; } 六:如何保持后台音乐的一直播放呢? (参考:http://blog.csdn.net/yhawaii/article/details/7788340) 1:在Info.plist中,添加"Required background modes"键,其值设置如下图所示: 2:系统音频服务支持音频播放,并关闭其他正在播放的音频 AVAudioSession *session = [AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil];3:设置app支持接受远程控制事件代码:[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 设置app支持接受远程控制事件,其实就是在dock中可以显示应用程序图标,同时点击该图片时,打开app,如下图所示: 4:执行 AVAudioPlayer 七 关于音量 1:由应用主动获取系统音量 UInt32 dataSize = sizeof(float); AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareOutputVolume, &dataSize, &keyVolume); 获取之前要确保 AVAudioSession *session = [AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; 2:由应用主动设置系统音量 (参考:http://blog.csdn.net/studyrecord/article/details/6452354) 八:关于音频边下载边播放的实现.请参考:AudioStreamer https://github.com/mattgallagher/AudioStreamer 如果你只是想简简单单在线播放以下不做任何处理. 那使用AVPlayer 等等 去实现在线播放,也是可以的,但是如果要实现更多更能,还是别折腾这玩意,浪费生命.
1:通过 CoreLocation.framework 获取当前位置所在的城市(适用于iOS 5以上) - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { CLGeocoder *geocoder = [[CLGeocoder alloc] init]; [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) { NSLog(@"%d",placemarks.count); for (CLPlacemark *placemark in placemarks) { NSLog(@"%@ %@ %@ %@",[placemark.addressDictionary objectForKey:@"City"],[placemark.addressDictionary objectForKey:@"Country"],[placemark.addressDictionary objectForKey:@"State"],[placemark.addressDictionary objectForKey:@"SubLocality"]); } }]; [manager stopUpdatingLocation]; } 注:在通过CLLocationManager 的委托获取具体位置数据以后,通过CLGeocoder 来获取相关城市信息.
引言: iOS的数据存储与缓存涉及到的知识方方面面,有蛮多需要去好好了解了解,那么做为移动设备的应用,离线数据的重要性,相对于PC应用显得重要的多. 文中介绍的方法,不一定都是最好的,但都是目前博主都在使用的技术,如果有更好的方法和技术,不吝赐教! 参考资料: 1:使用Keychain存储用户敏感信息 http://blog.csdn.net/tianyitianyi1/article/details/7643365 2:NSUserDefaults简介及使用 http://qing.blog.sina.com.cn/2739139371/a343f32b33001kpq.html 介绍: 1: KeyChain KeyChain是用于在iOS中存储敏感信息时的一个推荐型方案. 如果直接使用其API,还是非常繁琐的,为此网上已经有牛人,对此其API进行了封装,我们学会使用就行了. 两部操作即可: 第一步:引入系统框架文件 Security.framework 第二步:导入开源框架 SFHFKeychainUtils 参考链接中有源代码下载地址,这里就不提供了, 那么我这里提供一个 我使用过后的一个优化版:Demo 使用非常简单, 4个方法:添加,删除,查询,查询全部 注:KeyChain的信息存储不会因为App的被删除而丢失数据,这个有点类似Windows的注册表的概念差不多. 2:Property List (熟称的Plist) Property List是在App里面存储一些较为简单且零散的键值对的一个推荐型方法 以下代码 分别用于创建或修改Property List文件内部值: //通过文件路径获取 NSMutableDictionary *attachmentInfoDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:attachmentInfoPathString]; //判断是否成功从指定的路径中取到Plist 文件,如果没有,则实例化一个新的供写入 if (attachmentInfoDictionary == nil) { attachmentInfoDictionary = [[NSMutableDictionary alloc] init]; } [attachmentInfoDictionary setObject:@"值" forKey:@"键"]; NSLog(@"%@",attachmentInfoDictionary); //返回是否写入成功 但是需要确保指定的路径结构存在,否则肯定失败 [attachmentInfoDictionary writeToFile:attachmentInfoPathString atomically:NO]; [attachmentInfoDictionary release]; 3:NSUserDefaults NSUserDefaults是系统级Property List文件,里面存储一些App配置参数,当然,我们也可以加一些自己的值进行,用户和Property List几乎一样 1:查询NSUserDefaults 里面全部存储的值,这里面可以看到我们开发时存储的: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation] 2:当对NSUserDefaults 修改的值以后,需要调用同步函数实现数据持久化 [[NSUserDefaults standardUserDefaults] synchronize]; 4:Core Data Core Data是对SQLite的封装,以面向对象的方式与本地数据库进行数据交互 Core Data的介绍参考这篇文章. 5:StackMob StackMob是专为移动App打造的后端云存储解决方案,是博主大力推荐使用的Baas平台 StackMob的使用参考这篇文章. 6:SQLite SQLite 的使用参考FEDM 总结: 信息化离不开数据
引言: 枚举值 它是一个整形(int) 并且,它不参与内存的占用和释放,枚举定义变量即可直接使用,不用初始化. 在代码中使用枚举的目的只有一个,那就是增加代码的可读性. 使用: 枚举的定义如下: typedef enum { //以下是枚举成员 TestA = 0, TestB, TestC, TestD }Test;//枚举名称 亦可以如下定义(推荐:结构比较清晰): typedef NS_ENUM(NSInteger, Test1) { //以下是枚举成员 Test1A = 0, Test1B = 1, Test1C = 2, Test1D = 3 }; 枚举的定义还支持位运算的方式定义,如下: 等于号后面必须等于1 typedef NS_ENUM(NSInteger, Test) { TestA = 1, //1 1 1 TestB = 1 << 1, //2 2 10 转换成 10进制 2 TestC = 1 << 2, //4 3 100 转换成 10进制 4 TestD = 1 << 3, //8 4 1000 转换成 10进制 8 TestE = 1 << 4 //16 5 10000 转换成 10进制 16 };什么时候要用到这种方式呢? 那就是一个枚举变量可能要代表多个枚举值的时候. 其实给一个枚举变量赋予多个枚举值的时候,原理只是把各个枚举值加起来罢了. 当加起来以后,就获取了一个新的值,那么为了保证这个值的唯一性,这个时候就体现了位运算的重要作用. 位运算可以确保枚举值组合的唯一性. 因为位运算的计算方式是将二进制转换成十进制,也就是说,枚举值里面存取的是 计算后的十进制值. 打个比方: 通过上面的位运算方式设定好枚举以后,打印出来的枚举值分别是: 1 2 4 8 16 这5个数字,无论你如何组合在一起,也不会产生两个同样的数字. 手工的去创建位运算枚举,还有稍微有点花时间的,好在Apple已经为我们准备的uint.所以,用下面这种方式来初始化一个位运算枚举吧: typedef NS_ENUM(uint, Test) { TestA, TestB, TestC, TestD, TestE }; 多枚举值 赋值方式如下: Test tes = (TestA|TestB); 判断枚举变量是否包含某个固定的枚举值,使用前需要确保枚举值以及各个组合的唯一性: NSLog(@"%d %d %d %d %d",TestA,TestB,TestC,TestD,TestE); Test tes = (TestA|TestB); NSLog(@"%d",tes); NSLog(@"%d",(tes & TestA)); if ((tes & TestA)) { NSLog(@"有"); }else { NSLog(@"没有"); } NSLog(@"%d",(tes & TestB)); if ((tes & TestA)) { NSLog(@"有"); }else { NSLog(@"没有"); } NSLog(@"%d",(tes & TestC)); if ((tes & TestC)) { NSLog(@"有"); }else { NSLog(@"没有"); }如果 没有包含,将返回0, 0表示false NO 则进入else 也可以随时为枚举变量累加某个值,但是要自己控制不要添加已经加入过的枚举值, 枚举变量的值不会有变动,但这样将会误导阅读代码的人 tes |=TestC; 有累加,自然有累减了,如果累减不存在的枚举值, 那么本次累减的枚举值,会自动累加上去. tes^= TestE; 以上,差不多就介绍完了, 如果有什么疑问,欢迎提问.
其实 一般我在博客 针对控件这一块来说,是很少进行解说. 不过主要 TabBar 相对于其他控件的重要性要多得多,制作方面也要复杂一些. 为此,我专门写一篇教程,记录一下原理和思路.以供各路神仙参考! OK,进入正文: iOS 5以后 其实制作这类控件简单多了, 不需要自己去组织各个View 之间的切换. 第一点,原理: TabBar它讲白了 也是View,只是在一个View上面暂时只显示一个View 根据用户选按决定显示哪个View iOS5以后提供在ViewController内部直接填装其他子ViewController,代码如下: [self addChildViewController:viewController];这样写的好处是什么,为什么iOS5以后要这样添加呢? 参考下文,你就知道为什么: http://blog.devtang.com/blog/2012/02/06/new-methods-in-uiviewcontroller-of-ios5/ 那么针对系统下的UITabBarViewController 如何进行自定义呢? 默认的TabBarView高度是49 这里注意,不要尝试去修改这个高度以来展现更多的容器视图,总之这样怎么弄都弄不到你想要的效果. 原理方面是 将UITabBar 隐藏. 那么在 添加一个 自定义的UIView 到 UITabBarViewController的View上面 这样这个UIView 就随便你什么开发了. 另外值得一提的是,当你Push到下一个视图时,你想隐藏你自己自定义的视图. 此时纠结了. 那么在我自己经过无数次尝试以后,得带以下代码,配合系统默认的 hidesBottomBarWhenPushed 也算是实现了. 原理则是,默认addSubView 到 根视图中,当要切换Push到新的视图的时候.再把自定义视图 addSubView到TabBarView上面. 连带着一起过去. 一:声明这个变量在第一次加载界面时不会有错乱的动画出现 @interface FESBHomeViewController () { BOOL _isFirstShowCoopTabBar; } 二:在TabBarViewController 执行ViewDidLoad时 显示出自定义选项卡视图 - (void)viewDidLoad { [super viewDidLoad]; _isFirstShowCoopTabBar = YES; [self isHiddenTabBar:NO withViewController:nil]; } 三:核心切换自定义选项卡视图的代码 - (void)isHiddenTabBar:(BOOL)hidden withViewController:(UIViewController *)viewController { if (!self.tabBar.hidden == hidden) { return; } [coopTabBarView removeFromSuperview]; if (hidden) { [self.tabBar setHidden:NO]; [coopTabBarView setFrame:self.tabBar.bounds]; [self.tabBar addSubview:coopTabBarView]; if (viewController != nil) { viewController.hidesBottomBarWhenPushed = YES; } }else { [self.tabBar setHidden:YES]; [coopTabBarView setFrame:self.tabBar.frame]; int statusBarHeight = 0; if (_isFirstShowCoopTabBar) { _isFirstShowCoopTabBar = NO; statusBarHeight = 20; } if (self.navigationController.viewControllers == nil) { coopTabBarView.Help_top -=statusBarHeight; }else { coopTabBarView.Help_top -=statusBarHeight+44; } [self.view addSubview:coopTabBarView]; } }
没机会尝试,自己做练习吧,记录一下详细步骤吧,以备要用之需. 在这里我参考了这边文章,引荐一下 表示感谢! http://lizaochengwen.iteye.com/blog/1452076 我喜欢图文并茂,看起来比较有感觉!^^ 首先第一步是创建文件,创建步骤如图: 取名的话,必须使用: Localizable.strings 创建成功以后,接下来就是选择支持的语言了. 这里要注意的是”Chinese”要选择“zh_Hans”,这个是简体中文。“zh-Hant“ 是繁体中文 选择好支持的语言后,就可以开始对具体的语言文件进行编辑工作了, 编辑格式必须如下: "Key"="value"; /* Localizable.strings UICustomControl Created by 余书懿 on 12-9-17. Copyright (c) 2012年 珠海飞企. All rights reserved. */ "Test"="测试中"; 通过宏代码调用: NSLocalizedString(@"Test", @"这个字段只是一个注释") 通过Key来配对,应用会自己获取当前iOS 设置的使用语言 运行后即可看到效果 另外还有一个是修改应用的显示名称,此时要新建一个资源文件 名称必需叫做:infoPlist.strings 创建以后同样设置可以支持的语言,设置以后开始编辑如下代码: /* infoPlist.strings UICustomControl Created by 余书懿 on 12-9-17. Copyright (c) 2012年 珠海飞企. All rights reserved. */ CFBundleDisplayName="自定义控件"; 还有一点要注意的是 工程Plist文件中的 Bundle display name 要设置为: ${PRODUCT_NAME}. 那么应用在显示iPhone 桌面时 就一样会根据设置的语言而显示不同的语言了.
引言: ARC的全称是Automatic Reference Counting,中文翻译过来是:自动引用计数,是苹果在WWDC2011发布iOS5时随同一起的新特性.其用途是为了加强内容管理的便利性和稳定性.简而言之是为了取代MRC.并且ARC是编译时特性,它的性能和MRC不相上下,甚至效率更高,苹果建议所有的开发者都去尝试使用,提高生产效率.本文将对ARC进行一个全面的介绍. 参考资料: 1:手把手教你ARC——iOS/Mac开发ARC入门和使用 http://onevcat.com/2012/06/arc-hand-by-hand/ 2:ARC下dealloc过程及.cxx_destruct的探究 http://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/ 安装: 安装只需要一个步骤: 找到工程的设置项Objective-C Automatic Reference Counting,如下图所示,设置为YES即可 一般需求你手动开启ARC工程的项目,都是之前用MRC开发的.因为新的工程在建立时,默认都会建议你开启ARC模式. 当开启ARC以后,所有release,retain,等会被编译器提出,此时通过两种方式来对代码进行适配以兼容ARC模式 1:使用Xcode自带的ARC代码转换小工具 打开方式:Edit > Refactor > Convert to Objective-C ARC 在尝试执行转换工具时,我们可能会遇到一些问题,此时需要去手动修改一些源代码,否则无法完成转换: 前往issue面板,我们需要找出影响转换工具的代码,像下面这样: 像图中一样搜索一下 ARC Casting Rules 类型的错误, 因为这些都是需要我们手动去更正的. 产生这个错误的原因通常是因为我们将一个NSObject强行转换成 Core Services对象, 特别是Core Foundation(CF)方面的. 在MRC时代,因为手动管理内存,释不释放是开发者自己说了算,编译器不会关心这些,所以不会报错. 但进入ARC时代以后,编译器需要进一步的确定转换后的释放问题,这里需要用到一个独特的关键字: bridge ,通过 bridge 来转换类型. 好在强大的Xcode可以帮我们辅助的插入这些关键字,加快修改速度,就像下图这样: 修正完成这些问题以后,再次尝试运行转换工具,不出意外,就会出现一个转换后,预览结果,而我们呢,最好是再好好审阅一遍我们转换后的代码,看看有没有哪里不对了. 2:手动更改代码至兼容ARC模式. 3:在iOS项目中经常会用到第三方开源项目,强行将其源代码改为ARC是不明智的,吃力不讨好. 不过我们通过对工程进行一些设定,来让其MRC代码兼容ARC模式首先选择target,然后选择Build Phases标签,展开Compile Sources;在相关MRC代码的文件后面(Compile Flags)加入编译选项”-fno-objc-arc”如图: 反之,如果project是不基于ARC的,就需要对ARC的文件设置:"-fobjc-arc" 使用: 二:ARC记录一些零碎细节 1:为 NSMutableArray addObject 时 如果 设置 属性 是copy add时会crash 用strong 2:关于 dealloc 方法 (摘自ARC完全指南) 启用 ARC 之后,dealloc 方法在大部分时候都不再需要了,因为你不能调用实例对象的 release 方法,也不能调用[super dealloc]。假如原先的dealloc 方法只是释放这些对象,Xcode 就会把 dealloc 方法完全移除。你不再需要手动释放任何实例变量。 如果你的 dealloc 方法处理了其它资源(非内存)的释放,如定时器、CoreFoundation 对象,则你仍然需要在 dealloc 方法中进行手动释放,如 CFRelease(),free()等。这时 Xcode 会保留 dealloc 方法,但是移除所有的 release 和[superdealloc]调用。如下: - (void)dealloc { AudioServicesDisposeSystemSoundID(soundID); } 3:property 的修饰符总结如下: strong:等同于"retain",属性成为对象的拥有者 weak:属性是 weak pointer,当对象释放时会自动设置为 nil,记住 Outlet 应该使用 Weak unsafe_unretained:等同于之前的"assign",只有 iOS 4 才应该使用 copy:和之前的 copy 一样,复制一个对象并创建 strong 关联 assign:对象不能使用 assign,但原始类型(BOOL、int、float)仍然可以使用 总结:
引言: 有时候,在UITableView的顶部,也就是HeaderView上面会有一些附加功能,例如常见的搜索功能,或者是一些数据类型的筛选. 一般情况我们会把这个附加的功能视图放在UITableView的tableHeaderView上面. 此时你尝试把系统的UISearchBar设定为tableHeaderView时.UITableView尽然自动实现了一个隐藏显示的吸附功能. 这个小小的功能特性对于体验来说还是不错的.还有就是我们在初始化展示界面时,默认不想让用户看到附加功能. 可是,当我们把tableHeaderView设定为自定义的视图时. 这个吸附功能就自己消失了(这是多么让人失望的一件事情). 博主我尝试以自己的思路去完成这个功能. 那么就需要对这个吸附功能去分析它的实现原理. 在将tableHeaderView拖动到可见区域时,我需要调整UIScrollView的contentOffect或contentInset. 为此我尝试着添加观察者去监听这两个属性的变动情况以做出这个功能. 可是经过多次努力,最终失败了. 非常多的瑕疵,而需要应变的场景又变化多样. 其实博主对于此非常不甘心呐,但因为时间关系又无可奈何. 好了,上面讲了一堆还是没进入重点,现在呢,就是既能够使用自定义视图,又能够拥有那种有趣的吸附功能. 这里用一个成语来比喻一下我实现这个功能的方式:草船借箭(有别的更好的词,欢迎留言) 既然系统在UISearchBar上面已经把这个功能给实现了,我为什么不好好利用一下呢?
引言: Block是Apple在iOS4.0对Objective-C引入的新特性,通过Block可以增强代码的可读性和耦合性,减少非必要性的代理(Delegate)模式. Block是一个代码块,非常类似JavaScript里面的匿名函数,也可以叫做闭包,所以你也可以用匿名函数的方式来理解Block. 本文Block的运行环境是在ARC模式下进行,非ARC的Block内存管理方面还是较为繁琐.所以,有了ARC,开发者不必过多的去关注Block在内存支配方面的问题. 参考资料: 1:Block使用中的一些疑问解答 http://article.ityran.com/archives/1221 2:Block编程值得注意的那些事儿 http://www.cocoachina.com/macdev/cocoa/2013/0527/6285.html 3:iOS中block实现的探究 http://blog.csdn.net/jasonblog/article/details/7756763?reload 安装: 让你的项目支持iOS4以上即可 使用: Block的定义有两种方式如下: 第一种是匿名方式(做一次性使用,该Block不需要被重复使用时 使用): 第二种是通过typedef来定义一个新的Block(这样声明在头文件中,可以在其他类中重复使用,但必须在接口和实现外面声明引用): 以上两种定义Block的结构直接手动输入还是蛮难记的. 不过Xcode已经为我们添加了这两种定义的Code Snippet辅助编码 第一种的辅助输入:inlineBlock 第二种的辅助输入:typedefBlock 使用Block最大的一个好处就是可以在代码块中随时访问外部变量,比如你在A.class类中的某个方法中声明了一段代码块. 你可以在代码块中直接对A.class所拥有的成员变量进行调用,并且,通过一定的条件(__block),还可以随时的修改这些变量的值和指针. 下面看一段代码的实例: 1:通过图中可以看到,对没有通过__block修饰的局部变量进行赋值会修改指针地址,编译器会产生警告,提示这是无效的编码,正确的方式是为局部变量加上__block修饰. 2:在Block里面可以随时访问全局变量,静态变量等,并对它们的值和指针进行修改. 3:但在Block中直接使用所在声明区域的类的成员变量和self时也是可以直接使用和修改的,但需要注意循环引用. 在ARC环境下使用Block时的注意点: 开启ARC以后,Block的内存管理也交给了ARC,这让开发者不用再去关心何时需要引用,何时需要释放.如果要释放某个不再需要使用的Block成员变量,只需要将其设置nil即可. 但在这强大的环境下,我们的编码也任然需要谨慎,否则很容易产生循环引用. 在编码过程中,编译器可能会产生像下面这样的警告: Xcode会最大程度的提示你可能造成了循环引用. 产生这个警告的主要原因是我们在Block内使用了不是在Block里面声明的变量. 就像下面这样的代码: @interface KSViewController () { id _observer; } @end @implementation KSViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. KSTester * tester = [[KSTester alloc] init]; [tester run]; _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestNotificationKey" object:nil queue:nil usingBlock:^(NSNotification *n) { NSLog(@"%@", self); }]; } - (void)dealloc { if (_observer) { [[NSNotificationCenter defaultCenter] removeObserver:_observer]; } } 在上面代码中,我们添加向通知中心注册了一个观察者,然后在 dealloc 时解除该注册,一切看起来正常。但这里有两个问题: a) 在消息通知 block 中引用到了 self,在这里 self 对象被 block retain,而 _observer 又 retain 该 block的一份拷贝,通知中心又持有 _observer。因此只要 _observer 对象还没有被解除注册,block 就会一直被通知中心持有,从而 self 就不会被释放,其 dealloc 就不会被调用。而我们却又期望在 dealloc 中通过 removeObserver 来解除注册以消除通知中心对 _observer/block 的 retain。 b) 同时,_observer 是在 self 所在类中定义赋值,因此是被 self retain 的,这样就形成了循环引用。 如何解决循环引用: __weak KSViewController * wself = self; _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestNotificationKey" object:nil queue:nil usingBlock:^(NSNotification *n) { KSViewController * sself = wself; if (sself) { NSLog(@"%@", sself); } else { NSLog(@"<self> dealloc before we could run this code."); } }]; 下面来分析为什么该手法能够起作用。 首先,在 block 之前定义对 self 的一个弱引用 wself,因为是弱引用,所以当 self 被释放时 wself 会变为 nil;然后在 block 中引用该弱应用,考虑到多线程情况,通过使用强引用 sself 来引用该弱引用,这时如果 self 不为 nil 就会 retain self,以防止在后面的使用过程中 self 被释放;然后在之后的 block 块中使用该强引用 sself,注意在使用前要对 sself 进行了 nil 检测,因为多线程环境下在用弱引用 wself 对强引用 sself 赋值时,弱引用 wself 可能已经为 nil 了。 通过这种手法,block 就不会持有 self 的引用,从而打破了循环引用。总结: Block在语法方面如果初次接触会令开发者有些畏惧感,不过不要怕,要耐下性子好好记住相关语法,因为这是iOS开发者高手进阶的必备技术之一.
引言: HomeBrew是Mac平台下不可或缺的套件管理器,通过HomeBrew,可以全面武装我们的终端,让它变的强大起来. 安装: 首先进入Homebrew的官方网站找到它的终端安装命令,如下: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" Homebrew的安装包大概50MB左右.下载需要一段时间,执行以后不出意外,你会看到终端提示安装成功,如下图所示: 接下里我们可以运行Brew doctor 来看看 Brew在我们当前的系统环境下是否运行良好,有没有什么不兼容的问题: 博主我运气不错,没有任何问题 ^_^
引言: 静态库是一个(.a)为后缀的文件,它由Xcode编译生成.并且,库中只能存放代码,如果编译静态库的工程中有其他资源是无法一起编译到静态库中.静态库有点类似Windows中的Dll文件.做过Windows桌面应用的人应该很熟悉.静态库出现的主要目的是为了让代码可以在多个地方重用.此篇主要记录在iOS中使用静态库时遇到的问题汇总和解决方案. 如何创建一个静态库: 1:主工程关联静态库具体步骤 一:让静态库与项目工程关联起来 二:让项目工程写书写代码时,能够得带静态库中的头文件提示 三:为保证在修改静态库中的代码编译运行后能够每次都取用最新的静态库而进行如下设置(非必要设置) Full Path 需要选择 DerivedData 文件夹里面 具体编译后所生成的 静态库文件,这样可以保证每次都使用最新的静态库文件 四:Library Target -> Build Settings -> Build Active Architecture Only 此项在Debug 和Release 模式下 需要设置 为 NO 否则会出现 在使用库里面的类文件时 找不到的错误警告 二:解决静态库无法使用资源文件的方式: 首先,所有的资源文件都依旧依附在静态库中,然后对需要使用的工程传入资源文件的引用即可. 将需要使用的资源文件拖动到工程中以后出现如下图中的设置选项: 三:静态库使用其他静态库的资源时,可直接设置 Header Search Path 即可,前提保证主工程都引用了这些静态库 四:解决在静态库中 使用 Category(类别) 产生的Crash 在调用静态库的主工程中进行如下设置即可解决问题: Project->Build Settings->Other Linker Flags 添加值: -all_load 注:这种设置方法会让所有与主工程有关的静态库和框架都加载其中的Category. 如图: 参考文章: http://blog.csdn.net/leonpengweicn/article/details/6799994 Category 的使用参考: http://blog.csdn.net/sanpintian/article/details/7406180 http://www.54xue.com/w/70/n-30670.html 如果要在类别中保存变量,可以参考以下文章: http://www.cnblogs.com/liping13599168/archive/2012/09/13/2682664.html 解决讯飞语音框架导入后产生编译不通过的解决方案:(将静态库单一设置其加载Category). 参考帖子: http://dev.voicecloud.cn/bbs/forum.php?mod=viewthread&tid=4154&extra=&page=1 1.去掉-ObjC -all_load参数(这个参数会强制所有的静态链接库都加载其中的category);2.改为逐一加载Three20的各个静态库,即修改链接参数(Other Linker Flag)为如下形式:-force_load $(BUILT_PRODUCTS_DIR)/libThree20.a 如下图: 五:如果你将整个文件切换到了其他目录后,XCode编译时可能产生目录找不到的警告,如下图: 解决方法如下: 确定 Library Search Paths 的路径是否指向正确: 六: Unknown class MyClass in Interface Builder file. 的解决办法 设置 Other Linker Flag 的 -all_load 在使用静态库时,如果没有设置-all_load . 那么主工程在编译时是不会检测是否引用应该引用的框架(framework). 但是使用是没问题的,但是遇到需要框架才能执行的代码会崩溃,另外xib也会报出 找不到相应的类,除非在代码中主动调用. 七:静态库中的资源使用和管理的最佳解决方案. 使用Bundle捆绑包的方式来使用和管理资源. 步骤如下: 1:新建一个文件夹,将其命名为xxx.bundle. 以后 Mac自定识别到并标识为捆绑包. 2:将静态库使用的相关资源拷贝的bundle里面. 3:抒写代码的时候在路径方面有所改变,如下: [UIImage imageNamed:@"PullTableView.bundle/arrowhead_up.png"] 4:这一点是我的建议,将捆绑包还是放在静态库的目录里. 然后在拖动到主工程中时,不要选择复制到执行区域.如下图设置即可: 八:当一个项目里面有两个Target都需要使用同一个静态库时,应该这样操作才可以正常使用: 因为在Frameworks里面只会生成一个.a 文件. 所以直接在Target Membership直接勾选即可.
宏的定义: #define BookContentContainerViewCornerRadius 4.0 #define BookContentContainerViewCornerRadius @"yushuyi" 在宏里面同样可以执行代码: #define BookContentContainerViewCornerRadius 1+1 使用宏编写IF ELSE 语句语法: #if YES //Code #else //Code #endif 1:UI_USER_INTERFACE_IDIOM() 功能:判断运行当前应用的设备是iPhone 还是 iPad 返回: UIUserInterfaceIdiom 枚举值 2: TARGET_IPHONE_SIMULATOR 功能:判断当前运行环境是否是模拟器 返回:YES 表示 是模拟器, NO表示不是模拟器
1: CSS3 border-image的使用方法(来自:喵吱妹抖) http://www.wufangbo.com/css3-border-image/ 2: 22款给力的HTML5和CSS3帮助工具(来自:HTML5研究小组) http://www.mhtml5.com/2012/09/5310.html 3:在学习HTML5时,别忘了CSS技术 http://software.intel.com/zh-cn/blogs/2012/05/09/html5css/?cid=sw:prccsdn2241 4:国内HTML5前端开发框架汇总 http://software.intel.com/zh-cn/blogs/2012/12/03/html5/?cid=sw:prccsdn22995
1: 搜索指定文件夹内的 指定文件, 进行批量删除操作(摘自:mac 删除文件夹里所有的.svn文件) sudo find /Users/justfly/Documents/workspace/justSVN/ -name ".svn" -exec rm -r {} \; 2: 显示Mac隐藏文件的命令 defaults write com.apple.finder AppleShowAllFiles YES注:默认为NO,表示不显示,命令执行后需要重新启动Finder.3:利用 lipo 合并静态库文件 lipo -create /Users/amarishuyi/Desktop/map/Release-iphoneos/libbaidumapapi.a/Users/amarishuyi/Desktop/map/Release-iphonesimulator/libbaidumapapi.a -output/Users/amarishuyi/Desktop/map.a 注: 1:两个红色 一个绿色 ,请全部使用完整路径 2:两个红色 分别表示 真机版本的静态库 和模拟器版本的静态库 3:绿色表示合并后的产物 4:在终端中进入文件夹 cd Desktop 如果文件夹的名称中有空格,则需要加反斜杠来标识,例如: cd iPhone Developer 需要写成 cd iPhone\ Developer 如果需要返回上一层目录: cd ../ 5:统计代码行数 find . -name "*.m" -or -name "*.h" -or -name "*.xib" -or -name "*.c" -or -name "*.storyboard" |xargs wc -l注:需要进入(cd)到统计代码的目录参考: http://www.cnblogs.com/visen-0/archive/2013/02/18/2915147.html 6:查找指定目录下 静态库(.a)架构支持情况 find path -name "*.a" -exec lipo -info "{}" \;
---->ProjectName-info.plist 通过代码获取系统Pilst键值对: NSDictionary *dicAppInfo = [[NSBundle mainBundle] infoDictionary]; 1: Root > Status bar is initially hidden Boolean 决定了App启动时,初始化的状态栏是否隐藏 YES:隐藏状态栏 NO:显示状态栏 2: Root > Icon files (iOS 5) > Primary Icon > Icon already includes gloss effects Boolean YES:取消高亮光泽效果 NO:附加高亮效果 默认 3: 通过代码获取App 域名 ID (Bundle Identifer): [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"] 4:Application supports iTunes file sharingBoolean YES:让App 支持与iTunes 文件共享 NO :不让 默认 参考链接:http://www.rritw.com/a/caozuoxitong/OS/20130309/277825.html ---->ProjectName-Prefix.pch 预编译头文件 #ifdef __OBJC__ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #endif 注: 表示在编辑器编译时会自动为 所有的编译文件添加如上头文件的import 减少手动引入的麻烦,主要添加要经常使用的头文件. 上面如果添加了某个引入的话,那么在编写代码时 就不需要再引入同样的头文件了.
1:打开Mail NSString *recipients = @"mailto:ysy@flyrise.cn?subject=Hello from California!"; NSString *body = @"&body=It is raining in sunny California!"; NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body]; email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]]; 2:打电话 NSString *num = @"110"; //number为号码字符串 NSString *mobileNumber = [NSString stringWithFormat:@"telprompt://%@", num]; NSLog(@"call phone %@;", mobileNumber); [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mobileNumber]];注:上面代码触发后,系统会提示用户是否真的要打电话, 电话结束后,会返回至应用程序, 如果将telprompt修改为:tel 后,点击可直接拨打电话, 但电话结束后,不会返回至应用程序 2:打开Safari [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.baidu.com"]]; 3:打开Messages [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms://800888"]]; 4:打开App Store 某个应用的评价系统 NSLog(@"%@",[[[SystemGlobalInfo defaultInstance] deviceInfo] applicationId]); NSString *str = [NSString stringWithFormat: @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@",[[[SystemGlobalInfo defaultInstance] deviceInfo] applicationId]]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]]; 5:打开App Store 中的某个应用 途中经过Safari NSString *appID = @"291586600"; NSString *appUrl = [NSString stringWithFormat:@"http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=%@&amp;mt=8",appID]; NSURL *appStoreUrl = [NSURL URLWithString:appUrl]; [[UIApplication sharedApplication] openURL:appStoreUrl]; 6:打开App Store 中的某个应用 直接跳转 NSString *urlString = @"http://itunes.apple.com/us/app/ye-wu-xie-zuo-ping-tai/id507704613?mt=8&uo=4"; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]] ; 7:打开谷歌Maps进行搜索 NSString* searchQuery = @"珠海"; searchQuery = [searchQuery stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", searchQuery]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlString]]; 8:利用OpenUrl打开第三方应用程序本文释权了打开的方式,很详细.. 地址如下: http://blog.cnrainbird.com/index.php/2012/06/04/tong_guo_openurl_qi_dong_di_san_fang_app_bing_chuan_can_shu/ 这里简单描述一下注意点, 1: A工程 需要打开 B 工程 那么B工程需要在plist文件中 进行UrlType的注册 2:在启动其他第三方应用程序之前,可以通过如下代码判断,应用程序是否已经安装在iPhone中. NSURL *url = [NSURL URLWithString:@"AppMessageDemo:11"]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; }else { [ShareCode Msg:@"没安装"]; } 3: B工程被打开时,请使用如下委托处理打开的消息 -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { NSLog(@"%@",[url path]); NSLog(@"%@",sourceApplication); NSLog(@"%@",annotation); return NO; } 下面这个委托,也就是微文中提到的委托,已经被苹果弃用 -(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { NSLog(@"123"); return [WXApi handleOpenURL:url delegate:self]; } 注: sourceApplication 表示 App plist文件中标明的 Bundle identifier 至于返回YES,还是返回NO,似乎没有发生任何事情,待继续考证.9:为应用设置首选项功能 创建:
引言: 苹果为广大的开发者提供了一个很好的应用生态环境 参考资料: 1:如何向App Store提交应用 http://www.cocoachina.com/newbie/tutorial/2013/0508/6155.html 2:App Store提交上线和市场推广专题 http://www.cocoachina.com/special/market.html 3: App Store Review Guidelines https://developer.apple.com/appstore/resources/approval/guidelines.html#functionality 苹果App Store审核指南中文翻译(2014.9.1更新) http://www.cocoachina.com/appstore/20140901/9500.html#0-tsina-1-5518-397232819ff9a47a7b7e80a40613cfe1 4:iTunes Connect 开发者上手经验 http://www.cnblogs.com/zhw511006/archive/2013/01/15/2860945.html 5:傻瓜式IOS发布教程(一)iTunes Connect创建应用以及内购 http://blog.csdn.net/mrdo_home/article/details/10286633 6:iOS开发者账号申请流程详解: http://girlios.github.io/blog/2014/03/16/enrolling-in-apple-developer-programs/ 7:iOS内购实现及测试Check List http://onevcat.com/2013/11/ios-iap-checklist/ 8:应用被拒10大理由 https://developer.apple.com/app-store/review/rejections/ 9:iOS证书说明和发布内购流程整理 http://www.cocoachina.com/ios/20150521/11889.html 发布App: 手动编包发布: 发布App有两种方式,一种是手动方式,手动编包,再利用Application Loader 上传应用 1:将编译模式设置release 2:使用发布版本的证书: 自动编包发布: 1:编译环境设置为iOS Device 2:点击Archive 了解编译指令集: http://wangzz.github.io/blog/2014/05/09/xcodeshe-zhi-xiang-zhi-architectureshe-valid-architectures/ 在iTunes Connect 中创建一个新App或提交一个更新App的申请: Developer -> Member Center -> iTunes Connect -> Manage Your Applications -> Add New App 1:开始创建一个App 提供App名称等等 SKU Number 的意思是区分你的app用的,可以填app的BundleIdentifier,或者能唯一标识你的app的字符都可以。但是注意,SKU Number在app发布后就不能更改了。 2:接下设置可用日期,价格等等 一般是设置一下价格就可以确定了. 3:接下来要准备不少东西: 1:icon 需要 1024 * 1024 尺寸的 图标 2:5张软件使用截图 最后开始编译打包App,使用 Application Loader 上传我们的App. 可能遇到的错误: 1:icon图标设置问题 解决办法: 检测info.plist文件的icon设置项是否有多余的: 2:是否是报纸,杂志类应用程序? 解决办法: 如果你的应用不包含这项功能那么删除掉info.plist的设置就可以了,如下图: 如果是的话: http://hi.baidu.com/yanh105/item/d9b0dbc1fee3cd2aee4665bd 这篇教程教了怎么制作 当我们正式准备提交应用时,苹果会问如下图中两个问题: 1:第一个问题是问你的代码中是否用到了加密, 2:第二个是问你的应用有没有涉及到侵权问题. 一般情况,是两个都选择NO. 途中碰到一个非常炙手的问题,存档编译的App,每次都编译失败,生成一个 名字叫:Generic Xcode Archive 的玩意,这玩意目前不知道 是做什么用的, 网上的解决办法是:将其他静态库中的 Build Settings 中的 Skip install 更改为 YES, 主工程的 Skip install 依旧保持 为 NO. 就解决问题了, 但纠结的是,我这样设置以后,依旧编译错误, 原来还需要将静态库中的 所有头文件,归置到Project 之中. 再次编译,成功! 如图: Date:2012-08-06 16:27 关于Xcode 4.3x 版本提交审核失败的解决办法:将工程中 Build Settings 的 Compress png files 设置为NO. (默认YES) 编译后的大小和原来一样,尝试再次提交.成功! 资料参考: 1:ipa中提取图片资源 png处理方法 http://blog.csdn.net/stonexing5/article/details/7429422 2:用xcode4.3.2中的organizer顺利发布成功 http://blog.csdn.net/kingkong1024/article/details/7483606 3:由Corrupt Icon造成的Invalid Binary http://hi.baidu.com/wwssttt/item/a74136506ead3adbd48bacd7 Date: 2012-08-14 11:28 今天附上两个链接 Application Loader 下载地址: https://itunesconnect.apple.com/apploader/ApplicationLoader_2.8.dmg Application Loader 图解教程 http://www.cocoachina.com/newbie/basic/2010/0726/1927.html Date: 2013-02-26 10:33 应用在App Store 的详情地址: https://itunes.apple.com/cn/app/id604608273?mt=8 只需要更换iD即可 Date:2013-08-13 13:52 审核被拒绝了,触犯了2.23,如下: 2.23: Apps must follow the iOS Data Storage Guidelines or they will be rejected 理解和遵循苹果的iOS数据存储指南 https://developer.apple.com/icloud/documentation/data-storage/ 参考以下两则: http://www.cnblogs.com/wellsoho/archive/2012/10/16/2725718.html Date:2013-11-13 编译不通过,提示LibPods.a 找不到. 请尝试单独编译Pod的工程
引言: 推送通知是移动终端保持永远在线概念的一个核心方式,当人们离开桌面互联网以后想在第一时间收到与之相关的信息时.推送通知的出现就再好不过了. 但是,要注意不可滥用,繁多的非必要性推送消息会给用户造成非常烦躁的心理.作为用户体验的一部分,那就已经在往失败的方向走了. 推送通知共为两种类型,分别是本地推送通知和远程推送通知. 本文将对这两类通知的使用展开详细讨论. 参考资料: 1: ios本地通知和远程通知 http://wangjun.easymorse.com/?p=1482 2: 苹果远程通知服务申请激活例图 (外国佬写的.) http://mobiforge.com/developing/story/programming-apple-push-notification-services 3:书籍参考:iPhone 开发秘籍 第16章 推送通知. 使用: 首先是申请证书的网址 https://developer.apple.com/ios/manage/overview/index.action 登录成功以后,进入iOS 配置管理的 主页面. 第一步操作是去 创建一个新的App IDs创建成功后,会需要提供安全证书来激动推送服务,如下图: 选择存储到磁盘以后,生成一个文件名称为(简称CSR): CertificateSigningRequest.certSigningRequest 回到Apple页面 将这个提交并提示激动成功. 激活成功后的App IDs 提供下载 开发版或是发布版的主动推送证书(aps_development.cer),如果需要做服务器方面的主动推送的话,就必须要下载这个文件来使用推送服务了. 第二步要为App提供接受推送许可的证书,点击Provisioning进行设置,添加一个新的许可,选择刚刚新创建的App IDs. 再选择可以调试的iPhone 设备. 最后,同样是下载下来: YsyPushMessageDemo.mobileprovision双击该证书,让其加载一次. 接下来,进入iOS工程,选择使用该证书来调试. 红圈中,全部设置刚刚加载的许可证书. 那么到这里,关于证书类的 准备工作,已经全部准备就绪. 在这里再此强调一次,每个文件的具体作用 1: CertificateSigningRequest.certSigningRequest : 为生成App IDs 而用 2: aps_development.cer 为开发主动推送服务而用到的证书 3: YsyPushMessageDemo.mobileprovision 为App 接受推送通知的许可服务 主动推送的Push 代码 及使用,请参考一开始介绍的第一篇博客 这里只附上下载地址: https://github.com/stefanhafeneger/PushMeBaby 接下来,说说收到推送通知代码方面. 1:申请本App需要接受来自服务商提供推送消息, [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; 2:申请发出后,如果成功,委托会自动返回一个设备令牌(toKen),如果失败,将会进入另外一个失败的委托 //远程通知注册成功委托 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { NSLog(@"%@",deviceToken); self.viewController.toKenValueTextView.text = [NSString stringWithFormat:@"%@",deviceToken]; self.viewController.pushStatusLabel.text = @"已经注册."; } //远程通知注册失败委托 -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { self.viewController.toKenValueTextView.text = [error description]; } 3:将设备令牌码提供给服务商,以供推送到具体的手机上面. 如果远程推送消息来了,用户点击了推送消息,或者应用已经处于打开状态,系统都会自动调用以下委托: //点击某条远程通知时调用的委托 如果界面处于打开状态,那么此委托会直接响应 -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"远程通知"); [self PMD_uesPushMessage:userInfo]; } 4: 第三点里面的介绍的情况是应用程序已经处于运行状态,上面的委托才会被执行,如果应用程序处于未启用状态,此时又需要响应消息,那么需要以下委托处理. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //这里处理应用程序如果没有启动,但是是通过通知消息打开的,此时可以获取到消息. if (launchOptions != nil) { NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; [self PMD_uesPushMessage:userInfo]; } return YES; } 5:清空通知中心已有的推送消息,只需要将指定App 的 Badge 设置为 0即可 [[UIApplication sharedApplication ] setApplicationIconBadgeNumber:0]; 6:主动推送的字符串必须符合如下Json数组的格式,才能正确推送到手机当中. @"{ //自定义参数 \"userinfo\": { \"name\":\"remote notice\" }, //标准写法 \"aps\": { \"alert\": { \"action-loc-key\":\"Open\",//支持多语言 \"body\":\"messgae content\"//消息正文 }, \"badge\":1,//为App 的icon 标记 具体数值 \"sound\":\"default\" //播放的音频文件,default 表示系统默认的选择列铃声 } }";
--------------------------------------------------------------------------------UIStoryboardSegue-------------------------------------------------------------------------------- 1: 使用UIStoryboardSegue跳转时触发 此方法优先与下一个ViewController 的 viewDidLoad 方法 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { } 2: 在UIStoryboardSegue中存放原始的ViewController 和 目的地的ViewController 的引用, 转换类型后,可以直接对其操作 [(UIViewController *)segue.sourceViewController setTitle:@"嘿"];//原始 [(DrawViewController *)segue.destinationViewController setDrawTypeString:segue.identifier];//目的地 --------------------------------------------------------------------------------UIStoryboardSegue----------------------------------------------------------------------------------------------------------------------------------------------------------------UIStoryboard-------------------------------------------------------------------------------- 1: 根据 Identifier 获取 指定 Identifier 的实例 AboutViewController *about = [storyboard instantiateViewControllerWithIdentifier:ABOUT_IDENTIFIER]; 注:调用时如果没有找到,App 将直接Crash 2: 获取StoryBoard 默认的第一个实例 [storyboard instantiateInitialViewController]; 3:在AppDelegate 中手动初始化 某个StoryBoard self.window = [[iConsoleWindow alloc] init]; self.window.rootViewController = [[UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil] instantiateInitialViewController]; 4:从普通XIB中跳转到StoryBoard UIStoryboard *stryBoard=[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; self.view.window.rootViewController=[stryBoard instantiateInitialViewController]; XCode 对 StoryBoard 监听到的警告解析 --------------------------------------------------------------------------------UIStoryboard--------------------------------------------------------------------------------
1:初始化 NSURLConnection [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url] delegate:self]; 2: 在 didReceiveResponse 委托中获取服务器返回过来的信息 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSDictionary *responseHeaders = [httpResponse allHeaderFields]; NSLog(@"didReceiveResponseHeaders = %@",responseHeaders); } 3:在任何时候都可以将connection 撤销对网络的请求和访问 [connection cancel]; 4:网络连接成功后会开始下载, 会定时调用这个委托. 里面提供每一段的下载数据 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSLog(@"定时会有新的数据进入这个委托"); }
Xcode Error Solutions ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1: Xcode component installation Error-> An unknown error occurred. See theinstall log for more details. Just double click Xcode 4.3.1 dmg file ... or Just go to the Applications folder and right click on Xcode. Then window will show Xcode app. Right Cick App Show Package contents Then go to /Contents/Resources/Packages/ Install That MobileDevice.pkg ... After installation Double click on Xcode app ... Just See Xcode is ready ... https://discussions.apple.com/thread/3863980?start=0&tstart=0 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Xcode Building Error Solutions ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1:clang failed with exit code 1 一:检测所属工程文件中,所有的引用文件是否都已经正常引用了. 没有引用,请正确引用. 再次尝试编译 2:clang failed with exit code 254 一:检测代码中 是否 有 NSLog 打印了 返回 void 的值. 3:Verify exit code of build task with internal identifier 'CopyPNGFile 123.png' 一:将出错的png,用PhotoShop重新转换一次, 如果PhotoShop打不开,改后缀为Jpg 试试. 转换时,请使用 :存储为Web或设备所使用的格式格式转换成 PNG-24这样的图片大小比较合适 4: 一:确定静态库中是否有自定义的类文件,如果一个也没有,就会出现这种错误,这也是为什么新建的静态库都包含一个默认的类. 5: _OBJC_CLASS_$_UIMainKpiXML", referenced from: 1:检测类文件是否已经指定了Project Target 2:检测类文件是否在Bulid Phases 中的 Compile Source 是否包含了这个类文件 以上两步都检查完成以后,如果编译还报错误,请尝试彻底关闭XCode 再次编译试试. 6: for architecture armv7s 以下摘自: http://stackoverflow.com/questions/12570116/what-is-the-difference-between-arm7-and-arm7s Yes you are right about armv7s is about the iPhone 5. Here some summary info I found on the web: ARMv6 ISA (used by the ARM11 core in the iPhone 2G and iPhone 3G) ARMv7 (used by modern ARM cores, iPhone 3GS, iPhone 4 and 4S) ARMv7s (new A6 SoC for iPhone 5). 注:错误含义表示 指定的framework 不支持对 armv7s 的支持, 也就不支持搭载A6处理器的iPhone 5. 如果在编译framework或者静态库的工程中依旧编译时,可能是以下设置导致,设置为NO即可 7: Local declaration of '' hides instance variable 1:私有变量与属性变量同名所致 8:Instance variable '' accessed in class method 1:在静态方法不能使用到类的属性变量,否则就报上面的错误 9:ld: symbol(s) not found for architecture i386 1:里面意思说:"_stroyboard" 这个属性在目标类中 根本就没声明! 那就声明一下咯? 注:XCode4.5 会默认声明了,但是只是针对自定义类,系统类还没有. 所以,小心 @synthesize storyboard; 10:PerformSelector may cause a leak because its selector is unknown 通过如下代码解决产生的编译器警告 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:nextView]; #pragma clang diagnostic pop 来源:(http://www.ooso.net/archives/620) 11:unable to open executable 1:检测同一个静态库或工程中是否有两个或以上的想同类文件存在 2:删除模拟器中的应用,删除DerivedData文件夹 重新启动XCode. 12: Property's synthesized getter follows Cocoa naming convention for returning 'owned' objects 不要在头文件声明变量命名是以new copy开头 参考:http://kongbei888.blog.163.com/blog/static/24326613201261902510652/ 13:ld: file not found: 1:指向的静态库没有找到 14: _utf8_countTrailBytes add library libicucore.dylib 15:Stray "@" in program 工程使用的编译器版本过低所致. 修改编译器版本至最新版本,如下图: 参考:http://stackoverflow.com/questions/12821938/stray-in-program-with-nsdictionary-definition Build Url 1: exit code 254错误解决方法 iOS 5 - Apple LLVM compiler 3.0 error http://www.cnblogs.com/mfryf/archive/2012/02/26/2369190.html ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Objective-C Crash Solutions ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1:不要让0参与除法 Crash异常错误如下: 2012-07-10 13:31:59.471 FeOAClient[2155:15b03] *** Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan 3]' *** First throw call stack: (0x3889022 0x3a1acd6 0x3831a48 0x38319b9 0xc94c0d 0xc8af55 0xc8aa0a 0x10f1fb8 0xfce8a 0x10f89d4 0x10f8b11 0xfcb6c 0x37ef4ed 0x37ef407 0x381a46a 0xc09db 0x37ef4ed 0x37ef407 0x388ae42 0xd869df 0x385d94f 0x37c0b43 0x37c0424 0x37bfd84 0x37bfc9b 0x40a57d8 0x40a588a 0x10be626 0x350d 0x2485) terminate called throwing an exception(lldb) 解决办法: 检查代码 是否有0 参与的除法的计算,主要常见原因: 以下摘自:Minroad 1.除以0 2.sizeWithFont的字符串为nil 3.数学函数不正确运算 解决方法除了排除根源所在之外,用函数isnan()也是不错的选择(至少在没有彻底解决以前) 如下: float _x = NAN; if (!isnan(_x)) { cell.imgView.frame = CGRectMake(_x, 8, 10, 12); } 2: Could not instantiate class named NSLayoutConstraint (Could not instantiate class named NSLayoutConstraint ) 由于iOS 6 提供XIB 自动布局功能,那么在iOS 5上面使用肯定是不行的. 那么为了兼容iOS 5,目前只有将iOS 6的自动布局去掉先吧 再最右边的 inspector 一栏 将 interface builder document下的use autolayout 复选框去掉就可以了 如图:
1:StoryBoard下会自动为UIWindow 初始化,那么此时需要使用自定义UIWindow时,请进行如下编写: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[iConsoleWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.rootViewController = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil] instantiateInitialViewController]; return YES; } 1:Window层级 2:谁来当keyWindow? 3:Airplay直接投影Window 4:Window上的View横竖屏显示兼容 http://stackoverflow.com/questions/2508630/orientation-in-a-uiview-added-to-a-uiwindow
1:在iOS 5开始,新增的API可以直接定制个性化的 UISegmentedControl ,方式如下: 首先要知道,它的高度只能是:44.而且还改变不了,那么开发者需要告诉设计师提供的图片资源高度 需要 44高度,高清就是88. 另外设计师提供的44像素中的顶部和底部不要包含暗淡的像素存在,否则,中间的线条就会被迫突显出来,如下图: 2:利用iOS5 为 UISegmentedControl 设置按钮里面的字体颜色大小等 [self setTitleTextAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [UIColor Help_colorWithRGB:@[@210.0,@210.0,@210.0] alpha:1], UITextAttributeTextColor, [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8],UITextAttributeTextShadowColor, [NSValue valueWithUIOffset:UIOffsetMake(0, -1)],UITextAttributeTextShadowOffset, [UIFont fontWithName:@"Arial-Bold" size:0.0],UITextAttributeFont,nil] forState:UIControlStateNormal]; 注:可以设置以下四种值: UITextAttributeFont :字体格式 UITextAttributeTextColor:字体颜色 UITextAttributeTextShadowColor:字体阴影颜色 UITextAttributeTextShadowOffset:字体阴影偏移量 3:记录 UISegmentedControl 分段的数量 NSLog(@"%d",self.numberOfSegments); 4:通过遍历子视图的方式获取 分段的每一个View [self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"%@",NSStringFromClass([obj class])); [obj setTag:idx + 5]; }]; 注:再循环的时候 为每一个View 添加tag 值,那么接下来就可以通过标记来取到指定的View 分段控件在遍历所有子视图的时候是倒着来的,所以,想要按照正常思路取到指定的View需要如下写代码: -(UIView *)Parent_segmentViewWithIndex:(NSInteger)index { if (index > self.numberOfSegments -1) { return nil; } return [self viewWithTag:self.numberOfSegments - 1 - index + 5]; } 上面代码中的 5 只是为了对应 设置时的 5. 没什么实际用处, 说明一点就是:Tag值 不要从 0 开始 就行了. 还有就是 iOS 5.0 的时候 分段控件内部视图的每一个View的大小 只有在ViewDidAppear 调用时,才计算出了 具体大小. 而iOS 6.0以后 在ViewDidLoad时 就可以直接获取了.
就在今天,终于把两种主流的压缩格式给搞定了.. 途中真的碰到不少坎坷,百度谷歌了无数篇,不过还是要谢谢网上的牛人啊.. 在此,做一些记录,以及贡献一些资源. 给那些需要在IOS下 需要进行解压相关需求的孩纸一些帮助. 在开始之前,先列举一下对我有帮助的链接.谢谢这些博主了! 1:rar解压缩的源代码出处: https://github.com/ararog/Unrar4iOS 2:这个帮我解决了rar解压缩无法生成目录功能缺失以及乱码问题: http://stackoverflow.com/questions/7785659/is-there-an-unrar-library-out-there-for-ios 3:zip解压缩的源代码出处: http://code.google.com/p/ziparchive/ 4:zip解压缩教程 http://blog.sina.com.cn/s/blog_833996210100udkl.html 针对这两种主流格式我专门制作了一个Demo 并对解压缩的源代码都进行了修改,保证了无BUG.. 本Demo演示了Zip和Rar 解压缩的效果,我把他们都解压到了Documents目录里面. 点击按钮后你们可以自己去Documents目录里面查看解压的结果. 并且,Zip和Rar 的压缩源代码 我都已经打包成了framework以供日后使用方便. 那么在示例中,我也是直接使用framework来实现压缩的相关功能.(framework的制作请点击此处) 具体效果还是下载 Demo 以后自己多去尝试尝试吧! Demo下载地址:猛击此处. 以下有一点需要注意: 1:在使用Zip 的framework时 需要为我们的工程先引入一个 libz.dylib 文件. 不然编译时无法通过.(很蛋疼~,具体为什么不知道. 你知道的话在评论解释一下,谢谢了) 2:保证你的整个工程是以C++混编模式进行编译 3:Rar 的framework 无需引入文件,导入框架后,可以直接使用. 最后展示一下Demo的效果图: Date:2012-09-26 16:27 iPhone 5 搭载 A6处理器, 使用了Armv7s 模式编译,. 那么我之前提供的解压缩Demo 不支持Armv7s 所以会导致XCode 调试时无法烧进真机. 两种解决办法: 1:删除对工程对Armv7s 的编译支持.操作如下图: 2:重新编译不支持Armv7s的Framework. 以来支持A6处理器. 在重新编译rar框架 时,要特别, 目录链中的文件夹名称不能有空格,否则会编译失败,这估计是XCode 的BUG. 那么在此呢,我上传我重新编译后的压缩Demo
这篇文章不知道会不会很长,总之废话就先不多说了.以后再补充一些概念的东西上去. 直接进入正题: 首先我是百度了一下 framework 方面的资料,网上有不少教程. 其中有一个非常详细,地址如下: http://blog.csdn.net/proteas/article/details/6642364 我大致浏览了一下,步骤真的非常非常多而繁琐. 不过上文结尾处,作者提到一个框架模版的东东.感觉这个要轻松许多..所以,本文的目的就针对框架模块做一个步骤记录. 先附上框架模版的下载地址: https://github.com/kstenerud/iOS-Universal-Framework 于是我又开始继续百度关于框架模块的使用介绍相关(但确实译文,内容阅读性太差了),还好有牛人帮助,让我这种英语文盲少吃不少苦头! http://blog.csdn.net/kmyhy/article/details/7369354 当下载成功后,会发现有两个文件目录,分别是: 第一个是假框架 第二个是真框架. 我这里以真框架来开展制作步骤: 第一步是安装, 进入真框架目录 找到 文件: install.sh 右键用终端.app 打开. 接下来的一堆的英文别吓着了, 第一步这个脚本会去寻找你的XCode所在的具体位置,找到以后,会询问你,是不是这个目录? Where is Xcode installed? (CTRL-C to abort) [ /Applications/Xcode.app/Contents/Developer ]: 第二步就问你是不是要正式开始安装,你输入"Y" 然后回车. The templates will be installed in /Users/amarishuyi/Library/Developer/Xcode/Templates/Framework & Library continue [y/N]: y 第三步就在询问的计算机密码了,不然是不让安装的. 密码校验成功后. 英文的末尾出现: [ Installation complete. Please restart Xcode. ] 所以第四步是:重启XCode. 打开Xcode, New - > Project,然后出现下图: 当我正准备把完整的教程写完时,我又意外搜索到了某个牛人已经写了关于"真"框架的教程 http://www.itivy.com/iphone/archive/2012/4/1/634689026349024044.html http://blog.csdn.net/kmyhy/article/details/7419222 不如直接粘贴上来. 那么本文也就提前结束了.. Date:2012-07-18 02:38 本来是结束了的,今天自己摸索着 尝试制作了一款真框架版本的framework,觉得还是有几点想记录一下,以免下次制作时浪费时间. 1:至于是Debug版本的framework 还是Release版本的framework 选用哪个都可以. 2:请将需要放置的代码文件拖动至工程的根目录中,如下图所示(图中列出需要注意的地方): Date:2012-09-26 11:19 新的XCode 安装以后,之前安装好的框架模版,需要重新安装,不然会出现下面的错误 target specifies product type 'com.apple.product-type.framework.static', but there's no such product type for the 'iphoneos' platform
1:iTunes iPhone软件下载地址: /Users/amarishuyi/Library/iTunes/iPhone Software Updates/ 2:iTunes iPhone备份地址: ~/Library/Application Support/MobileSync 3:取消iPhone 连接后打开iTunes时自动备份功能. 4:利用iTunes 更换 音乐插图 右键歌曲->显示简介->插图: 5:取消连接iPhone设备时的 iTunes自动启动 6:往 iTunes 添加 资料时,阻止拷贝到指定目录:
引言: NSLogger是一款专门负责输出日志信息的工具.其强大程度让我不得不佩服该作者,针对引言方面,有一篇博文很好诠释了其通途. NSLogger是给高手用的,开发一些简单的Objective-C程序很难发挥其效用. 参考资料: 1:使用NSLogger代理NSLog输出日志信息 http://ishalou.com/blog/2012/10/20/how-to-use-nslogger/ 2: 安装: Github托管地址:https://github.com/fpillet/NSLogger 首先的安装方式是CocoaPods NSLogger分为两个部分: 一个是iOS类文件(3个),也就是需要我们放入到自己工程中的文件. 如果是手动引入这三个类文件的话,需要再分别引入 CFNetwork.framework 和 SystemConfiguration.framework 后即可 另一个是Mac应用 NSLogger for Mac ,所有日志输出信息都会在这个Mac应用监听和打印出来. 使用: 前往GitHub下载代码以后的目录结构如下: 主要关心的就是这两个目录里面的东西. Desktop Viewer 里面放在 NSLogger for Mac 的源代码. Client Logger 里面放着需要集成到工程中的类.