• 强迫UIView以某种方向显示的秘诀

    我有一个项目其中某些UIView必须以特定的方向(Portrait或者Landscape)显示。这个看似简单的问题,困惑了我很久,直到今天我才完全找到解决的方法。
    读文章 »

     
  • pdf渲染的小窍门

    我们都知道,在iPhone/iPad显示pdf的基本方法有两个,一个是使用UIWebView直接加载pdf文件,另一个是使用Core Graphics进行渲染(姑且称之为CGPDF方法)。UIWebView的方法是简单,只需加载pdf,其他诸如放大翻页等问题通通交给UIWebView去头痛吧。但其缺点是性能较慢,功能有限,比如要实现搜索,添加笔记等功能就比较难。而使用CGPDF方法,功能就没有限制(虽然pdf解析方面,苹果提供的文档实在有限),使用Core Graphics进行渲染,性能上也比UIWebView要提高许多,只不过翻页,放大缩小等功能都需要自己实现。

    读文章 »

     
  • 检查UIWebView上touch的最简单的方法

    我有一个程序需要检测UIWebView是否有touch动作,不幸得很,UIWebView上的touchesBegan等事件无法被检测。在网上查了一下,有许多解决方法,比如在UIWebView上再加一个透明的UIView,重置UIWindow的sendEvent或重置UIWebView的hitest方法等等,要么就是方案不太完美,要么就是太过复杂,经过实验我使用的这种方法最为简单(否则我也没有时间写在这里了),当然我只要求检测UIWebView上有touch动作即可。我的方法是使用UITapGestureRecognizer。关键的地方有两点,请看我的代码(我的代码是在一个UIViewController中):

    1
    2
    3
    4
    5
        UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
        [self.view addGestureRecognizer:singleTap];
        singleTap.delegate = self;
        singleTap.cancelsTouchesInView = NO;
        [singleTap release];


    第三行是第一个关键的地方,就是必须设置UITapGuestureRecognizer的delegate(一般我们直接使用initWithTarget,无需设置其delegate)。

    第二个关键的地方是在下列delegate方法return YES。

    1
    2
    3
    4
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
        return YES;
    }


    做到这两点,你的UIWebView就可以响应touch事件了,你可以使用不同的UIGestureRecognizer来满足你不同的要求等。(handleSingleTap方法请自行补上)。

     
  • NSOperation和NSURLConnection

    最近一个客户向我汇报了一个程序的问题。经过分析发现问题是NSURLConnection没有正常工作。记得以前测试时没有问题啊。后来上网查到原来在iOS 4.0以后,NSURLConnection无法在NSOperation中正常运行,换句话说,NSURLConnection只能运行于主线程。解决方法其实倒是蛮简单(比如我的程序在download中):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    -(void)download
    {
        // NSURLConnectionn won't work if it's not in the main thread
        if (![NSThread isMainThread])
        {
            [self performSelectorOnMainThread:@selector(download) withObject:nil waitUntilDone:NO];
            return;
        }
       
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:_urlString]];
        _connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];     
    }
     
  • CALayer简单教程

    前一阵子读到一篇介绍CALayer(这里简单地称其为层)的教程,比较简单易懂,适合初学者,我在这里就不完全翻译了,只是把要点说明一下。

    读文章 »

     
  • 分享一段代码帮助进行调试

    有时程序崩溃根本不知错误发生在什么地方。比如程序出现EXEC_BAD_ACCESS的时候,虽然大部分情况使用设定NSZombieEnabled环境变量可以帮助你找到问题的所在,但少数情况下,即使设定了NSZombieEnabled环境变量,还是不知道程序崩溃在什么地方。那么就需要使用下列代码进行帮助了:

    1
    2
    3
    4
    5
    6
    #ifdef _FOR_DEBUG_
    -(BOOL) respondsToSelector:(SEL)aSelector {
        printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);
        return [super respondsToSelector:aSelector];
    }
    #endif

    你需要在每个object的.m或者.mm文件中加入上面代码,并且在other c flags中加入-D _FOR_DEBUG_(记住请只在Debug Configuration下加入此标记)。这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。

     
  • 程序中读取可用内存

    iPhone/iPad的内存十分紧张,所以有时进行调试时可能需要读取当前可用内存。其实挺简单,见下列代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #import <mach/mach.h>
    #import <mach/mach_host.h>

    @implementation Utils

    + (double)getAvailableMemory
    {
        vm_statistics_data_t vmStats;
        mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;
        kern_return_t kernReturn = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmStats, &infoCount);
       
        if (kernReturn != KERN_SUCCESS)
        {
            return NSNotFound;
        }
       
        return (vm_page_size * vmStats.free_count);
    }

    end

    使用方法更简单,比如:

    1
    NSLog(@"Available memory (KB) =  %f",[Utils getAvailableMemory]);
     
  • 怎样处理EXC_BAD_ACCESS

    相信很多人都知道通过NSZombies来帮助调试出现EXC_BAD_ACCESS的情况,但有时还是找不到需要的信息,那么应该怎么办呢?

    下面通过一个例子来说明.下面是hello world的代码:

    1
    2
    3
    NSString* hello = [NSString stringWithFormat:@"Hello world"];
    NSLog(@"What you say is %@",hello);
    [hello release];

    运行后出现EXC_BAD_ACCESS错误.但没有其他任何提示,这时候通过右击executables下的应用程序名,选择get info后,在arguments下输入环境变量(NSZombieEnabled,MallocStackLogging),如图所示:

    add_zombie

    再次运行后程序crash,如图:
    crash

    这次可以看到问题是”message sent to dealloced object”了,但具体是哪个语句引起的还并不知道,于是需要在gdb上输入以下语句:

    shell malloc_history pid address

    那么pid和address是什么呢?再看下crash的图片结合一下我以下使用的命令,你应该很快就可以判定pid和address是从哪里来的了,我的命令是:

    shell malloc_history 596 0×5f3ef80

    再次运行,程序crash时会出现大量的stack trace信息,如下图是与本程序相关的:
    malloc_history

    根据这些信息大家就可以找到问题出现在[BadAccessViewController viewDidLoad] 中与 +[NSString stringWithFormat:] 有关的地方.

    最后大家记得把环境变量NSZombieEnabled,MallocStackLogging删除或设置为NO,因为它们会使得内存不会被释放.

     
  • 怎样使UISearchBar背景透明

    在使用UISearchBar时,将背景色设定为clearColor,或者将translucent设为YES,都不能使背景透明,经过一番研究,发现了一种超级简单和实用的方法:

    1
    [[searchbar.subviews objectAtIndex:0]removeFromSuperview];

    背景完全消除了,只剩下搜索框本身了。

     
  • 都是Backgrounder惹的祸 – 解决“越狱”真机调试的问题

    我的iPhone越狱后,在用Xcode进行调试时出现以下错误信息:


    warning: Unable to read symbols for “/Library/MobileSubstrate/DynamicLibraryies/libstatusbar.dylib” (file not found).

    Program received singal: “SIGUSR1″.


    上网查了一下原来是Backgrounder在作怪,最简单的解决方法就是在Backgournder的overrides设定中,禁止要调试的程序以background模式运行即可。


    想要知道具体原因,请参见:Backgrounder vs. Build and Run