Heim  >  Artikel  >  Backend-Entwicklung  >  Erste Schritte mit iOS 5-Storyboards (4)

Erste Schritte mit iOS 5-Storyboards (4)

黄舟
黄舟Original
2017-01-20 10:26:281306Durchsuche

Verschieben Sie das Fenster „AddPlayer“

Ignorieren wir vorerst die Zeile „Spiel“ und berücksichtigen Sie nur den vom Benutzer eingegebenen Spielernamen.
Wenn der Benutzer auf die Schaltfläche „Abbrechen“ tippt, wird das Fenster geschlossen und alle vom Benutzer eingegebenen Daten gehen verloren. Dieser Teil ist in Ordnung. Wenn das Delegate-Objekt (Players-Fenster) die Nachricht „didcancel“ empfängt, wird das AddPlayer-Fenster einfach geschlossen.

Wenn der Benutzer die Schaltfläche „Fertig“ berührt, sollten wir ein neues Player-Objekt erstellen und seine Eigenschaften festlegen. Teilen Sie dem Delegate-Objekt dann mit, dass wir einen neuen Spieler hinzugefügt haben und er die Benutzeroberfläche aktualisieren soll.

Daher wird die done-Methode in PlayerDetailsViewController.m zu:

- (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];
}

Wir müssen die Header-Datei importieren:

#import "Player.h"

done-Methode erstellt jetzt ein neues Player-Objekt und sendet es an das Delegate-Objekt. Das aktuelle Delegationsprotokoll in der Datei PlayerDetailsViewController.h muss wie folgt geändert werden:

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

Die Methodendefinition „didSave“ wurde entfernt und wir haben sie ersetzt mit einer didAddPlayer-Methode. Der nächste Schritt besteht darin, diese Methode in

PlayersViewController.m zu implementieren:

  - (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];
}

Fügen Sie zunächst einen neuen Player zum Players-Array-Objekt hinzu . Weisen Sie dann die Tabellenansicht an, die neue Zeile (unten) hinzuzufügen, da die Tabellenansicht und die Datenquelle synchron bleiben müssen. Wir könnten auch [self.tableView reloadData] verwenden, aber es wäre besser, das Einfügen einer neuen Zeile zu animieren. UITableViewRowAnimationAutomatic ist eine neue Konstante, die in iOS 5 eingeführt wurde. Sie wählt automatisch die entsprechende Animation aus, je nachdem, wo Sie die Zeile einfügen, was sehr praktisch ist.

Führen Sie das Programm aus und Sie sollten in der Lage sein, neue Spieler zur Liste hinzuzufügen!

Sie vermuten möglicherweise, dass es Leistungsprobleme mit dem Storyboard gibt. Aber ein ganzes Storyboard auf einmal zu laden ist nichts Besonderes. Das Storyboard initialisiert nicht alle ViewController direkt, sondern nur den anfänglichen View Controller. Da es sich bei unserem ersten View-Controller um einen TabBarController handelt, werden auch die beiden darin enthaltenen View-Controller geladen (der Players View Controller und der zweite View-Controller).

Andere ViewController werden erst initialisiert, wenn Sie sie „trennen“. Sobald Sie diese ViewController schließen, werden sie freigegeben, sodass nur der aktuell verwendete ViewController im Speicher verbleibt, genau wie die von Ihnen verwendeten NIB-Dateien.

Lass uns ein Experiment machen. Fügen Sie diese Methode zu PlayerDetailsViewController.m hinzu:

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

Wir überschreiben die Methoden initWithCoder und dealloc, um einige Informationen auszugeben. Führen Sie das Programm erneut aus und öffnen Sie das Fenster „Player hinzufügen“. Sie können sehen, dass der ViewController bis zu diesem Zeitpunkt nicht zugewiesen wurde. Schließen Sie das Fenster (Schaltfläche „Abbrechen“ oder „Fertig“) und Sie können die Ausgabe von delloc sehen. Öffnen Sie das Fenster erneut und Sie können auch den von initWithCoder ausgegebenen Inhalt sehen. Dies zeigt vollständig, dass der Viewcontroller bei Bedarf geladen wird, genau wie bei der Verwendung der Nib-Methode.

