`

Storyboard 解析

    博客分类:
  • IOS
 
阅读更多

 

故事版(Storyboard)是一个能够节省你很多设计手机App界面时间的新特性,下面,为了简明的说明Storyboard的效果,我贴上本教程所完成的Storyboard的截图:


The full storyboard we'll be making in this tutorial.

现在,你就可以清楚的看到这个应用究竟是干些什么的,也可以清楚的看到其中的各种关系,这就是Storyboard的强大之处了。如果你要制作一个页面很多很复杂的App,Storyboard可以帮助你解决写很多重复的跳转方法的麻烦,节省很多时间,以便你能够完全的专注于核心功能的实现上。

 

开始

 

首先启动Xcode,新建一个工程,我们在这里使用Single View App Template,这个模板会提供一个类和一个Storyboard,免去我们自己创建的麻烦。

Xcode template options

 

 

创建完成之后,Xcode的界面大概是这样的:

 

Xcode window after creating project

 

 

这个新的工程由两个类:AppDelegate和ViewController以及一个Storyboard组成(如果你选择了两个设备会有两个Storyboard),注意这个项目没有xib文件,让我们首先看看Storyboard是什么样的,双击Storyboard打开他:

 

Storyboard editor

 

 

Storyboard的样子和工作方式都和Interface Builder(以下简称为IB)像极了,你可以从左下方的控件库中拖动控件到你的View之中并且组织他们的排放顺序,唯一不同的地方就是,Storyboard不止是包含一个视图控件,而是所有的视图控件以及他们之间的关系。

 

 

 

Storyboard对一个视图的官方术语是一个场景,但是一个场景其实就是一个ViewController,在iPhone中一次只能够展示一个场景,而在iPad中一次可以展示多个场景,比如Mail应用程序。

通过尝试添加一些控件,你可以感受一下Storyboard的工作方式。

Dragging controls from Object Library

这个是数据显示器,显示所有场景及其控件的结构。

Document outline

 

在IB中,这个位置显示的是你的NIB文件中的文件,而在Storyboard中这里显示的是ViewController,目前这里只有一个ViewController,我们接下来可能会增加一些。

这是一个文档管理器的缩小版,叫做dock。

 

The dock in the Storyboard Editor

 

Dock展示场景中第一级的控件,每个场景至少有一个ViewController和一个FirstReponder,但是也可以有其他的控件,Dock还用来简单的连接控件,如果你需要向ViewController传递一个关系时,只需要将其按住Ctrl键拖到ViewController上就可以了。

Note:你大概不会太长使用FirstResponder,因为它只是一个代理控件,代表着当前你所使用的控件。

现在运行这个应用,他会向我们设计的界面一样。

 

App with objects

 

 

如果你以前制作过NIB型的应用的话,你也许回去寻找MainWindow.xib ,这个文件包括所有的ViewController,Appdelegate等等,但是在Storyboard中这个特性已经被废止了。

 

No more MainWindow.xib

那么,没有这个文件,应用从那里起始呢?

让我们打开AppDelegate文件,看看那上面是怎么说的:

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end

如果要使用Storyboard特性,那么AppDelegate必须继承自UIResponder类, 之前则是继承自NSObject类的,而且必须有一个不是UIOutlet类的Window属性声明才可以。

如果你再去看AppDelegate的执行文件,里面大概什么都没有,甚至连 application:didFinishLaunchingWithOptions: 也只是返回了一个 YES,而之前,这里则需声明一个ViewController并且将他设置成起始页面,但是现在这些都没有了。

秘密就在info.plist文件中, 打开Ratings-Info.plist (在 Supporting Files group里) 你就会看到这些:

 

Setting the main storyboard file base name in Info.plist

 

在NIB为UI的应用里,info.plist文件中有一个键兼做NSMainNibFile,或者叫做Main nib file base name,他用来指示UIApplication载入MainWindow.xib,并且将他与应用链接起来,而现在这个键值消失了。

而Storyboard应用则利用 UIMainStoryboardFile,或者 “Main storyboard file base name” 键值来表示当App初始化时的Storyboard名称,当程序运行时,UIApplication会使用MainStoryboard.sotryboard作为第一加载项,并且将他的UIWindow展示在屏幕上,不需要任何编程工作。

在项目总结面板上,你也可以看到并且编辑这些信息:

 

Setting Main Storyboard in Target summary

如果你还想设置nib文件的话,另外有地方去设置的。

为了完成这个实验性的小程序,我们打开main.m,加入

#import <UIKit/UIKit.h>

#import "AppDelegate.h"

int main(int argc, char *argv[])
{
	@autoreleasepool {
		return UIApplicationMain(argc, argv, nil,
			NSStringFromClass([AppDelegate class]));
    }
}

之前是UIApplicationMain()的函数现在是空的, 变成了 NSStringFromClass([AppDelegate class]).

与之前使用MainWindow.xib的一个最大的不同是:现在app delegate已经不是Storyboard的一部分了,这是因为app delegate不再从nib文件中,而侍从Storyboard中加载了,我们必须告诉 UIApplicationMain 我们的app delegate类的名字是什么,否则他将无法找到。

 

制作一个Tab类型的应用

 

本教程中的Rating App拥有两个Tab,在Storyboard中,很轻松就能够做出一个Tab视图。

回到MainStoryboard.storyboard中,直接从左边的Library拖进来一个TabViewController就可以了。

Adding a new tab bar controller into the Storyboard

 

新的Tab Bar Controller附带了两个View controller,分别作为Tab的视图使用,UITabBarController被称为包含视图,因为他包含这其他一些View,其他常见的包含视图还有那vi嘎提鸥鸟 Controller和SplitView Controller。

 

在iOS 5中,你还可以自己写一个自定义的Controller,这在以前是做不到的。

 

包含关系在Storyboard中用一下这种箭头表示。

 

Relationship arrow in the Storyboard editor

 

拉一个Label控件到第一个子试图中,命名为“First Tab”,再在第二个子视图中添加一个Label,命名为“Second Tab”。

注意:当屏幕的缩放大于100%时,你无法在单个场景中添加控件。

选中Tab Bar Controller,进入属性检查器,选中“作为起始场景”,如下图:

 

Is Initial View Controller attribute

 

现在那个没有头的虚虚的小箭头指向了Tab Bar Controller,说明他是起始场景。

 

Arrow indicating initial view controller in Storyboard editor

 

这意味着,当你启动这个应用的时候,UIApplication将会将这个场景作为应用的主屏幕。

Storyboard一定要有一个场景是起始场景才行。

现在运行试试吧

 

App with tab bar

code专门为创造这种Tab Bar的应用准备了一个模板,我们也可以使用他,但是自己有能力不用模板自己做一个Tab Bar也是不错的事。

 

 

如果你添加了多于五个子视图到一个TabBarcontroller的话,并不会创造五个Tab,第四个tab会自动变成More标签,不错吧

 

制作一个表格视图

 

目前连接到Tab bar Controller的视图都是普通的View Controller,现在,我要用一个TableViewController来代替其中的一个ViewController。

单击第一个视图并删除,从Library中拖出一个TableViewController。

 

 

Adding a new table view controller to the Storyboard

 

 

在选中这个TableViewController的前提下,从Library中拖出一个NavController,将会直接附着在上面。

Embedding in a navigation controller

 

当然也可以调换顺序,我完全没意见。

由于NavController和TabBarController一样也是一个包含控制器视图,所以他也必须包含另一个视图,你可以看到同样的箭头连接者这两个View。

 

View controller relationships in outline of Storyboard editor

 

请注意所有嵌套在NavController下的View都会有一个Navigation Bar,你无法移除它,因为他是一个虚拟的Bar。

 

如果你检视属性检测器,你就会发现所有bar的属性都在一起:

 

Simulated metrics in Storyboard editor

 

“Inferred”是Storyboard中的默认设置,他意味着继承的关系,但是你也可以改变他。但是请注意这些设置都是为了让你更好的进行设计和这样设置的,随意修改默认设置会带来不可遇见的后果,施主自重。

 

现在让我们把这个新的场景连接到Tab Bar Controller中,按住Ctrl拖动,或者右键。

 

 

Connecting scenes in the storyboard

当你放手的时候,一个提示框会出现。

Create relationship segue

当然是选第一个了,Relationship – viewControllers ,这将自动创建两个场景之间的关系。

 

Relationship arrow in the Storyboard editor

 

Rearranging tabs in the Storyboard editor

直接拖动就可以改变Tab Item的顺序,同时也会改变显示Tab的顺序,放在最左边的Tab会第一个显示。

 

现在运行试试看吧

App with table view

 

在我们在这个应用中加入任何实质性的功能之前,我们先来清理一下Storyboard,你不需要改变TabBarController中的任何内容而只需要改变他的子视图就可以了。

 

每当你连接一个新的视图到TabBarController中的时候,他就会自动增加一个Tab Item,你可以使用他的子视图来修改该Item的图片和名称。

 

在NavController中选中Tab Item并且在属性编辑其中将其修改为Player。

 

Setting the title of a Tab Bar Item

 

将第二个Tab Item命名为“Gesture”

我们接下来把自定义的图片加入到这些item中, 源码 中包含一个名为“Image”的文件夹,在那里你可以找到我们用到的资源。

接下来,将NavController的title改为Player,也可以使用代码··

 

Changing the title in a Navigation Item

 

运行看一看,难以置信吧,你到现在也没写一条代码。

App with the final tabs

原型表格单元

 

你也许已经注意到了,自从我们加入了Table View Controller之后,Xcode便会现实下面这样一条警告。

Xcode warning: Prototype cells must have reuse identifiers

 

这条警告是:“Unsupported Configuration: Prototype table cells must have reuse identifiers”意思是,原型表格单元必须有一个身份证(意译啦)

原型单元格是另一个Storyboard的好特性之一。在之前,如果你想要自定义一个Table Cell,那么你就不得不用代码来实现,要么就要单独创建一个Nib文件来表示单元格内容,现在你也可以这样做,不过原型单元格可以帮你把这一过程大大的简化,你现在可以直接在Storyboard设计器中完成这一过程。

 

Table View现在默认的会带有一个空白的原型单元格,选中他,在属性控制器中将他的Style改为subtitle,这样的话,每一格就会有两行字。

 

Creating a Prototype cell

 

Creating a view controller with the table view controller template

将附件设置为Disclosure Indicator并且将这个原型单元格的Reuse Identifier 设置喂“PlayerCell”,这将会解决Xcode所警告的问题。

试着运行一个,发现什么都没变,这并不奇怪,因为我们还没有给这个表格设置一个数据来源(DataSource),用以显示。

 

新建一个文件,使用UIViewContoller模板,命名为 PlayersViewController ,设置喂UITableViewController的子类,不要勾选建立XIB文件。

 

回到Storyboard编辑器,选择Table View Controller,在身份控制器中,把他的类设置为PlayerViewController,这对于把Storyboard中的场景和你自定义的子类挂钩是十分重要的。要是不这么做,你的子类根本没用。

Setting the class name in the identity inspector

 

现在起,当你运行这个应用时,table view controller其实是PlayersViewContoller的一个实例。

在 PlayersViewController.h 中声明一个MutableArray(可变数组)

 

#import <UIKit/UIKit.h>

@interface PlayersViewController : UITableViewController

@property (nonatomic, strong) NSMutableArray *players;

@end

这个数组将会包含我们的应用的主要数据模型。我们现在加一些东西到这个数组之中,新建一个使用Obj-c模板的文件,命名为player,设置喂NSObject的子类,这将会作为数组的数据容器。

 

编写Player.h如下:

@interface Player : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *game;
@property (nonatomic, assign) int rating;

@end

编写Player.m如下:

#import "Player.h"

@implementation Player

@synthesize name;
@synthesize game;
@synthesize rating;

@end

这里没有什么复杂的,Player类只是一个容器罢了,包含三个内容:选手的名字、项目和他的评级。

接下来我们在App Delegate中声明数组和一些Player对象,并把他们分配给PlayerViewController的players属性。

在AppDelegate.m中,分别引入(import)Player和PlayerViewController这两个类,之后新增一个名叫players的可变数组。

 

 
#import "AppDelegate.h"
#import "Player.h"
#import "PlayersViewController.h"

@implementation AppDelegate {
	NSMutableArray *players;
}

// Rest of file...

修改didFinishLaunchingWithOptions方法如下:

 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	players = [NSMutableArray arrayWithCapacity:20];
	Player *player = [[Player alloc] init];
	player.name = @"Bill Evans";
	player.game = @"Tic-Tac-Toe";
	player.rating = 4;
	[players addObject:player];
	player = [[Player alloc] init];
	player.name = @"Oscar Peterson";
	player.game = @"Spin the Bottle";
	player.rating = 5;
	[players addObject:player];
	player = [[Player alloc] init];
	player.name = @"Dave Brubeck";
	player.game = @"Texas Hold’em Poker";
	player.rating = 2;
	[players addObject:player];
	UITabBarController *tabBarController =
     (UITabBarController *)self.window.rootViewController;
	UINavigationController *navigationController =
     [[tabBarController viewControllers] objectAtIndex:0];
	PlayersViewController *playersViewController =
     [[navigationController viewControllers] objectAtIndex:0];
	playersViewController.players = players;
    return YES;
}

这将会创造一些Player对象并把他们加到数组中去。之后在加入:

 

 
UITabBarController *tabBarController = (UITabBarController *)
  self.window.rootViewController;
UINavigationController *navigationController =
  [[tabBarController viewControllers] objectAtIndex:0];
PlayersViewController *playersViewController =
  [[navigationController viewControllers] objectAtIndex:0];
playersViewController.players = players;

咦,这是什么?目前的情况是:我们希望能够将players数组连接到PlayersViewController的players属性之中以便让这个VC能够用做数据来源。但是app delegate根本不了解PlayerViewController究竟是什么,他将需要在storyboard中寻找它。

 

这是一个我不是很喜欢storyboard特性,在IB中,你在MainWindow.xib中总是会有一个指向App delegate的选项,在那里你可以在顶级的ViewController中向Appdelegate设置输出口,但是在Storyboard中目前这还不可能,目前只能通过代码来做这样的事情。

UITabBarController *tabBarController = (UITabBarController *)
  self.window.rootViewController;

我们知道storyboard的起始场景是Tab Bar Controller,所以我们可以直接到这个场景的第一个子场景来设置数据源。

 

PlayersViewController 在一个NavController的框架之中,所以我们先看一看UINavigationController类:

UINavigationController *navigationController = [[tabBarController
  viewControllers] objectAtIndex:0];

然后询问它的根试图控制器,哪一个是我们要找的PlayersViewController:

PlayersViewController *playersViewController =
  [[navigationController viewControllers] objectAtIndex:0];

但是,UIViewController根本就没有一个rootViewController属性,所以我们不能把数组加入进去,他又一个topViewController但是指向最上层的视图,与我们这里的意图没有关系。

 

 

现在我们有了一个装在了players物体合集的数组,我们继续为PlayersViewController设置数据源。

 

打开PlayersViewController.m,加入以下数据源方法:

 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
	return 1;
}

- (NSInteger)tableView:(UITableView *)tableView
  numberOfRowsInSection:(NSInteger)section
{
	return [self.players count];
}

真正起作用的代码在cellForRowAtIndexPath方法里,默认的模板是如下这样的:

 
- (UITableViewCell *)tableView:(UITableView *)tableView
  cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]
          initWithStyle:UITableViewCellStyleDefault
          reuseIdentifier:CellIdentifier];
    }

    // Configure the cell...
    return cell;
}

无疑这就是以前设置一个表格视图的方法,不过现在已经革新了,把这些代码修改如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:@"PlayerCell"];
	Player *player = [self.players objectAtIndex:indexPath.row];
	cell.textLabel.text = player.name;
	cell.detailTextLabel.text = player.game;
    return cell;
}

这看上去简单多了,为了新建单元格,你只需使用如下代码:

UITableViewCell *cell = [tableView
  dequeueReusableCellWithIdentifier:@"PlayerCell"];

如果没有现存的单元格可以回收,程序会自动创造一个原型单元格的复制品之后返回给你,你只需要提供你之前在Storyboard编辑视图中设置的身份证就可以的,在这里就是“PlayerCell”,如果不设置这个,这个程序就无法工作。

 

由于这个类对于Player容器目前一无所知,所以我们需要在文件的开头加入一个引入来源

 

#import "Player.h"

记得要创建synthesize语句哦亲

@synthesize players;

现在运行应用,会看到Table里有着players容器。

Table view with data

请注意:我们这里只使用一种单元格原型,如果你需要使用不同类型的单元格的话,只需要在storyboard中另外加入一个单元格原型就可以了,不过不要忘记给他们指派不同的身份证。

 

设计自定义的原型单元格

 

对于很多应用来说,使用默认的单元格风格就OK了,但是我偏偏要在每一个单元格的右边加上一个一个图片来表示选手的评级,但是添加图片对于默认类型的单元格来说并不支持,我们需要自定义一个设计。

 

让我们转回MainStoryboard.storyboard,选中table view中的prototype cell,把它的Style attribute改为Custom,所有默认的标签都会消失。

首先把单元格变得更高一些,你可以直接拉它,也可以在大小控制器中修改数字,我在这里使用55点的高度。

 

从 Objects Library中拖出两个标签物体,按照之前的样式安插到单元格里,记得设置label的Highlighted颜色为白色,那样的话当单元格被选中的时候会看起来更好看一些。

 

之后添加一个Image View对象,将它放置在单元格的右边,设置他的宽度为81点,高度并不重要,在属性检查器中设置模式为置中。

我把标签设置为210点长以确保他不会和ImageView重合,最后整体的设计会看起来象下面这样:


Prototype cells with a custom design

由于这是一个自定义的单元格,所以我们不能够使用UITableView默认的textLabel和detailLabel来设置数据,这些属性也不再指向我们的单元格了,我们使用标签(tags)来指定标签。

 

将Name标签的tag设置为100,Game的设置喂101,image的设置喂102,在属性检查器里设置哦亲。

 

之后打开 PlayersViewController.m ,在PlayersViewcontroller中将cellForRowatIndexPath修改为:

 

- (UITableViewCell *)tableView:(UITableView *)tableView
  cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	UITableViewCell *cell = [tableView
      dequeueReusableCellWithIdentifier:@"PlayerCell"];
	Player *player = [self.players objectAtIndex:indexPath.row];
	UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
	nameLabel.text = player.name;
	UILabel *gameLabel = (UILabel *)[cell viewWithTag:101];
	gameLabel.text = player.name;
	UIImageView * ratingImageView = (UIImageView *)
      [cell viewWithTag:102];
	ratingImageView.image = [self imageForRating:player.rating];
    return cell;
}

这里是用了一个新的方法,叫做ImageRating,在 cellForRowAtIndexPath方法之前加入这个方法:

- (UIImage *)imageForRating:(int)rating
{
	switch (rating)
	{
		case 1: return [UIImage imageNamed:@"1StarSmall.png"];
		case 2: return [UIImage imageNamed:@"2StarsSmall.png"];
		case 3: return [UIImage imageNamed:@"3StarsSmall.png"];
		case 4: return [UIImage imageNamed:@"4StarsSmall.png"];
		case 5: return [UIImage imageNamed:@"5StarsSmall.png"];
	}
	return nil;
}

这就完成了,运行看看:

Wrong cell height for custom cells made in Storyboard editor

这和我们想象的结果并不是很符合,我们修改了原型单元格的属性和高度,但是table view却没有考虑进去,有两种方法可以修复它,我们可以改变table view的行高或者加入 heightForRowAtIndexPath 方法来修改,地一种方法更简单,我们就用他。

 

注意:在一下两种情况下,你应该使用 heightForRowAtIndexPath 方法:一是,你不能预先知道你的单元格的高度,二是不同的单元格会有不同的高度。

 

回到MainStoryboard.storyboard,在大小检查器中将高度设置为55:

Setting the table view row height

通过这种方式的话,如果之前你是使用拖动而不是键入数值的方式改变高度的属性的话,则table view的数值也会自动改变。

 

现在运行看看,好多了吧

 

为原型单元格设置子类

 

我们的表格视图已经相当像模像样了,但是我并不是很喜欢使用tag来访问label,要是我们能够把这些lable连接到输出口,之后在回应属性中使用他们,该多好,而且不出所料,我们可以这样做。

 

使用 Objective-C class模板新建一个文件,命名为PlayerCell,继承UITableViewCell。

修改PlayerCell.h

@interface PlayerCell : UITableViewCell

@property (nonatomic, strong) IBOutlet UILabel *nameLabel;
@property (nonatomic, strong) IBOutlet UILabel *gameLabel;
@property (nonatomic, strong) IBOutlet UIImageView
  *ratingImageView;

@end

修改PlayerCell.m

#import "PlayerCell.h"

@implementation PlayerCell

@synthesize nameLabel;
@synthesize gameLabel;
@synthesize ratingImageView;

@end

这个类本身并不其很大的作用,只是为nameLabel、gameLabel和ratingImageView声明了属性。

回到MainStoryboard.storyboard选中原型单元格,将他的class属性修改为“PlayerCell”,现在当你向table view请求dequeueReusableCellWithIdentifier,他会返回一个PlayerCell实例而不是一个普通的UITableViewCell实例。

请注意我将这个类和reuse Indetifier的名字命名的一样,只是营卫我喜欢这样哦亲,这两个之间其实没啥关系。

现在你可以将标签和image view连接到输出口去了,选中或者将他从链接检查器拖动到table view cell。

 

Connecting the player cell

I

请注意:要把这个control连接到table view cell而不是view controller哦亲,别选错了。

 

现在我们把一切都链接好了,只需要加入数据源的代码就可以了。

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	PlayerCell *cell = (PlayerCell *)[tableView
     dequeueReusableCellWithIdentifier:@"PlayerCell"];
	Player *player = [self.players objectAtIndex:indexPath.row];
	cell.nameLabel.text = player.name;
	cell.gameLabel.text = player.game;
	cell.ratingImageView.image = [self
      imageForRating:player.rating];
    return cell;
}

我们现在将接收到 dequeueReusableCellWithIdentifier 的控件指派到PlayerCell,只需要简单的使用已经链接labels和image view到设置好的属性上就可以了,这会让这个设计看上去更加好控制,更加简明。

当然,在PlayerCell前要引入资源:

 

#import "PlayerCell.h"

试着运行,你会发现其实什么都没有变化,可是我们都知道,内部已经有了变化。

在这相同的场景下面,我们可是在使用子类呢。

这里还有一些设计小窍门:第一点:一定要设置标签被选中时的颜色。

Selecting the proper highlight color

第二点,确保你加入单元格的字符大小是可以变化的,这样,当单元格大小变化时,他的内容的大小也会跟着变化,比如说:

在PlayersViewController.m中加入如下方法:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
	if (editingStyle == UITableViewCellEditingStyleDelete)
	{
		[self.players removeObjectAtIndex:indexPath.row];
		[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
	}
}

这个方法加入好了之后,用手指轻扫一行单元格,会出现一个删除键,试试看

The delete button overlapping a table view cell's content

 

Delete按钮出现在右边,遮住了一部分评级图片,怎么解决呢?

打开MainStoryBoard.storyboard,选中table view cell中的image view,在大小检查器中修改Autosizing属性,是它能够跟随上级view的边缘。

 

Autosizing attributes for the image view

为labels设置同样的属性。

Autosizing attributes for the labels

加入了这些变动之后,删除按钮如我们意料的出现了:

Delete button appearing with proper autosizing

其实,最好的做法是让这些星星在出现delete按钮的时候消失,不过这只是一个练习,不要太较真哦亲

分享到:
评论
5 楼 whao189 2014-03-31  
回家了详细看看
4 楼 southking 2012-06-14  
chairmanMao 写道
前面都做成了,就是添加按钮时,那个按钮把整个手机屏都给盖住了,这是什么情况?

估计是按钮的长和宽没调整吧。
3 楼 chairmanMao 2012-06-12  
前面都做成了,就是添加按钮时,那个按钮把整个手机屏都给盖住了,这是什么情况?
2 楼 southking 2012-04-23  
levismcgrady 写道
你好,我试按照你的教程做的,但是到这一步
引用
在选中这个TableViewController的前提下,从Library中拖出一个NavController,将会直接附着在上面。

我拉了nav过去,但是没有直接附上去,而是生成了一个nav还有一个TVC-Root,请问应该怎么做呢!?我的试xCode4.3.2


在xCode4.3.2里 NVC 和 TVC已经绑定到一块了,也就是无需先拖TVC再拖NVC,现在直接拖NVC过来就行了,已经包含TVC了。
1 楼 levismcgrady 2012-04-21  
你好,我试按照你的教程做的,但是到这一步
引用
在选中这个TableViewController的前提下,从Library中拖出一个NavController,将会直接附着在上面。

我拉了nav过去,但是没有直接附上去,而是生成了一个nav还有一个TVC-Root,请问应该怎么做呢!?我的试xCode4.3.2

相关推荐

    (175797816)华南理工大学信号与系统Signal and Systems期末考试试卷及答案

    内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    深圳建设施工项目安全生产奖惩管理制度.docx

    深圳建设施工项目安全生产奖惩管理制度

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    离散数学课后题答案+sdut往年试卷+复习提纲资料

    自考04741计算机网络原理真题及答案及课件

    04741计算机网络原理 2018(尚德).pdf 13年试题(2套).pdf 2015年10月自考计算机网络原理04741试题及答案解析.docx 2021年4月自考04741计算机网络原理真题及答案.docx 2021年4月自考04741计算机网络原理试卷.bak.docx 计算机网络原理 课后题答案 全 李全龙版 自考04741.zip.zip 计算机网络原理课件 计算机网络原理课件.rar

    C++实现rpc,全程手写

    C++实现rpc,全程手写

    前端拿到的列表数据里id都一样的处理办法.txt

    前端拿到的列表数据里id都一样的处理办法.txt

    最新仿720云全景制作源码-krpano仿720云全景网站源码 新增微信支付+打赏+场景红包

    最新仿720云全景制作源码|krpano仿720云全景网站源码(新增微信支付+打赏+场景红包等)是一款基于php+mysql开发制作的全景在线制作网站源码,包含全景图片,全景视频等。数据存储全部存于OSS云端或本地,源码完全开源可自行二次开发。 环境要求:PHP5.5.X+MYSQL5.6.X+伪静态 熟悉linux系统推荐使用LAMP,web服务器最好使用apache,不要使用nginx(发布大全景图需要时间可能需要20多分钟, nginx超时机制不好控制)。 Windows系统推荐使用phpstudy。Liunx推荐宝塔控制面板apache 前端为HTML5开发,自适应手机版! 1、支持VR虚拟现实、全景视频、环物全景、说一说、点赞评论、重力感应、智能视频嵌入、场景切换热点、加载进度条、 地图导航、光晕flash特效、物体全景嵌入、场景自播、场景解说、雷达导航等业内前沿功能。 2、支持windows、Linux、Mac、安卓、IOS等几乎所有的系统观看。支持CDN图片转存,极大的减轻的服务器流量费用。 3、支持用户权限分配。方便会员制收费。

    YOLO算法-可乐罐子数据集-336张图像带标签-可乐.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    环境监测系统源代码全套技术资料.zip

    环境监测系统源代码全套技术资料.zip

    【编码解码】基于matlab罗利衰落信道编解码器设计【含Matlab源码 9930期】.zip

    Matlab领域上传的视频均有对应的完整代码,皆可运行,亲测可用,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作

    四轮转向系统横摆角速度控制simulink仿真模型,利用滑模控制算法,基于八自由度车辆模型,控制有比较好的效果,附参考说明

    四轮转向系统横摆角速度控制simulink仿真模型,利用滑模控制算法,基于八自由度车辆模型,控制有比较好的效果,附参考说明。

    YOLO算法-工作场所安全隐患数据集-859张图像带标签-倒下的工人-配备个人防护装备的工人-无个人防护装备的工人-火.zip

    YOLO系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中,文件名末尾是部分类别名称; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值; 【注】可以下拉页面,在资源详情处查看标签具体内容;

    自学考试02331数据结构试题及答案2021-2022

    02142数据结构导论历年真题及答案(2012-2018共13套).rar 02331数据结构历年真题共267页2009.10-2019.4.rar 24数据结构201704_8.pdf 25数据结构201710_10.pdf 26数据结构201804_11.pdf 27数据结构201810_9.pdf 全国2021年04月高等教育自学考试02331数据结构试题及答案.docx 全国2022年04月高等教育自学考试02331数据结构试题及答案.docx 数据结构-课件.rar 第l六讲.ppt 第一讲.ppt 第七讲.ppt 第三讲.ppt 第九讲.ppt 第二讲.ppt 第五讲.ppt 第八讲.ppt 第四讲.ppt

    验收确认单表格.docx

    验收确认单表格.docx

    内存搜索工具(易).rar

    内存搜索工具(易).rar

    饮食管理系统项目源代码全套技术资料.zip

    饮食管理系统项目源代码全套技术资料.zip

    计算机视觉项目:Swin-Transformer 【tiny、small、base】模型实现的图像识别项目:番茄病害图像分类

    【项目简介】 代码主干网络采用Swin-Transformer 家族系列,包括【tiny、small、base】三种模型。pretrained和freeze_layers参数为是否采用官方预训练模型和是否仅训练分类头。为了做对比消融试验,优化器采用了Adam和SGD、AdamW三种。损失函数采用多类别的交叉熵、学习率优化策略采用cos余弦退火算法 【评估网络】 评估的指标采用loss和准确率(accuracy),分别会在训练集和验证集上进行评估、输出、绘制曲线图像。同时会在训练集、验证集进行一系列评估,包含混淆矩阵、recall、precision、F1 score等等曲线图像,以及recall、precision、F1 score、特异度的输出信息等等。 【具体各类别的指标在json文件中查看】 【如果想要更换数据集训练,参考readme文件】 【本项目为8种番茄病害图片(约4k张数据),包含数据集和标签,可以一键运行】

    (177121232)windows电脑下载OpenHarmony鸿蒙命令行工具hdc-std

    windows电脑下载OpenHarmony鸿蒙命令行工具hdc_std。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    小程序毕业设计项目-音乐播放器

    本项目可以作为小程序毕设项目,主要功能为音乐播放器,主要功能是:可以播放歌曲(采用mp3网络连接实现)、专辑封面播放时可以旋转,能够实现开始和暂停播放,可以点击下一首歌曲,主页面实现动态轮播图

    考研学习分享-JAVA-基于Vue+SpringBoot的考研学习分享平台设计与实现(毕业论文)

    考研学习分享功能的描述可以涵盖以下几个主要模块,旨在为考研学生提供一个互动、资源共享、经验交流的平台: 1. 用户注册与个人信息管理 学生可以通过邮箱或手机号注册账户,填写个人信息,如姓名、专业、目标院校等。 用户可设置学习目标和进度,方便记录自己的学习历程。 2. 学习资料共享 用户可以上传、下载考研相关学习资料,如教材、真题、笔记、复习计划等。 提供文件分类功能,按学科、院校、难度等进行整理,方便用户查找。 支持多种文件格式,如PDF、Word、Excel、图片等。 3. 复习经验分享 学生可以发布自己的复习经验文章,分享复习方法、备考心得、时间管理技巧等。 提供文章评论和互动功能,其他学生可以点赞、评论、提问,促进经验交流。 设置专栏或专题,帮助学生快速找到自己感兴趣的复习内容。 4. 考研小组与社交功能 学生可以创建或加入学习小组,组内成员可共享资料、讨论问题、互相鼓励。 提供私信、群聊功能,方便学员在小组内进行实时讨论和交流。 支持设置小组学习目标和定期检查进度,增加学习动力。 5. 在线课程与讲座 提供考研各科目(如英语、数学、政治等)的在线课程资源,用户可以报名参加。

Global site tag (gtag.js) - Google Analytics