>  기사  >  백엔드 개발  >  iOS 5 스토리보드 시작하기 (4)

iOS 5 스토리보드 시작하기 (4)

黄舟
黄舟원래의
2017-01-20 10:26:281300검색

AddPlayer 창을 이동시키세요

지금은 "Game" 줄을 무시하고 사용자가 입력한 플레이어 이름만 고려하겠습니다.
사용자가 취소 버튼을 터치하면 창이 닫히고 사용자가 입력한 모든 데이터가 손실됩니다. 이 부분은 괜찮습니다. 대리자 개체(Players 창)가 "didcancel" 메시지를 받으면 AddPlayer 창이 닫힙니다.

사용자가 완료 버튼을 터치하면 새 플레이어 개체를 만들고 해당 속성을 설정해야 합니다. 그런 다음 새 플레이어를 추가했으며 인터페이스를 새로 고쳐야 한다고 위임 개체에 알립니다.

따라서 PlayerDetailsViewController.m의 done 메소드는 다음과 같습니다.

- (IBAction)done:(id)sender {
        Player *player = [[Player alloc] init];
        player.name = self.nameTextField.text;
        player.game = @"Chess";
        player.rating = 1;
        [self.delegate playerDetailsViewController:self         didAddPlayer:player];
}

헤더 파일을 가져와야 합니다.

#import "Player.h"

done 메소드는 이제 새 Player 객체를 생성하고 이를 위임 객체로 보냅니다. PlayerDetailsViewController.h 파일의 현재 위임 프로토콜을 다음과 같이 수정해야 합니다:

@class Player;  
@protocol PlayerDetailsViewControllerDelegate <NSObject>
- (void)playerDetailsViewControllerDidCancel:   (PlayerDetailsViewController *)controller;
- (void)playerDetailsViewController:   (PlayerDetailsViewController *)controller    didAddPlayer:(Player *)player;
@end

didSave 메서드 정의가 제거되었으며 이를 다음으로 대체했습니다. didAddPlayer 메소드. 다음 단계는

PlayersViewController.m에서 이 메서드를 구현하는 것입니다.

  - (void)playerDetailsViewController:   (PlayerDetailsViewController *)controller    didAddPlayer:(Player *)player{
        [self.players addObject:player];
        NSIndexPath *indexPath =       [NSIndexPath indexPathForRow:[self.players count] - 1         inSection:0];
        [self.tableView insertRowsAtIndexPaths:       [NSArray arrayWithObject:indexPath]         withRowAnimation:UITableViewRowAnimationAutomatic];
        [self dismissViewControllerAnimated:YES completion:nil];
}

먼저 플레이어 배열에 새 Player 개체를 추가합니다. 그런 다음 테이블 뷰와 데이터 소스가 동기화 상태를 유지해야 하므로 테이블 뷰에 새 행(하단)을 추가하도록 지시합니다. [self.tableView reloadData]를 사용할 수도 있지만 새 행 삽입에 애니메이션을 적용하는 것이 더 좋습니다. UITableViewRowAnimationAutomatic은 iOS 5에 등장한 새로운 상수입니다. 행을 삽입하는 위치에 따라 적절한 애니메이션을 자동으로 선택하므로 매우 편리합니다.

프로그램을 실행하면 목록에 새로운 플레이어를 추가할 수 있습니다!

스토리보드에 성능 문제가 있는 것으로 의심할 수 있습니다. 그러나 전체 스토리보드를 한 번에 로드하는 것은 결코 멋진 일이 아닙니다. 스토리보드는 모든 viewController를 직접 초기화하지 않고 초기 뷰 컨트롤러만 초기화합니다. 초기 뷰 컨트롤러는 TabBarController이기 때문에 여기에 포함된 두 개의 뷰 컨트롤러(Players View Controller와 두 번째 뷰 컨트롤러)도 로드됩니다.

다른 ViewController는 "segue"할 때까지 초기화되지 않습니다. ViewController를 닫으면 해제되므로 사용한 nib 파일과 마찬가지로 현재 사용되는 ViewController만 메모리에 유지됩니다.

