Swift开发iOS应用过程中的问题和解决记录

file

虚拟机里安装OSX+XCode开发环境

用真机的请直接跳过这个部分。

主要是在VitrualBox里安装mac系统和xcode,参考这篇教程,VirtualBox的版本是4.3.18 r96156,OSX版本是10.11 El Capitan,XCode版本是7.1 (7B91b)。

经过几天的初步使用,感觉用虚拟机开发iOS基本能够满足要求,i5/8g/ssd的配置目测能达到真机70%的开发效率。主要存在的问题是:1)在宽屏上无法满屏,两边会留有黑框,导致鼠标无法顺利停靠在两侧 2)键盘敲击频率过快(约5Hz)时字符乱序出现在编辑器里,例如快速输入“update”可能会得到“updaet” 3)虚拟机里的时钟有可能不准,开发过程中偶尔有问题与此相关,需要考虑在内。

调整分辨率

以管理员方式启动virtualbox和cmd命令行(重要!),执行下面的命令

cd "C:\Program Files\Oracle\VirtualBox"

VBoxManage setextradata "OSXElCapitan" VBoxInternal2/EfiGopMode 3 (注意,参数值 0=640×480, 1=800×600, 2=1024×768, 3=1280×1024, 4=1440×900, 5=1920×1200)

VBoxManage setextradata "OSXElCapitan" "CustomVideoMode1" "1360x768x32" (必须8的整数倍,我这里没有成功)

参考链接:How to Guide for Mavericks VM on Mavericks

通过Virtualbox的远程桌面连接到虚拟机

如果是NAT方式,目标地址是127.0.0.2,用guest里通过ifconfig查看到的10.0.2.xx是不行的。 远程桌面的方式比直接使用虚拟机卡不少,作为开发使用并不实用。

修饰键

苹果键盘与Windows键盘几个修饰键的对应关系如下(在设置里可以改,这里是缺省情况):

Command键(花键 ⌘):对应windows键

Control键(⌃):对应ctrl键

Shift键(⇧):对应shift键

Option键(⌥):对应alt键

两种键盘上这几个修饰键物理布局最大的区别,在于Command键与Option键位置相反,可以去“偏好设置->键盘”里改过来,这样切换两种键盘时就比较容易适应了。

OSX

常用快捷键

官方文档

跳到行首:“Command+左键”(Home键是跳到页首)

结束任务:Command+Option+Esc

XCode7快捷键

代码辅助:control+.

打开/关闭左侧Navigator边栏:Command+0(数字0)

打开/关闭右侧Utilities边栏:Option+Command+0(数字0)

快速打开文件(Open Quickly):Shift+Command+O(字母O)

转到定义(Go to Definition):Command+鼠标点击,或Option+Command+J

自动格式化代码:Control+I(需要先全选,否则只格式化当前行)

调整模拟器大小:Command+3(50%)

Swift2

一些教程

官网:swift.org

官方文档:The Swift Programming Language, 中文版

斯坦福免费视频公开课:Developing iOS 8 Apps with Swift中文翻译

国内视频教程:精通iOS移动开发(Xcode7&Swift2)(免费,内容比较初级)

cocoachina专题:从今天开始学习Swift(大量资源链接)

常量

多数项目里都需要一些常量,java里通常定义在interface里,用swift时可以定义在一个struct里,作为静态的存储成员(static let)。

类型转换

Double转Int: Int(myDouble)

保留小数点位数

方法1(3.1415926->3.14, 3->3.00):

let i = 3.1415926
let str = NSString(format:"%.2f",i)
print("\\(str)")  //will output 3.14

方法2(3.1415926->3.14, 3->3):

let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.DecimalStyle
nf.maximumFractionDigits = 2
print("\\(nf.stringFromNumber(3.1415926))") //will output 3.14

时间

NSDate:相当于java.util.Date,获得系统当前时间直接用NSDate()

NSTimeInterval:等同于double,NSDate.timeIntervalSince1970可以得到自1970年的秒数(注意不是java里的毫秒数)

NSDateFormatter:用来帮助在String与NSDate类型之间做转换 参考

官方文档页

嵌套struct

注意一个地方,例如struct A里定义了struct B,实例化A时,必须先执行a.property1 = value1以后再执行b.a=a,否则会发现b.a.property1=nil。

UIView

所有控件的基类。

frame与bound的区别:frame是view的边界,bound是view可以绘制的区域(bound是可以超出frame范围的)。参考链接1 参考链接2

UITableView

基本使用:可以用ViewController里放TableView,也可以直接用TableViewController,参考How to make a simple tableview with iOS 8 and Swift

使用TableViewController时显示Activity Indicator View(菊花)有问题,不太完美的解决方案见:UITableView Activity Indicator the Apple way,或使用第三方实现,例如下面会提到的SVProgressHUD

处理cell点击事件,实现tableView:didSelectRowAtIndexPath方法即可,注意看清楚别实现成tableView:didDeselectRowAtIndexPath。 参考链接

点击cell跳转(假设从A跳到B):在storyboard里,直接从A的cell到B拖拽创建一个show类型的segue并指定此segue的identity,在A代码里实现forSegue方法将要传的参数赋值给B即可。

UICollectionView

相当于Android里的GridView,可以显示多列数据,只是没有cell的默认实现,也就是必须使用自定义cell。

要让每个cell的宽度恰好等于collection view的一半,可通过实现下面的方法实现(参考链接):

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
  return CGSize(width: CGFloat(self.view.frame.size.width / 2), height: self.view.frame.size.height / 3) 
}

AutoLayout

假设有一个长宽都是150的image view,里面要加载一个网络图片(尺寸未知),要求网络图片能够完整显示在image view里且最长边与image view的边长相等,如何实现呢?参考以下链接:

