xml中的网页元素处理与TTStyle显示
很无奈得发现xml中有网页元素,有<br>这种编码的,也有#86br这种编码的,而一般的网页视图或TTstyle只认前一种,所以只能转换。
采用是tbxml解析xml,所以以它为例。
用到的库有GTMNSString+HTML和NSString+HTML
在TBXML中引入NSString+HTML头文件,加入+ (NSString*) textForHTMLElement:(TBXMLElement *)aXMLElement
{ if (nil == aXMLElement->text) return @""; return [[[NSString stringWithCString:&aXMLElement->text[0] encoding:NSUTF8StringEncoding] stringByDecodingHTMLEntities] stringByDecodingHTMLEntities]; }
解析时调用该函数即可。
uitabbarcontroller in ios6 warning Two-stage rotation animation is deprecated
除了提示的用了two-stage rotation方法外,在设uitabbarcontroller为rootviewcontroller的appdelegate中未设置选定的selectedindex也会出现该警告。
解决方法:在初始化uitabbarcontroller后设定selectedindex=0即可。
three20 in xcode 4.5
按官方提供的脚本将three20库导入项目后各种出错。
1. _tapcount not found错误。将代码评论掉或将预处理DEBUG改成DEBUG_TOUCHES. #829
2. dyld: lazy symbol binding failed: Symbol not found: _objc_setProperty_nonatomic_copy three20-1.0.6.2无法在ios6下运行,将deploy target改成5.1。
3. self.view.width错误。引入Three20+Additions.h
ios6 simulator 报 Using two-stage rotation animation错
小白的用了xcode4.5,之前跑得好好的程序在ios6上报个Using two-stage rotation animation被遗弃,然后portaitupsidedown不支持旋转了。
其它一切正常。
但是我一点都没用two-stage的方法啊。。。
汗,还是用回ios5.1...
frame bounds理解
原文见http://ashfurrow.com/you-probably-dont-understand-frames-and-bounds
每个视图控制器都有一个在其视图层次最下层的视图属性(self.view),这个视图可以有子视图。
self.view的范围是从状态栏下面到属于该控制器的下界。如下图,其下界就是tabbar上方。这是因为tabbar是属于tabbarcontroller的,层次上算是该视图控制器的superviewcontroller. 而上界为什么是状态栏下面就可以理解了。如果在interfacebuilder里看self.view的属性,则可以看出其frame是(0,20,宽度,高度)。注意,由于xib显示不了父控制器,就算是在ib中选择了bottombar,高度也是460.
在ios4及以前,你只能在一个视图控制器中加载子视图,这样写代码管理这些视图显示就有些难了.
在ios5,你可以在一个视图控制器中加载子视图控制器中的视图
下面说下frame与bounds的区别
一言以蔽之,就是frame是指视图在父视图坐标系中的坐标,bounds是指在自已坐标系中的坐标.所以加载子视图的过程就了然了.
ios开发中显示网页元素的一些问题
如果文本中有网页元素(如换行,特殊字符,图片链接等),在处理文本显示上就显得比较麻烦了,特别是要将这些文本放在uitableviewcell中时,涉及cell重用,也许只有采用三方提供的uilabel才可能同时在cell重用的同时显示网页元素. 利用uiwebview和uitextview都不太能将把两者解决好.
方案:
1. uiwebview. 利用uiwebview显示网页元素比较简单,只要解析文本就好了. 尝试用了这里提供的解码库,采用的是GTM.注意解码的层次,uiwebview能够解析一层而已.如&<br>需要解析2次才成转成<br>然后在uiwebview中显示换行. 如果只解析一次,在uiwebview中就只显示<br>了.
2. uitextview. 利用未公开的实例方法setcontexttohtmlstring可以实现webview的功能,但是据说苹果不允许这样做.同时试了下在uitableview中显示简单的网页元素,结果打开到显示出来延时半秒到1秒左右,影响效率.
3. 三方库,如three20的styledtextlabel. 没试,主要是three20各组件都集成在一起. 把整个three20都加入现有的程序中觉得不太好.有空试着学着用.
uiwebview与uitableview
将uiwebview加入到uitableviewcell中在uitableview中显示的时候有如下几个问题:
1. tablecell的高度问题.
因为uitableview的delegate中高度的决定是在cell加载之前,而webview的最佳尺寸得当webview加载完后(其delegate的uiwebviewdidfinishload方法中,两种方法sizethatfits和javascript)才能得到,所以这里就有一个矛盾. 尝试了下通过自定义delegate将webview加载完后得到的最佳尺寸传递到uitableview中然后table重新加载(示例代码),结果导致了无限循环(原因见这里).
另一种方法是根据文本猜测uiwebview的高度, = =.由于uiwebview的字体和大小都不一样,所以要文本字体及大小的设置都要自己决定. 据说苹果也是这么做的,将cell的高度预先定义到一个表中然后使用.
2. tableviewcell的重用问题.
当高度的问题解决后,接着是cell重用.如果tablecell只是显示信息,而没有didselectedcellAtRowIndexPath然后pushviewcontroller的话,就应该没有重用问题.但是如果选择并进入下一个视图控制器,返回后再滚动table,重用问题就显示出来了----选择的那个cell的frame加载了多次文本. 可能是由于下一视图控制器retain了该cell中的对象,原因不明.
所以只好舍弃tabelcell重用,直接cell=[tablaeviewcell alloc] init],一切就正常了.
loadView/viewDidLoad/initWithNibName/awakeFromNib/initWithCoder的用法总结
首先,区别程序化初始viewcontroller,半程序化初始和IB初始的区别.
1. 代码化初始:完全没有Xib之类的东西,通过纯代码实现加载.
2. 半代码化:设计xib,然后在程序中用代码调用xib来初始化.
3. 完全IB化:设计xib之类,然后加入到其它xib里.如MainWindow.xib中加入rootviewController,而rootviewcontroller通过xib设计.或者tabbarcontroller中加入多个由xib设计的viewcontroller,这些都是通过IB来初始化的.
好了,了解了这些,来看看各种方法的使用.
1. viewDidLoad:这个方法在三种方式下都会调用,而且是加载完view后调用.
2. loadView:代码初始化程序时设计view用. 半代码化时和完全IB化初始时亦调用,但会重写xib中的view,调用在initwithNibName之后,viewDidLoad之前.
3. initWithNibName:半代码化初始时使用. 完全IB化初始时不调用,UIViewController代码化初始时会通过init调用,且调用在[super init]中.
4. awakeFromNib:awakeFromNib这个方法是一个类在IB中被实例化是被调用的.看了帖子发现大家都推荐使用viewDidLoad而不要使用awakeFromNib,应为viewDidLoad会被多次调用,而awakeFromNib只会当从nib文件中unarchive的时候才会被调用一次.实际测试中发现,当一个类的awakeFromNib被调用的时候,那么这个类的viewDidLoad就不会被调用了,这个感觉很奇怪.
5.initWithCoder是一个类半代码化实例时被调用的.比如,通过IB创建一个controller的nib文件,然后在xocde中通过initWithNibName来实例化这个controller,那么这个controller的initWithCoder会被调用.
调用顺序: [super init](可选)->initWithNibName(其实在super init中) ->int after self created->loadView->ViewDidLoad
ios内存管理总结 (非ARC)
一. 变量与属性
1. interface中定义的变量为指针,在实现文件中指向对象,若没有定义它的property,则无引用计数.(应该是这样的吧).
2. interface中定义的属性根据修饰符管理内存对象,其属性其实是变量的setter和getter,同时根据修饰符附加相应的release和retain方法.如retain(strong)修饰符会在setter中定义
- (void)setCount:(NSNumber *)newCount { [newCount retain]; [_count release]; // Make the new assignment. _count = newCount; }
而resign,weak则不会retain. copy会对复制的对象retain.
3. 变量与属性.如上所述,如果只要变量指向已有的对象,则不用指定其属性.而定义了属性,则必有变量,不论是显示的synthesize p=_p 或隐式的synthesize p.
其中_p为指定的变量名,而隐式的,则p同时为变量名. 这里要注意p和self.p的区别, p只是变量指针,而self.p其实是调用了setter,这就和修饰符有关了.
二.对象生命周期(alloc,init,retain,release,release)
1. 对于alloc/new/copy/mutablecopy产生的对象要负责release掉,因为这些方法会加引用计数.
2. 其它方法创建的对象,如类工厂方法(+), (id)以及带error的,产生的引用不需要主动释放,如果没有主动retain的话.
3. 函数返回值.如在函数中定义的变量需要返回给调用者,则使用autorelease方法.
三.IB中的对象
1. 苹果推荐一般使用weak,但是需要自定义的,如要隐藏的view,则只能使用retain.
2. 由于viewcontroller用kvo设置所有outlets是强引用的,所以如果一个控制器被解除了,top-views也将自动释放.
3. 这时有两种策略:要么retain所有outlets,然后在viewDidUnLoad,dealloc中释放解除之.要么把所有outlets都定义为弱.但显然,每二种在某些功能上不现实.
多线程和GCD初学者教程
原文链接:http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial
当不使用多线程时,UI(主线程)经常变得不可响应. 该教程针对有相关开发经验者介绍多线程,如果是新手,请先查看更多教程.
一.为什么关注?
首先通过一个没有用多线程的实际的例子进行说明.
下载该工程,用xcode打开并运行,你将在屏幕上看到一个免费的游戏包(网页).
这个app的任务是把该网页中的所有图片都下载下来,并把它们在一个tableview中显示出来.
运行后你会发现该app经常无法响应用户操作,如grap按钮.
因为该app解析html,下载图片和zip文件,解压等操作都在主线程中进行.
二.多线程...和猫!(作者爱猫?)
如果你已经熟悉多线程的概念,那么就跳到下一节吧,否则,请继续.
当你想像程序的运行时,你可以认为是一只带着箭头的猫,这箭头指向某一行代码....(汗). 这只猫移动这个箭头使得程序运行.
app的关键问题是这只可怜的猫已经精疲力尽了,因为它又要管UI(响应用户事件)和大量数据处理.
多可怜啊,它需要休息.
解决文案很简单:再买几只猫.(买猫怕养不活哦).
现在,你的第一只猫负责更新UI和响应用户事件,而其它猫负责暗地里的下载文件,解析html等等等等.
这个就是多线程的要旨.
在ios中,你经常要实现的方法如viewDidLoad,button tap callbacks等都运行在主线程中,不要把费时的工作放到主线程中,不然你会得到一个不响应UI和精疲力尽的猫.
三.孩子,不要在家做这个.
让我们看看现有的代码并且讨论它是怎么工作的.
app中的rootViewController是WebViewController,当你点grabTapped按钮,它获取当前页的html并传给imageListViewController.
在imageListViewController中的viewDidLoad中,它创建一个新的imageManager并处理之.这个类,还有imageInfo,包含了所有的费时代码.
让我们看看这两个类是如何工作的:
1.ImageManager:processHTML:用正则表达式匹配来搜索html中的链接.
2.ImageInfo:initWithSourceURL:调用getImage来获取图片,通过[NSData dataWithContentsOfURL:..]同步进行.
3.ImageInfo:retrieveZip:同2来获取zip文件
4.ImageInfo:processZip:调用zipArchieve库进行解压缩.
其它的一些方法就不介绍了.主要就是理解它如何工作的,以便让我们改进.
四.异步下载.
首先让我们用异步下载来代替这个最慢的操作--下载文件.
其实用苹果内建的NSURLRequest和NSURLConnection并不难,但是使用ASIHTTPRequest更简单.
下载和添加该类就不介绍了.
现在是时候进行替换了.
在ImageManger.m中:
// Add to top of file #import "ASIHTTPRequest.h" // Replace retrieveZip with the following - (void)retrieveZip:(NSURL *)sourceURL { NSLog(@"Getting %@...", sourceURL); __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:sourceURL]; [request setCompletionBlock:^{ NSLog(@"Zip file downloaded."); NSData *data = [request responseData]; [self processZip:data sourceURL:sourceURL]; }]; [request setFailedBlock:^{ NSError *error = [request error]; NSLog(@"Error downloading zip file: %@", error.localizedDescription); }]; [request startAsynchronous]; }
该代码实现了当异步下载后可能要执行的两个Block.
同理,在ImageInfo.m中
// Add to top of file #import "ASIHTTPRequest.h" // Replace getImage with the following - (void)getImage { NSLog(@"Getting %@...", sourceURL); __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:sourceURL]; [request setCompletionBlock:^{ NSLog(@"Image downloaded."); NSData *data = [request responseData]; image = [[UIImage alloc] initWithData:data]; }]; [request setFailedBlock:^{ NSError *error = [request error]; NSLog(@"Error downloading image: %@", error.localizedDescription); }]; [request startAsynchronous]; }
运行之,app马上转到detail tab,但是还有一个问题:图片没有显示.
五.介绍NSNotifications.
传更新从一段代码到另一段代码,使用NSNotification是一种很简单的方法.
1.当你完成一任务后想通知给其它代码段,调用postNotificationName.
2. 当你想知道更新是否完成时,使用addObserver:selector:name:object.
3.用完后请记得removeObserver:name:object.
现在,打开ImageInfo.m
// Add inside getImage, right after image = [[UIImage alloc] initWithData:data]; [[NSNotificationCenter defaultCenter] postNotificationName:@"com.razeware.imagegrabber.imageupdated" object:self];
ImageListViewController.m
// At end of viewDidLoad [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageUpdated:) name:@"com.razeware.imagegrabber.imageupdated" object:nil]; // At end of viewDidUnload [[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.razeware.imagegrabber.imageupdated" object:nil]; // Add new method - (void)imageUpdated:(NSNotification *)notif { ImageInfo * info = [notif object]; int row = [imageInfos indexOfObject:info]; NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row inSection:0]; NSLog(@"Image for row %d updated!", row); [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; }
这样,图片更新就完成了.
六.Grand Central Dispatch and Dispatch Queues.
这样还有一个问题:解压zip还在主线程中.
有空再翻译....
ios without interface builder 开发
一.视图
把自定义视图控制器赋给Delegation-window-rootViewController,在自定义视图控制器的loadView方法中初始化view.
@class NOIBViewController; @property (strong, nonatomic) NOIBViewController *viewController;
self.window.rootViewController = viewController;
@property (strong, nonatomic) UILabel *label;
- (void)loadView { CGRect frame = CGRectMake(0, 0, 320, 480); self.view = [[UIView alloc] initWithFrame:frame]; self.view.backgroundColor = [UIColor whiteColor]; frame = CGRectMake(0, 0, 100, 50); label = [[UILabel alloc] initWithFrame:frame]; label.text = @"hello world"; [self.view addSubview:label]; }
二. 事件响应
- (void)loadView { frame= CGRectMake(20, 80, 280, 50); button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; button.frame=frame; [button setTitle:@"OK" forState:UIControlStateNormal]; button.backgroundColor = [UIColor clearColor]; [button addTarget:self action:@selector(btnClicked:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } -(IBAction)btnClicked:(id)sender { Some codes //删除自身视图 [self.view removeFromSuperview]; //加载子视图 [self.view addSubview:UserViewController.view]; }
三.navigationController
设置nav的initWithRootViewController为自定义视图控制器, 通过pushViewController方法加载其它视图控制器,一般popViewController自动实现--即为左侧按钮. 右侧按钮需要手动添加(NavigationItem.rightBarButtonItem).
nvc = [NumberViewController controllerWithNumber:1]; UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:nvc]; window.rootViewController = nav;
+ (id) controllerWithNumber: (int) number { NumberViewController *viewController = [[NumberViewController alloc] init]; viewController.number = number; viewController.textView.text = [NSString stringWithFormat:@"Level %d", number]; return viewController; } - (void) pushController: (id) sender { NumberViewController *nvc = [NumberViewController controllerWithNumber:number + 1]; [self.navigationController pushViewController:nvc animated:YES]; } - (void) viewDidAppear: (BOOL) animated { self.navigationController.navigationBar.tintColor = COOKBOOK_PURPLE_COLOR; // match the title to the text view self.title = self.textView.text; self.textView.frame = self.view.frame; // Add a right bar button that pushes a new view if (number < 6) self.navigationItem.rightBarButtonItem = BARBUTTON(@"Push", @selector(pushController:)); }
四.SplitViewController