실험을 해보자. PlayerDetailsViewController.m에 이 메소드를 추가합니다:

- (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
               NSLog(@"init PlayerDetailsViewController");
        }
        return self;
 }
- (void)dealloc {
        NSLog(@"dealloc PlayerDetailsViewController");
}

initWithCoder 및 dealloc 메소드를 재정의하여 일부 정보를 출력합니다. 프로그램을 다시 실행하고 플레이어 추가 창을 엽니다. 이 시점까지는 ViewController가 할당되지 않았음을 알 수 있습니다. 창을 닫으면(취소 또는 완료 버튼) delloc의 출력을 볼 수 있습니다. 창을 다시 열면 initWithCoder가 출력한 콘텐츠도 볼 수 있습니다. 이는 nib 메서드를 사용하는 것처럼 Viewcontroller가 요청 시 로드된다는 것을 완전히 보여줍니다.

한 가지 더, 정적 셀은 UITableViewController에서만 사용할 수 있습니다. 스토리보드 편집기를 사용하면 일반 UIViewController의 TableView에서 정적 셀을 사용할 수 있지만 런타임에는 사용할 수 없습니다. 그 이유는 UITableViewController가 정적 셀에 대한 특별한 데이터 소스 메커니즘을 제공하기 때문입니다. Xcode는 이러한 프로젝트를 컴파일하지 못하도록 경고도 표시합니다.

"잘못된 구성: 정적 테이블 보기는 UITableViewController 인스턴스에 포함될 때만 유효합니다."(정적 테이블 보기는 UITableViewController에만 유효합니다.)

템플릿 셀은 일반 ViewController에서 사용할 수 있습니다. 물론 IB에서는 그렇지 않습니다. 따라서 템플릿 셀과 정적 셀을 사용하려면 스토리보드를 사용해야 합니다.

테이블 뷰에서 정적 셀과 동적 셀을 동시에 사용하고 싶을 수도 있지만 SDK에서는 이를 지원하지 않습니다. 꼭 해보고 싶다면 여기를 참고해주세요.

참고: 창에 정적 셀이 너무 많으면(화면이 수용할 수 있는 것보다 많은 경우) 마우스나 트랙패드 스크롤 동작을 사용하여 스토리보드 편집기에서 보기를 공유할 수 있습니다. 이 기능은 그다지 직관적이지는 않지만 유용합니다.

게임 선택 창

플레이어 추가 창에서 게임 셀을 탭하면 사용자가 게임을 선택할 수 있는 목록이 열립니다. 따라서 Table View Controller를 추가해야 합니다. 이번에는 Modal 방식이 아닌 Push 방식을 사용하겠습니다.

새 Table View Controller를 스토리보드로 드래그하세요. 플레이어 추가 창에서 게임 셀을 선택하고(위의 레이블이 아닌 전체 셀이 선택됨에 유의) Ctrl 키를 누른 채 새 TableViewController로 드래그합니다. 그러면 segue가 생성됩니다. Push 유형 segue를 선택하고 segue

이름을 "PickGame"으로 지정합니다.

탐색 표시줄을 두 번 클릭하고 제목을 "게임 선택"으로 변경합니다. 다음 그림과 같이 템플릿 셀의 스타일은 Basic으로 설정되고 재사용 ID는 "GameCell"로 설정됩니다.

iOS 5 스토리보드 시작하기 (4)

라는 이름의 새 UITableViewController 하위 클래스를 만듭니다. GamePickerViewController. 스토리보드에서 TableViewController의 식별자를 GamePickerViewController로 설정하는 것을 잊지 마세요.

先让我们为新场景准备一些数据。在GamePickerViewController.h 中添加实例变量:

@interface GamePickerViewController : UITableViewController {
     NSArray * games;
}

   

在 GamePickerViewController.m 的viewDidLoad 方法中:

- (void)viewDidLoad {
        [super viewDidLoad];
        games = [NSArray arrayWithObjects:
              @"Angry Birds",
              @"Chess",
              @"Russian Roulette",
              @"Spin the Bottle",
              @"Texas Hold’em Poker",
              @"Tic-Tac-Toe",
              nil];
}

   

