[cocoa demo]从图片中选取Frame的小工具

和设计相比,开发者的好处是遇到不好用的App就可以牛B哄哄的说:
“这个不给力啊,等哪天有空的时候自己做一个吧”。
等到真正要做的时候就愣了,满脑子的按钮不知道怎么摆….

在学校的时候,做的东西对UI的要求很低,不求美观,只要基本功能有就行了。
更不要谈什么用户体验,是不是User-Friendly之类的了。
工作的时候就大不一样了,做prototype的时候界面可能很粗糙。
但是等到Designer的东西(一般管这个叫mockup)出来,感觉就完全不一样了。
作为码农,常常觉得自己没有审美观….

我认为完全重现mockup的效果是很重要的,尤其是一些细节。
功能上开发者可以发挥,但是在自己不擅长的领域还是不要胡闹了。

好了,扯远了….

其实只是因为最近想学mac上的开发,然后因为有以上需求,
所以做了一个非常简单的工具作为练习。
真的很简单….不过如果你恰好有同样的需求,我觉得它还是有帮助的。
至少作为一个cocoa入门的demo,还是挺好的。

GetFrame

就是从mockup里框取一块区域,得到经过转换坐标的Frame。
左上的Button是用来打开图片文件的,左下的是用来切换显示的。

代码在Github上: GetFrame
App下载链接: GetFrame.zip

mac下的图标格式是icns
用这个网站转换图标比较方便,推荐一下: http://iconverticons.com/

UITableViewCell的背景

Saved Blog

UITableViewCell是一个很常用的View,通常我们都是直接使用它。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *cellIdentifier = @"CellIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"Line: %d", indexPath.row];
    
    return cell;
}

得到这个效果:

现在我们给tableViewCell加上点背景色:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *cellIdentifier = @"CellIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"Line: %d", indexPath.row];
    // cell.backgroundColor = [UIColor blueColor];
    cell.contentView.backgroundColor = [UIColor blueColor];
    return cell;
}

我们不应该直接使用cell.backgroundColor。Cell本身是一个UIView,我们所看到的部分其实只是它的一个Subview,也就是cell.contentView。所以,如果直接改变cell本身的背景色,依然会被cell.contentView给覆盖,没有效果。

不过,通过cell.contentView.backgroundColor来改变背景色还不是最好的Practice. 如果通过

tableView.editing = YES;

进入Edit模式,就会出现问题。

Cocoa提供的按钮背景色为透明。因为ContentView被移开,下面是tableView的颜色,已经不是cell的一部分了。

所以,最好的方式应该是通过cell.backgroundView来改变cell的背景。按照文档说明,backgroundView始终处于cell的最下层,所以,将cell里的其它subview背景设为[UIColor clearColor],以cell.backgroundView作为统一的背景,应该是最好的方式。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *cellIdentifier = @"CellIdentifier";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"Line: %d", indexPath.row];
    cell.textLabel.backgroundColor = [UIColor clearColor];
    
    UIView *backgrdView = [[UIView alloc] initWithFrame:cell.frame];
    backgrdView.backgroundColor = [UIColor blueColor];
    cell.backgroundView = backgrdView;
    [backgrdView release];
    
    return cell;
}

效果:

loadView的用法

UIViewController的loadView

用UIViewController有一段时间了,才发现以前对loadView的理解完全不到位。

假如我们用Xcode新建一个View-based Application,在ViewController.m中加上

- (void) loadView {
NSLog(@"loadView Called");
}

再增加viewDidLoad,按照一般的情况,我们会有这样的Code

- (void) viewDidLoad {
[super viewDidLoad];
UIButton *customButton = [UIButton buttonWith.....
......
[self.view addSubView:customButton];
}

现在打开MainWindow.xib,删掉其中的ViewController,并在AppDelegate.m的

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

里增加ViewController的初始化

viewController = [[XXXViewController alloc] init];

编译运行就有问题了。Console中不断的输出loadView Called!

仔细的阅读loadView的文档,才知道loadView不是这么用的。

loadView在每一次使用self.view这个property,并且self.view为nil的时候被调用,用以产生一个有效的self.view。这个接口原本是为了让我们自定义view用的。在不被subclass实现的情况下,也就是[super loadView]的效果,应该就是产生了一个有效的view,也就是一个空白的view。

在上面这种情况下,loadView被实现为空(只有一条打印语句),而且我们没有通过XIB初始化ViewController,所以在viewDidLoad被执行时,self.view是为nil的。所以在执行[self.view addSubView:customButton]时,loadView被调用,用来产生一个有效的view,使得self.view不再为nil。罢特,我们错了(-_-!)。我们的loadView什么也没有做,于是就出现了上面的情形,不断的调用一个什么都不做的loadView….

当然,我们只要在loadView中增加一句[super loadView]就没有问题了。但这并不是Cocoa的设计者所期望的。

loadView仅仅应该在开发者希望自行通过编码而不是Interface Builder定制view的时候被实现,而且不应该在其中调用[super loadView],你的loadView中应该有self.view = …这样的行为。

如果仅仅是想要在当前view上增加一些UIButton或是UILabel,应该在viewDidLoad里去做,此时不要实现自己的loadView。