被这个问题困扰了一段时间,以上几个链接提供的方法都没成功。后来发现,在使用autolayout的情况下,控件的width和height应该通过constraint指定(见下图),配合contentMode=scalefit即可解决。

file

顺便提一句,在storyboard里看到的控件位置并不一定是运行时的位置,因为autolayout情况下只考虑constraint,运行时的位置在storyboard里以橘黄色虚线表示(见下图)。

file

Storyboard还提供了多设备预览功能,使用方法是“打开 Main.storyboard ,然后选择 View\Assistant Editor\Show Assistant Editor ,这时编辑区会分隔为两部分。再点击顶部导航栏中的 Automatic ,在弹出菜单中选择 Preview ,最后选择 Main.storyboard (Preview) ... 点击预览界面左下角的 + 按钮,会弹出当前storyboard文件支持的各种尺寸的设备”(来源

关于"Relative to margins"选项,在xcode7里这个选项是默认勾选的,每个view默认有宽度为8的margin,可以通过“Editor->Canvas->Show Layout Rectangles”菜单项打开,margin位置显示为蓝色细线。举例来说,当勾选了“Relative to margins”选项后,如果一个子view的leading space to 父view是0,运行时会看到子view距离父view的边界还有8的空白区域(效果相当于android里给父view设置了8的padding)。这个缺省margin值可以通过下面的方法设置:

self.rootView.layoutMargins = UIEdgeInsetsMake(0, 50, 0, 0);

参考链接:

从此爱上iOS Autolayout 

Layout Margin Comes With iOS8

第三方依赖包

依赖包管理/Carthage

先安装Homebrew(osx的包管理工具),然后通过Homebrew安装Carthage(比cocoapods灵活,去中心,只支持ios8和以上版本)。

在工程目录下新建“Cartfile”文件,并填写要依赖的包,然后执行carthage update命令,此时如果提示“unable to find utility "xcodebuild", not a developer tool”,可使用"sudo xcode-select --switch"命令解决,这样就下载了依赖包。为了让xcode能用到carthage编译的framework,先从Finder把刚才生成的.framework文件拖到工程的General -> Linked Frameworks and Libraries区域(在Navigator里点击工程根目录即可看到)里,然后在Build Phases里添加一个新内容是“/usr/local/bin/carthage copy-frameworks”的Run Script,并在Input Files里增加所需的编译后的依赖包如“$(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework”。 参考Carthage的Getting Start 

经测试还需要在工程的Build Setting -> Framework Search Path里添加“$(SRCROOT)/Carthage/Build/iOS”,否则build会失败。参考资料

依赖包管理/CocoaPods

有些第三方依赖只支持cocoapods,所以也要装一个cocoapods。cocoapods的资料比较多,例如这篇,也比较容易安装使用。一个需要注意的问题是,执行pod setup命令后提示Setting up CocoaPods master repo要等很久,大约1小时。

在swift文件里使用object-c开发的库,需要借助一个bridging文件,通常名称是“项目名-Bridging-Header.h”。可以让xcode生成这个文件,方法是在工程里创建一个空的Objective-C文件,最后一步时xcode会提示是否创建bridging文件,点YES然后再删掉这个空文件即可。在bridging文件里使用类似#import <AFNetworking/AFNetworking.h>的方式包含依赖包。

如果pod install执行很慢,可能是pod更新spec时被墙,可以加--no-repo-update参数。参考链接

pod install --verbose --no-repo-update

还可以改用国内镜像,例如:

pod repo remove master 
pod repo add master https://git.oschina.net/6david9/Specs.git 
pod reap list

无法安装Realm:ios-charts依赖Realm,安装时很慢(有时等待数小时无果,有时提示SSL Handshake错误),其实真正原因是Realm/core所在服务器被墙。可以通过1)使用VPN解决,或2)手工下载安装core(参考Download core 0.96.0 failed),或3)使用国内镜像(如果已经下载core失败过需要清除缓存,参考Core occasionally cannot be downloaded from China)。

HTTP请求/远程图片

据说SwiftHTTP是个坑,暂时绕开,使用系统提供的NSURLSession简单封装一下。使用方法参考链接

iOS9里默认要求所有http请求都使用https,如果一定要用http,在工程的info.plist里修改“App Transport Security Settings -> Allow Arbitrary Loads”为true。

AFNetworking,功能强大且被广泛使用的Http库,还带有异步加载/缓存远程图片的功能;对应Swift版本的名称是Alamofire

JSON

暂时使用SwiftyJSON,感觉还好,使用举例:

let json1 = JSON(\["param1":1\])

下拉刷新/上拉翻页

下拉刷新:iOS8起内置了UIRefreshControl可以方便的实现下拉刷新功能 用法

上拉翻页/加载更多:暂时没有发现原生的简便方法实现,第三方库倒是比较多,例如SVPullToRefreshMJRefresh

进度条

由于TableViewController显示Activity Indicator有问题,目前在用SVProgressHUD作为替代方案。

折线图/饼图

ios-chart,项目主页的Usage里说了一堆步骤其实都是针对手动安装的,如果用cocoapods直接编辑Podfile添加pod 'Charts'再执行pod install即可,bridge都不用改,在自己的.swift文件里import Charts就可以使用了。不过我遇到一个编译问题,ChartPlatform.swift这个文件编译不过去,自己在文件结尾加了个空行解决。使用方面,一个比较好的教程见这个链接

经过试用,发现ios-charts画时序图(timeseries)比较麻烦,要自己做一些计算(参考)。

替代方案:Core Plot(侧重科学绘图),ios-linechart(使用简单),JBChartView(来自Jawbone),BEMSimpleLineGraph(使用简单)。

(本文原文链接:https://www.cnblogs.com/bjzhanghao/p/5211586.html