因为在 viewDidUnload 中初始化games 数组,所以必须在 viewDidUnload 中进行释放:

- (void)viewDidUnload {
        [super viewDidUnload];
        games = nil;
}

   

尽管这个窗口的 viewDidUnload实际上永远不会调用(我们永远也不会用另一个视图来覆盖它),但保持 release/alloc 平衡是一种良好的做法。

修改 TableView 的数据源方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
}
- (NSInteger)tableView:(UITableView *)tableView    numberOfRowsInSection:(NSInteger)section {
        return [games count];
}  
- (UITableViewCell *)tableView:(UITableView *)tableView    cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView       dequeueReusableCellWithIdentifier:@"GameCell"];
        cell.textLabel.text = [games objectAtIndex:indexPath.row];
        return cell;
}

   

关于数据源就这么多。运行程序,轻击 Game 行,Choose Game 窗口会出现。点击任何行都不会有任何动作。当然,由于窗口是以Push 方式呈现的,你可以点击导航栏的返回按钮返回 Add Player 窗口。

iOS 5 스토리보드 시작하기 (4)

这很好,不是吗?我们不需要写任何呈现新窗口的代码。只是从静态 cell 拖拽了一条线到新窗口而已。(注意当你点击Game 行是, table view 的委托方法即PlayerDetailsViewController的 didSelectRowAtIndexPath是会被调用的,请不要在其中加入任何代码,以免造成混乱)。

当然,目前新窗口还没有什么作用。 我们必须在 GamePickerViewController.h中定义一个新的委托协议使他能返回一些数据:

@class GamePickerViewController;  
@protocol GamePickerViewControllerDelegate <NSObject>
- (void)gamePickerViewController:   (GamePickerViewController *)controller    didSelectGame:(NSString*)game;
@end  
@interface GamePickerViewController : UITableViewController  
@property (nonatomic, weak) id <GamePickerViewControllerDelegate> delegate;
@property (nonatomic, strong) NSString *game;  
@end

   

修改 GamePickerViewController.m为:

@implementation GamePickerViewController {
        NSArray *games;
        NSUInteger selectedIndex;
}
@synthesize delegate;
@synthesize game;

添加了一个新的实例变量 selectedIndex,以及属性合成语句。

在 viewDidLoad 方法末增加:

selectedIndex = [games indexOfObject:self.game];

   

用户选定的游戏名称将保存到 self.game 中。selectedIndex则是 game 位于 games 数组中的索引。该索引用于在 TableView 中用一个对钩图标来标出所选 cell。因此,self.game 必须在视图加载之前就必须指定一个值。这可以通过prepareForSegue 方法进行,这个方法在 viewDidLoad 之前调用。

修改 cellForRowAtIndexPathto 方法为:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView       dequeueReusableCellWithIdentifier:@"GameCell"];
        cell.textLabel.text = [games objectAtIndex:indexPath.row];
        if (indexPath.row == selectedIndex)
               cell.accessoryType =            UITableViewCellAccessoryCheckmark;
        else
               cell.accessoryType = UITableViewCellAccessoryNone;
        return cell;
}

   

在当前选定的游戏名称后面标志一个对钩图标。这对于用户来说应该是需要的。

修改 didSelectRowAtIndexPath方法如下:

- (void)tableView:(UITableView *)tableView    didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        if (selectedIndex != NSNotFound)      {
               UITableViewCell *cell = [tableView            cellForRowAtIndexPath:[NSIndexPath              
               indexPathForRow:selectedIndex inSection:0]];
               cell.accessoryType = UITableViewCellAccessoryNone;
        }
        selectedIndex = indexPath.row;
        UITableViewCell *cell =       [tableView cellForRowAtIndexPath:indexPath];
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
        NSString *theGame = [games objectAtIndex:indexPath.row];
        [self.delegate gamePickerViewController:self       didSelectGame:theGame];
}

   