Eine weitere Sache: Statische Zellen können nur in UITableViewController verwendet werden. Mit dem Storyboard-Editor können Sie statische Zellen in einer TableView in einem regulären UIViewController verwenden, jedoch nicht zur Laufzeit. Der Grund dafür ist, dass UITableViewController einen speziellen Datenquellenmechanismus für statische Zellen bereitstellt. Xcode gibt sogar eine Warnung aus, die Ihnen das Kompilieren eines solchen Projekts verbietet:

„Unzulässige Konfiguration: Statictable-Ansichten sind nur gültig, wenn sie in UITableViewController-Instanzen eingebettet sind“ (statische Tabellenansichten sind nur für UITableViewController gültig).

Vorlagenzellen können im regulären ViewController verwendet werden. Natürlich nicht in IB. Wenn Sie daher Vorlagenzellen und statische Zellen verwenden möchten, müssen Sie Storyboards verwenden.

Möglicherweise möchten Sie statische und dynamische Zellen gleichzeitig in einer Tabellenansicht verwenden, das SDK unterstützt dies jedoch nicht. Wenn Sie dies wirklich tun möchten, lesen Sie bitte hier nach.

Hinweis: Wenn Ihr Fenster zu viele statische Zellen hat – mehr als der Bildschirm aufnehmen kann – können Sie Maus- oder Trackpad-Bildlaufgesten verwenden, um die Ansicht im Storyboard-Editor zu teilen. Diese Funktion ist nicht sehr intuitiv, aber nützlich.

Spielauswahlfenster

Tippen Sie im Fenster „Spieler hinzufügen“ auf die Zelle „Spiel“. Daraufhin wird eine Liste geöffnet, in der der Benutzer ein Spiel auswählen kann. Daher muss ein Table View Controller hinzugefügt werden. Dieses Mal verwenden wir die Push-Methode anstelle der Modal-Methode.

Ziehen Sie einen neuen Table View Controller auf das Storyboard. Wählen Sie im Fenster „Spieler hinzufügen“ die Zelle „Spiel“ aus (beachten Sie, dass die gesamte Zelle ausgewählt ist, nicht die Beschriftung oben), halten Sie die Strg-Taste gedrückt und ziehen Sie sie auf den neuen TableViewController. Dadurch wird ein Übergang erstellt. Wählen Sie den Abschnitt vom Typ „Push“ und nennen Sie den Abschnitt

„PickGame“.

Doppelklicken Sie auf die Navigationsleiste und ändern Sie den Titel in „Spiel auswählen“. Der Stil der Vorlagenzelle ist auf „Basic“ und die Wiederverwendungs-ID auf „GameCell“ festgelegt, wie in der folgenden Abbildung dargestellt:

Erste Schritte mit iOS 5-Storyboards (4)

Erstellen Sie eine neue UITableViewController-Unterklasse mit dem Namen GamePickerViewController. Vergessen Sie nicht, den Identifier von TableViewController im Storyboard auf GamePickerViewController zu setzen.

先让我们为新场景准备一些数据。在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 窗口。

Erste Schritte mit iOS 5-Storyboards (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 对象进行编辑。

  • Wie Sie mehrere ausgehende Segmente erstellen, wie Sie Ihren ViewController wiederverwendbar machen und mehrere eingehende Segmente verarbeiten.

  • So rufen Sie den Übergang von der Offenlegungsschaltfläche, Geste und jedem Ereignis auf.

  • Benutzerdefinierter Übergang – anstatt nur Standardanimationen im Push-/Modal-Stil zu verwenden!

  • So verwenden Sie Storyboards auf dem iPad, einschließlich Split-View-Controller und Popover.

  • Abschließend erfahren Sie, wie Sie Storyboards manuell laden und mehrere Storyboards in der App verwenden.

Das Obige ist der Inhalt der iOS 5-Storyboard-Einführung (4). Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn