Archive for December, 2008
iPhone表格中划动删除单元格
iPhone上的程序很多用表格UITableView来显示数据、做布局等。 当用UITableView显示数据是经常会需要删除、插入数据条目。 SDK提供两种删除UITableViewCell的方法
- 设置整个表格为编辑模式,如果没有做其他编辑风格(editing style),就会在每个单元格左面有一个圆形红色删除按钮,点击该按钮会在右边显示“Delete”按钮
- 在单元格上划动,会在划动的单元格右边显示“Delete”按钮, 这是SDK提供的快捷删除单元格的方法, 官方说法是 swipe to delete
要完成删除的动作必须在delegate中实现这个方法
1 2 3 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { } |
当点击“Delete” 删除按钮是会发送该消息, 在此可以处理删除。 此外如果想在删除按钮显示前以及删除动作完成后调整布局, 可以在delegate中实现下面两个方法
1 2 3 4 5 6 7 8 | // - (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath { } // - (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath { } |
需要注意的是,如果表格是在UIViewController里并且table的delegate是ViewController, 那么table必须是viewcontroller的view,而不是能是viewController 的view的一个子视图,比如在UIViewController的loadView实现中,以往会这么写
1 2 3 4 5 6 7 8 9 10 | -(void) loadView { UIView* contentView = [[UIView alloc] initWithFrame:...]; self.view = contentView; [contentView release]; tableView = [[UITableView alloc] initWithFrame:...]; // 初始化表格 [self.view addSubView: tableView]; } |
如果这样写,划动的时候就不会触发willBeginEditingRowAtIndexPath,必须这样写: self.view = tableView 才能触发willBeginEditingRowAtIndexPath。
Windows Mobile平台获取GPS信息
Windows Mobile(以下简称WM)的开发相对于iPhone来说简单的多, Apple在界面上明显略胜一筹,但是在实际应用的开发方面还是微软做的更到位。就像在iPhone上如果要想获取GPS信息是个不容易办到的事儿, 虽然开放了CLLocationManager类,但是所获得的GPS信息只是过期的cache, 要开发者自己写代码去获得最新的GPS信息, 还有可能不响应。 相比之下在WM系统就简单的多, WM5 Pocket SDk5 以后提供了叫做中间媒介驱动的一套函数,可以方便的访问GPS硬件及读取GPS定位信息,其实只有4个函数,他们分别是
GPSOpenDevice, GPSGetPosition, GPSGetDeviceState, GPSCloseDevice. 怎么样,简单吧
你只需要在文件中包含头文件
1 | #include <Gpsapi.h> |
以及链接Gpsapi.lib库文件即可,接下来就是在开始定位的地方打开设备
1 2 3 4 5 6 7 | // global vars static HANDLE hGPSEvents[2] = {0}; // hGPSEvents[0] = CreateEvent(NULL,TRUE,FALSE,"NewLocationDataEvent"); hGPSEvents[1] = CreateEvent(NULL,TRUE,FALSE,"DeviceStateChanged"); HANDLE hGPSDevice = GPSOpenDevice( hGPSEvents[0], hGPSEvents[1],NULL,0); |
两个由CreateEvent创建的事件对象分别在收到新的gps数据和gps硬件设备有变化的时候通知宿主, 比如当hGPSEvents[0]被触发的时候你可能会调用GPSGetPosition来获取最新的GPSGetPosition数据信息, 同样当hGPSEventsEvents[1]被触发时会使用GPSGetDeviceState获得设备当前状态以便控制程序。
在打开设备后, 如果卫星信号接收正常,而且接收到数据时会触发数据接收事件,在数据事件被触发时只要调用GPSGetPosition就可以读取最新GPS数据
1 2 3 | GPS_POSITION gpsPos = {0}; gpsPos.dwSize = sizeof(GPS_POSITION); DWORD dwRet = GPSGetPosition( hGPSDevice, &gpsPos, 3000,0 ); |
以上表示读取3秒钟之内的有效数据, gpsPos返回的是gps数据信息,其结构为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | typedef struct _GPS_POSITION { DWORD dwVersion; // 中间驱动的版本号,始终为GPS_VERSION_1 DWORD dwSize; // 该结构字节大小,必须在传入前赋值 DWORD dwValidFields; //标明返回的该结构中的哪些字段有效,比如经纬度,速度等, 不是所有GPSGetPosition硬件设备都支持 DWORD dwFlags; //数据状态, 始终为1 SYSTEMTIME stUTCTime; //卫星时间 double dblLatitude; //维度 double dblLongitude; //精度 float flSpeed; //速度,以节为单位 float flHeading; //卫星角度, 正北 为0 double dblMagneticVariation; //磁偏角度,由地理北极和磁北极误差所致 float flAltitudeWRTSeaLevel; //海拔高度 float flAltitudeWRTEllipsoid; //相对于地心的高度,这里使用WGS_84椭球做参考 GPS_FIX_QUALITY FixQuality; // 误差修正质量 GPS_FIX_TYPE FixType; //修正类型 GPS_FIX_SELECTION SelectionType; //二维、三维选择类型 float flPositionDilutionOfPrecision; // 有卫星方位所造成的误差,数值越小越精确,1.0表示误差最小,50 精度最差 float flHorizontalDilutionOfPrecision;// 水平方向误差 float flVerticalDilutionOfPrecision; // 垂直方向误差 DWORD dwSatelliteCount; //读取该数据时卫星数量 DWORD rgdwSatellitesUsedPRNs[GPS_MAX_SATELLITES];//相应卫星的伪随机数 DWORD dwSatellitesInView; // GPS设备可覆盖的卫星数量 DWORD rgdwSatellitesInViewPRNs[GPS_MAX_SATELLITES]; // 伪随机数 DWORD rgdwSatellitesInViewElevation[GPS_MAX_SATELLITES];// 卫星仰角 DWORD rgdwSatellitesInViewAzimuth[GPS_MAX_SATELLITES]; //卫星方位角 DWORD rgdwSatellitesInViewSignalToNoiseRatio[GPS_MAX_SATELLITES];// 卫星信号强度 } GPS_POSITION, *PGPS_POSITION; |
只要在线程或者消息队列里处理数据接收消息再读取数据即可完成定位及轨迹记录。
最后别忘了调用GPSCloseDevice关闭GPS设备, 实际上GPS中间驱动采用的是计数的方式来开关设备,当你调用GPSOpenDevice时如果设备处于关闭状态就将其打开, 否则如果已经被其它程序打开就只把计数加1, 关闭时如果计数为0,就关闭硬件设备,否则只是减少计数。
iPhone开发之打包zip文件
程序需要往服务器上上传文件, 因为iPhone用户往往是用gprs或者edge网络,为了节约流量以及加快上传速度,所以只好将要上传的文件打包成zip文件,这样体积小了, 也为用户节约了时间和金钱。 开始的时候抱有意思希望去挖掘SDK文档, 未果, sdk不提供zip相关接口,在apple论坛打听了一下,很多dx给的建议是用apple script在后台打包, 对此领域不熟悉,放弃。 好在iPhone的官方SDK支持zLib库,这就好了, 找来minizip,一个封装的挺好的C/C++ zip库, 动手创建Objective-C对象封装之, 只需要几行代码即可完成, 简单实用。
使用方法如下
1 2 3 4 5 6 7 | BOOL ret = [zip CreateZipFile2:l_zipfile]; ret = [zip addFileToZip:l_photo newname:@"photo.jpg"]; if( ![zip CloseZipFile2] ) { l_zipfile = @""; } [zip release]; |
其中 l_photo是之源文件路径, @”photo.jpg” 是在新的文件名(不带路径)
之前没有实现 解压缩的代码, 今天补上了, 使用起来也和压缩差不多,下面时用法演示
1 2 3 4 5 6 7 8 9 10 11 | ZipArchive* za = [[ZipArchive alloc] init]; if( [za UnzipOpenFile:@"/Volumes/data/testfolder/Archive.zip"] ) { BOOL ret = [za UnzipFileTo:@"/Volumes/data/testfolder/extract" overWrite:YES]; if( NO==ret ) { } [za UnzipCloseFile]; } [za release]; |
代码在附件中
solve crashes problem with UIImagePickerController
My application use UIImagePickerController for taking photos. It works well but one serious problem — memory crashes. The application crashes always after taking several photos, mostly around 4-6. Looked through the debug information, I found it occurs memory warning message frequently and crashes after entering didReceivedMemoryWaning twice. Viewing the memory use in Instrument (which is the performance tool integrated in xcode) , the memory in use increase about 18 MB, it’s really horrible. After a long time research, I got some solutions to improve it.
1. hold an UIImagePickerController instance during the lifetime of the application, this will avoid lots of memory leaks
2. set allowImageEditing property of UIImagePickerController to NO, if it’s not so necessary
Cocoa China也是非常好的网站,论坛高手如云,给过我很多帮助,特此感谢
下载google maps地图
最近在写一个iPhone上的程序, 其中用到了google maps, 最简单的做法是在程序中嵌入一个UIWebView然后加载一个网页,在网页中写上一段javascript并包含google maps 地图对象即可, 这样的做法简单但有很大的弊端。 主要是效率以及稳定性的问题, UIWebView似乎是缓存了很多文件, 而又没有开放的接口用于释放清除这些缓存内容,这就导致程序可使用内存越来越小,直至崩溃,尤其是如果要同时使用UIImagePickerController那就更严重。 所以只要自己写maps 显示模块, 基本思路是给定一个中心点 地理坐标也就是经纬度, 然后计算所在图块(Tile Maps)的序号,根据当前放大倍数构建URL 然后在使用多线程下载小图块后显示。
由此思路, 便开始着手找资料, 互联网是个好东西, 只要你细心去找肯定能找到自己想要的内容。 首先就是计算给定经纬度的点所在的图块编号,每个图块由其所在的纵横X,Y序号组成,这里给出几个基本函数可用于计算
根据经纬度计算X,Y序列号, zoom为所需要的放大倍数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static int LatitudeToYAtZoom( double lat, int zoom ) { double arc = EARTH_CIRCUM / ( 1<<zoom ); double result = DegToRad( lat ); result = sin( result ); result = (result+1) / (1-result ); result = log( result ); result *= EARTH_RADIUS / 2; result = (EARTH_HALF_CIRC - result ) / arc; return (int) round( result ); } static int LongitudeToXAtZoom( double lon, int zoom ) { double arc = EARTH_CIRCUM / ( 1 << zoom ); double result = DegToRad( lon ); result *= EARTH_RADIUS; result += EARTH_HALF_CIRC; result /= arc; return (int) round( result ); } |
你也可以根据图块号计算图块中心所在的经纬度坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | static double YToLatitudeAtZoom ( int y, int zoom ) { double arc = EARTH_CIRCUM / ( 1 << zoom ); double metersY = EARTH_HALF_CIRC - (y * arc ); double a = exp(metersY * 2 / EARTH_RADIUS ); double result = RadToDeg( asin((a-1) / (a+1) ) ); return result; } static double XToLongitudeAtZoom( int x, int zoom ) { double arc = EARTH_CIRCUM / ( 1 << zoom ); double metersX = (x * arc) - EARTH_HALF_CIRC; double result = RadToDeg( metersX / EARTH_RADIUS ); return result; } |
程序中用到的辅助函数,主要是转换弧度与角度的
1 2 3 4 5 6 7 8 | static double RadToDeg( double d ) { return d / PI * 180.0; } static double DegToRad( double rad ) { return rad* PI / 180.0; } |
还有几个重要的常量数据
1 2 3 4 | const double PI = 3.1415926; const double EARTH_RADIUS = 6378137.0; const double EARTH_CIRCUM = EARTH_RADIUS * 2.0 * PI; const double EARTH_HALF_CIRC = EARTH_CIRCUM / 2; |
接下来就是根据计算出的X,Y以及zoom来下载地图图块, google maps 一般的图块url形如 http://mt0.google.com/mt?v=w2.88&hl=en&x=&y=&s=&z=
其中mt0 中的‘0′ 之能是 0 - 3中的任一个数字, x是图块所在横向编号,y是图块所在纵向编号, z就是所需要的放大级数, 在程序下载的时候只需要格式化一下这个url即可
1 2 3 | static int tileServer = 0; //........ NSString* url = [NSString stringWithFormat:@"http://mt%d.google.com/mt?v=w2.88&hl=en&x=%d&y=%d&s=&z=%d", tileServer % 4, X,Y,zoomLevel]; |
注意 zoomLevel一般的范围是1-17。
关于下载的部分,可以根据自己的实际需要来实现, 还有要注意在移动以及放大缩小的时候计算新的图块重新下载绘制, 这里可以使用一些缓存技术以加快显示,带来更好的用户体验。
该文只是简单的介绍了下google maps 图块的原理和使用, 更多的内容欢迎大家一起探讨:)
终于拿到苹果iDP了
现在苹果放开了对中国大陆开发人员的限制, 国内的开发人员可以很容易的申请到iDP了,也就是iPhone的开发授权。只要按以下几个步骤进行,一般就没什么大问题了。
1. 登陆developer.apple.com/iphone 注册一个ID, 此步骤中最好填写真实信息,不然将来还得改
2. 在支付信息的页面找不到中国大陆, 此时你需要选择最后一项,发送请求给工作人员, 国内的是发给chinadev的
3. 如果没有什么问题, chinadev会发一封带有pdf附件的email, 这个pdf文件就是iDP Billing Doc
4. 认真填写, 其中Developer Name必须和注册时的相同, iDP PersonID不是你在第一步注册的id, 是登陆进入以后在个人信息profile页面显示的一串数字。 Follow-UP number 和 iDP Enrollment ID都回在第三步的email中告诉你,这篇文章的附件中有doc 版本的billing DOC, 需要的可以下载, 直接用word打开填写完毕打印即可, 此处要注意,签名必须手写,最好是中英文两种都写
5. 填写完成后发送传真给apple , 传真号码尾 +1(408)862 7602, 这个在billing doc上都有, 可千万别发错了哦, 上面可是有你的信用卡信息, 泄露就麻烦了
6. 等待apple 的确认,一般3- 7天就会有回应了, 如果表格填写不对, chinadev会发邮件联系
7. 上传你的软件到app store 等着数钱吧
注意: 必须要有支持visa的国际信用卡才行, 这里推荐招行信用卡:)