首先反选改行。这将使单元格从高亮的蓝色变回正常的白色。然后移掉或添加单元格末尾的对钩标志(依赖于选中状态)。最终,将用于已选的游戏名称通过委托协议返回给委托对象。

运行程序进行测试。从游戏列表中点击一个游戏。该行末自动添加一个对钩。点击另一个游戏名称,对钩随之移动到新的选择。当你点击行时,窗口应该关闭,但什么也没发生。因为实际上我们还没有将委托对象连接进来。

#import "GamePickerViewController.h"

   

在 @interface 语句中添加协议:

@interface PlayerDetailsViewController : UITableViewController <GamePickerViewControllerDelegate>
   
在 PlayerDetailsViewController.m,添加prepareForSegue 方法:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.identifier isEqualToString:@"PickGame"]) {
               GamePickerViewController *gamePickerViewController =            segue.destinationViewController;
               gamePickerViewController.delegate = self;
               gamePickerViewController.game = game;
        }
}

   

就如我们前面所做的一样。这次的目标场景是 game picker 窗口。记住,该方法发生在GamePickerViewController 被初始化之后,视图被加载之前。

 “game” 变量是新加的。它是一个实例变量:

@implementation PlayerDetailsViewController {
        NSString *game;
}

   

该变量用于存储用户选定的游戏,因此我们可以把它放到 Player 对象中。我们可以给它一个默认值。通过initWithCoder 方法是个不错的选择:

- (id)initWithCoder:(NSCoder *)aDecoder {
        if ((self = [super initWithCoder:aDecoder])) {
               NSLog(@"init PlayerDetailsViewController");
               game = @"Chess";
        }
        return self;
}

   

如果你用过 nib 文件,initWithCoder 你应该很熟悉。在故事板中,这些方法同样存在:initWithCoder、awakeFromNib和viewDidLoad。你可以把故事板看成是 nib 文件的集合,再加上它们之间的转换和关系。但是故事板中的 view controller 仍然是用同样的方法编码和解码。

修改 viewDidLoad ,让 Game 单元格显示游戏名:

- (void)viewDidLoad {
        [super viewDidLoad];
        self.detailLabel.text = game;
}

接下来实现委托方法:

#pragma mark - GamePickerViewControllerDelegate  
- (void)gamePickerViewController:   (GamePickerViewController *)controller    didSelectGame:(NSString*)theGame {
        game = theGame;
        self.detailLabel.text = game;
        [self.navigationController popViewControllerAnimated:YES];
}

很简单,将新游戏名称赋给我们的实例变量 game 以及 cell 的Label,然后关闭游戏选择窗口。由于它是一个 Push 式 segue,从导航控制器的栈顶中将它弹出即可。

在 done 方法中,我们将已选游戏放入 Player 对象:

- (IBAction)done:(id)sender {
        Player *player = [[Player alloc] init];
        player.name = self.nameTextField.text;
        player.game = game;
        player.rating = 1;
        [self.delegate playerDetailsViewController:self didAddPlayer:player];
}

好了,我们的游戏选择窗口就完成了!

接下来的内容

这是 示例工程源代码。

恭喜你,现在你已经基本掌握了故事板的使用,能够用 segue 在多个view controller 中导航及转换。

关于更多 iOS 5 中故事板的学习内容,请查看我们的新书iOS 5 教程,其中还包含了:

  • 修改 PlayerDetailsViewController 以便对 Player 对象进行编辑。

  • 여러 개의 나가는 segue를 만드는 방법, ViewController를 재사용 가능하게 만들고 여러 개의 들어오는 segue를 처리하는 방법.

  • 공개 버튼, 동작 및 모든 이벤트에서 segue를 호출하는 방법.

  • 표준 푸시/모달 스타일 애니메이션을 사용하는 대신 사용자 정의 segue를 사용하세요!

  • 분할 뷰 컨트롤러 및 팝오버를 포함하여 iPad에서 스토리보드를 사용하는 방법입니다.

  • 마지막으로 앱에서 스토리보드를 수동으로 로드하고 여러 스토리보드를 사용하는 방법입니다.

위 내용은 iOS 5 스토리보드 소개(4) 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.