-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathios_dev_kurs_skript.tex
1439 lines (883 loc) · 134 KB
/
ios_dev_kurs_skript.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\documentclass[parskip=half, final]{scrreprt}
\input{include/variables}
\input{include/style}
\input{include/code_listing}
\renewcommand{\doctype}{Skript}
\renewcommand{\shortdoctype}{Skript}
\usepackage{verbatim}
\begin{document}
\maketitle
\tableofcontents
\chapter{Einführung}
\section{Über dieses Skript}
Dieses Skript wird im Verlauf des Semesters auf der Vorlesungswebseite \linkref{http://ios-dev-kurs.github.io/} kapitelweise zur Verfügung gestellt. Parallel dazu wird es für die Apps, die wir im Rahmen des Kurses erstellen werden, ein weiteres Dokument geben. Dieser \strong{App-Katalog} wird ebenfalls auf der Vorlesungsseite zu finden sein und enthält außerdem die Übungsaufgaben, die wöchentlich zu bearbeiten sind.
Die Kursinhalte werden sich thematisch an der Struktur des Skriptes orientieren und ihr könnt es als Referenz bei der Lösung der Übungsaufgaben verwenden. Mit der dokumentübergreifenden Suche (\keys{\cmd + F}) lässt sich gut nach Stichwörtern suchen.
Das Skript ist kein Tutorial, sondern ist sehr allgemein gehalten und erläutert die Grundlagen der Kursthemen. Im ergänzenden App-Katalog findet ihr hingegen Schritt-für-Schritt Anleitungen.
Die Inhalte basieren auf der Xcode Version \vxcode{} und dem iOS SDK \vios{}. Die meisten Erläuterungen und Screenshots lassen sich ebenso auf frühere Versionen von Xcode und dem iOS SDK beziehen, doch einige neuere Funktionen sind der aktuellen Version vorbehalten.
\section{Programmieren in Swift}
Bis Apple auf der WWDC im Juni 2014 die völlig neue Programmiersprache \emph{Swift} vorstellte, wurde Software für die iOS und Mac Plattformen fast ausschließlich in der auf C basierenden Programmiersprache \emph{Objective-C} geschrieben. Seitdem hat Swift bereits enormen Zulauf erhalten und wird Objective-C langfristig ersetzen.
Swift bedient sich vieler Konzepte moderner Programmiersprachen und legt besonderen Wert auf eine aussagekräftige Syntax und typsicheren, unmissverständlichen Code, der Laufzeitfehler vermeidet. Swift's Grammatik ist sehr klein gehalten und viele grundlegende Typen, Funktionen und Operatoren sind stattdessen in der Sprache selbst in der \emph{Swift standard library} definiert.
Noch immer befindet sich Swift stark in Entwicklung und neue Versionen werden häufig veröffentlicht. Statt in diesem Skript eine Einführung in diese Sprache zu geben, verweise ich auf das Buch \emph{The Swift Programming Language} von Apple, das sowohl online \linkref{https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/} als auch im iBooks Store \linkref{https://itunes.apple.com/de/book/swift-programming-language/id881256329?mt=11} immer in aktueller Version vorliegt und uns in diesem Kurs als Referenz dienen wird.
Zu Beginn des Kurses lernen wir die Grundlagen der objektorientieren Programmierung in Swift anhand dieses Buches und werden dann sehr schnell anfangen, unsere ersten iOS Apps zu schreiben.
\section{Dokumentationen und Referenzen}\label{sec:docs}
Zusätzlich zu der in Xcode integrierten \secref{sec:xcode_documentation} und online verfügbaren Dokumentation der Frameworks der iOS Plattform bietet Apple einige Ressourcen für iOS Developer an:
\begin{description}
\item[iOS Dev Center] \linkref{https://developer.apple.com/devcenter/ios/} ist Apple's Onlineplattform für iOS Entwickler. Hier ist auch das Member Center zur Accountverwaltung und das Provisioning Portal zur Verwaltung der Certificates und Provisioning Profiles \secref{sec:provisioning} zu finden.
\item[iOS Human Interface Guidelines (HIG)] \linkref{https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/} ist ein Dokument, das jeder iOS Developer gelesen haben sollte. Die hier besprochenen Richtlinien bezüglich der Gestaltung von Benutzeroberflächen auf der iOS Plattform sind sehr aufschlussreich und haben sicherlich ihren Teil zum Erfolg der iOS Geräte beigetragen. Ein entsprechendes Dokument gibt es auch für Mac \linkref{https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/}.
\item[iOS App Programming Guide] \linkref{https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/} stellt eine Übersicht über die Architektur von iOS Apps dar.
\item[WWDC Videos] \linkref{https://developer.apple.com/wwdc/videos/} werden während der jährlichen Worldwide Developer Conference veröffentlicht. Apple Entwickler führen sehr anschaulich in neue, grundlegende und fortgeschrittene Technologien und Methoden ein und geben Best-Practices.
\end{description}
Hier darf natürlich auch die Community-basierte Q\&A-Seite \strong{Stack Overflow \linkref{http://stackoverflow.com}} nicht unerwähnt bleiben, die bei Codefragen immer sehr hilfreich ist.
\chapter{Xcode}
\strong{Xcode} ist Apple's Integrierte Entwicklungsumgebung (IDE) und wird von allen Entwicklern verwendet, die native Apps für die iOS oder Mac Plattformen schreiben. Apple bietet online \linkref{https://developer.apple.com/library/mac/documentation/ToolsLanguages/Conceptual/Xcode_Overview/} eine umfassende Dokumentation über den Funktionsumfang von Xcode an.
Wir werden lernen, Xcode's zahlreiche Funktionen zu nutzen, um nicht nur effizient Programmcode zu schreiben, sondern auch unsere Programmierprojekte zu verwalten, Apps auf eigenen iOS Geräten zu testen, Benutzerinterfaces zu gestalten, Datenstrukturen zu erstellen und unsere Apps schließlich zu veröffentlichen.
IDE's anderer Systeme bieten häufig ein ähnliches Funktionsspektrum und der Umgang mit diesen ist somit leicht übertragbar. Natürlich kann Programmcode auch mit einem beliebigen Texteditor geschrieben werden, doch IDE's wie Xcode erleichtern die Programmierung häufig erheblich. Allein schon die umfangreiche Autovervollständigung vereinfacht das Schreiben von Code sehr und korrigiert Syntaxfehler.
\section{Workspaces \& Projekte}
Jedes Programmierprojekt wird in Xcode in Form eines \strong{Projekts} angelegt. Diese können in \strong{Workspaces} zusammengefasst werden. Letztendlich wird immer ein Workspace angelegt, auch wenn er nur ein Projekt enthält.
\subsection{Neue Projekte anlegen}
Mit \menu{File > New > Project...} oder \keys{\cmd + \shift + N} erstellen wir ein neues Projekt und wählen im erscheinenden Dialogfenster \abbref{img:new_project_dialogue1} ein Template. In diesem Dialogfenster finden wir mit \menu{OS X > Application > Cocoa Application} auch das Template, um Mac OS X Apps zu schreiben. Für iOS Apps verwenden wir die Templates unter \menu{iOS > Application}. Es kann prinzipiell mit einem beliebigen Template begonnen werden, diese unterscheiden sich nur in bereits vorkonfigurierten Programmelementen. Häufig ist es hilfreich, eines der Templates zu verwenden, das der Struktur der App entspricht, die wir programmieren wollen.
\includegraphicsc{img/new_project_dialogue1.png}{img:new_project_dialogue1}{Ein neues Xcode-Projekt anlegen}
Nach der Wahl des Templates werden weitere Optionen für das Projekt präsentiert \abbref{img:new_project_dialogue2}.
\includegraphicsc{img/new_project_dialogue2.png}{img:new_project_dialogue2}{Ein neues Projekt konfigurieren}
\begin{description}
\item[Product Name] identifiziert die resultierende App im Projekt. Es können später in einem Projekt weitere Produkte hinzugefügt werden \secref{sec:targets}. Diesen müssen unterschiedliche Product Names zugewiesen werden. Als Product Name wird häufig ein kurzer Codename des Projekts gewählt.
\item[Organization Name] wird in jeder erstellten Datei hinterlegt.
\item[Company Identifier] identifiziert den Ersteller der Produkte. Nach Konvention wird hier eine sog. \emph{Reverse DNS} verwendet mit dem Schema \str{com.yourcompany}. Verwendet in diesem Kurs bitte immer \str{de.uni-hd.<deinname>} als Company Identifier.
\item[Bundle Identifier] setzt sich nach dem Reverse DNS Schema aus Company Identifier und Product Name zusammen und hat dann die Form \str{com.<yourcompany>.<productname>} bzw. in unserem Kurs \str{de.uni-hd.<deinname>.<productname>}. Der Bundle Identifier identifiziert eine App eindeutig im App Store und auf Apple's Servern.
\item[Language] bietet die Auswahl zwischen Swift und Objective-C als Basis-Programmiersprache für das Projekt. Code der anderen Sprache kann ebenfalls eingebunden werden.
\item[Devices] gibt die Möglichkeit, die App für iPhone, iPad oder Universal zu konfigurieren.
\item[Use Core Data] fügt dem Projekt direkt die benötigten Elemente zur Core Data Integration hinzu. Dies ist eine Datenbanktechnologie auf Basis von SQL \secref{sec:coredata}.
\end{description}
Anschließend kann ein Speicherort für den Projektordner gewählt werden. Hier besteht zusätzlich die Möglichkeit, direkt ein Git Repository für das Projekt anzulegen. Git werden wir zu einem späteren Zeitpunkt noch ausführlich thematisieren \secref{sec:git}.
\subsection{Targets und Products}\label{sec:targets}
Ein Projekt kann zur Entwicklung mehrerer Apps dienen, die sich Programmcode teilen können.
Das Ergebnis des Compilers, also eine fertige App, wird \strong{Product} genannt. Zu jedem Product gehört genau ein \strong{Target}. Das Target stellt die Repräsentation des Products in Xcode dar und gibt dem Compiler Auskunft über alle Konfigurationen, referenzierte Dateien, benötigte Frameworks und sonstige Informationen die zur Kompilierung benötigt werden.
Wenn wir ein neues Projekt erstellen, wird automatisch ein Target mit den gegebenen Informationen generiert. Mit \menu{New > Target} kann außerdem jederzeit ein neues Target hinzugefügt und konfiguriert werden. Jedes Target muss einen eindeutigen Bundle Identifier besitzen. Anschließend können wir die Targets separat kompilieren und so jeweils ein Product erhalten.
\subsection{Projekt- und Target-Konfiguration}\label{sec:projkonfig}
Wenn das Projekt selbst im Project Navigator \secref{sec:navigator} ausgewählt ist, wird im Editor-Bereich die Projekt- und Target-Konfiguration angezeigt \abbref{img:xcode_projekttargetconfig}. Mit der Schaltfläche oben links kann das Projekt oder ein Target ausgewählt werden.
\includegraphicsc{img/xcode_projekttargetconfig.png}{img:xcode_projekttargetconfig}{Die Projekt- und Targetkonfiguration wird angezeigt, wenn das Projekt links im Project Navigator ausgewählt ist}
Hier können alle wichtigen Einstellungen bearbeitet werden, die die App als Ganzes betreffen. Wählen wir ein Target aus, kann aus den Tabs in der oberen Leiste gewählt werden:
\subsubsection{General}
Hier ist eine Auswahl wichtiger Optionen zur Konfiguration unserer App zu finden, von denen viele direkt mit dem jeweiligen Eintrag im Tab 'Info' korrespondieren.
\begin{description}
\item[Bundle Identifier] wurde bereits besprochen und kann hier verändert werden. Wenn der Bundle Identifier einen hellgrauen, nicht editierbaren Teil enthält (meist der Product Name), dann wird dieser Teil aus einer Variable entnommen. Er kann dann im Tab \emph{Info} editiert werden. Verwendet bitte das Schema \str{de.uni-hd.<deinname>.<productname>} für Apps, die unserem Developer Team zugeordnet sind.
\item[Version/Build] gibt die aktuelle Versionsnummer an.
\item[Team] bestimmt die Zugehörigkeit der App zu einem Developer Team, sodass Xcode das richtige Provisioning Profile zur Signierung der App auswählen oder ein passendes Provisioning Profile erstellen kann \secref{sec:testondevice}.
\item[Deployment Target] bestimmt die iOS Version, die für die Installation der App auf dem Zielsystem \strong{mindestens} installiert sein muss. Zusätzlich gibt es im \emph{Info} Tab die \emph{Base SDK} Einstellung. Diese gibt an, für welche iOS Version die App kompiliert wird. Letztendlich bedeutet dies: Im Code können nur Features verwendet werden, die in der Base SDK Version (meist die neueste iOS Version) enthalten sind. Ist die Deployment Target Version geringer (um auch ältere Geräte zu unterstützen), so muss aufgepasst werden, dass bei neueren Features im Code immer zuerst deren Verfügbarkeit geprüft wird.
\item[Devices] gibt an, ob die App nur für iPhone \emph{oder} iPad oder Universal für beide Geräte entwickelt wird.
\item[Main Interface] bestimmt die Interface-Datei, die beim Starten der App geladen wird. Diese Option hat weitreichende Auswirkungen auf die initiale Startsequenz der App, die wir noch thematisieren werden \secref{sec:launchprocess}. In den meisten Fällen sollte hier die Storyboard-Datei ausgewählt werden.
\item[App Icons / Launch Images] referenziert die jeweiligen Bilddateien. Diese sollten für optimale Performance in einem sog. \emph{Asset Catalog} zusammengefasst werden.
\end{description}
\subsubsection{Capabilities}
Einige häufig verwendete Features von iOS Apps, für deren Verwendung ansonsten einige Konfigurationsschritte notwendig wären, können hier einfach aktiviert werden. Dazu gehören bspw. Services wie GameCenter, iCloud und In-App-Purchase. Wird die Schaltfläche rechts aktiviert, werden die benötigten Konfigurationen im Projekt vorgenommen. Alle Veränderungen, die bei der Aktivierung des Features ausgeführt werden, sind hier aufgeführt.
\subsubsection{Info}
Dieser Tab ist hauptsächlich eine Repräsentation der \filename{Info.plist}-Datei. Alle Einstellungen, die nicht den Compiler betreffen, sondern dem ausführenden Gerät zur Verfügung gestellt werden, werden in dieser Datei gespeichert. Die meisten Optionen im \emph{General}-Tab verändern direkt die Einträge dieser Datei. Mittlerweile muss hier nur noch selten manuell etwas geändert werden.
\subsubsection{Build Settings}
Hier wird der Compiler konfiguriert. Beispielsweise kann der Product Name hier verändert werden und die Base SDK Version angepasst werden, wenn nicht für \emph{Latest iOS}, sondern eine vorherige iOS Version kompiliert werden soll. Auch die hier zu findenden Optionen sollten mittlerweile nur noch selten benötigt werden.
\section{Benutzerinterface}
Xcode's Interface ist in die in der Abbildung farbig markierten Bereiche aufgeteilt \abbref{img:xcode_interface}.
\includegraphicsc{img/xcode_interface.jpg}{img:xcode_interface}{Xcode's Interface}
\subsection{Darstellungsmodi}
Rechts in der Toolbar können wir mit sechs Bedienelementen die Darstellung des Xcode-Fensters anpassen.
Die ersten drei Buttons beziehen sich auf den Editor:
\begin{description}
\item[Standard-Editor] zeigt einen großen Editor-Bereich zur Betrachtung einer einzelnen Ansicht an.
\item[Assistant-Editor] teilt den Editor-Bereich in zwei Ansichten. Auf der linken Seite befinden sich die geöffnete Datei, während die rechte Seite eine sinnvolle zugehörige Ansicht zeigt. Wir verwenden hauptsächlich diese Option und werden noch lernen, sie zu verwenden.
\item[Version-Editor] ersetzt den Assistenten auf der rechten Seite mit einer Ansicht der Änderungshistorie der Datei links. Verwendet das Projekt ein Git Repository, werden hier die Commits angezeigt, die die Datei betreffen. Bezüglich Git und Versionskontrolle wird es später eine Einführung geben \secref{sec:git}.
\end{description}
Mit den weiteren drei Buttons können die Bereiche \strong{Navigator}, \strong{Debug} und \strong{Inspector} ein- und ausgeblendet werden.
Es können ebenfalls mit \keys{\cmd + T} weitere (browserähnliche) Tabs geöffnet werden.
\subsection{Build \& Run}\label{sec:buildandrun}
In der Toolbar finden wir links die Bedienelemente \emph{Build \& Run}, \emph{Stop} und eine Targetauswahl.
Hier kann das zu kompilierende Target und das Zielsystem ausgewählt werden. Haben wir ein gültiges iOS Gerät angeschlossen, wird dieses an erster Stelle angezeigt, andernfalls erscheint \emph{iOS Device} und wir können einen iOS Simulator auswählen.
Mit \emph{Build \& Run} starten wir den Compiler, woraufhin das gewählte Target kompiliert und das resultierende Product, also die App, auf dem gewählten Zielsystem ausgeführt wird. \emph{Stop} beendet den Prozess. Im Menü \menu{Product} stehen noch weitere Optionen zur Verfügung. Da wir diese sehr häufig verwenden werden ist es sinnvoll, sich die Tastenkombinationen einzuprägen:
\begin{description}
\item[Build] \keys{\cmd + B} Startet den Compiler, ohne dass das Product anschließend ausgeführt wird. Diese Option ist hilfreich, um kurz die Ausführbarkeit des Targets zu prüfen und Fehler zu korrigieren.
\item[Build \& Run] \keys{\cmd + R} Kompiliert das Target und führt das resultierende Product auf dem Zielsystem aus.
\item[Stop] \keys{\cmd + .} Beendet den aktiven Prozess.
\item[Clean] \keys{\cmd + \shift + K} Entfernt kompilierte Build-Dateien und führt zu einer vollständigen Neukompilierung beim nächsten \emph{Build} Aufruf. Da Xcode bereits verarbeitete Dateien wiederverwendet, solange sie nicht verändert wurden, löst diese Option manchmal Probleme, wenn Dateien außerhalb von Xcode bearbeitet wurde (bspw. Bilddateien).
\item[Archive] Kompiliert das Target und erstellt ein Archiv. Dieses kann anschließend verwendet werden, um die App an Tester zu verteilen oder im App Store zu veröffentlichen.
\end{description}
\subsection{Navigator}\label{sec:navigator}
Der \strong{Navigator} (blau) dient zur Übersicht über die Projektelemente. Es kann zwischen acht Tabs gewählt werden, die über die Tastenkombinationen \keys{\cmd + 1} bis \keys{\cmd + 8} erreichbar sind:
\begin{enumerate}
\item \strong{Project Navigator}: Übersicht über die Projektdateien
\item Symbol Navigator: Übersicht über alle Programmcodeelemente des Projektes
\item \strong{Find Navigator}: Projektübergreifende Suche
\item \strong{Issue Navigator}: Übersicht aller Warnung und Fehler des Compilers
\item Test Navigator: Übersicht aller erstellten automatischen Tests
\item Debug Navigator: Übersicht der Situation, wenn eine App läuft oder zur Laufzeit angehalten wird
\item Breakpoint Navigator: Liste der Breakpoints
\item Log Navigator: Liste der letzten Output- und Compiler-Logs
\end{enumerate}
Wir verwenden hauptsächlich den Project Navigator, um zwischen den Dateien unseres Projekts zu wechseln. Der Find Navigator bietet sowohl eine projektübergreifenden Suche als auch eine sehr hilfreiche \emph{Find \& Replace} Funktion. Zur Laufzeit einer App wird der Debug Navigator wichtig, der sowohl einige Geräteinformationen anzeigt (bspw. CPU- und Speicherauslastung), als auch eine Übersicht über die laufenden Operationen, wenn die App angehalten wird.
\subsection{Editor}
Der \strong{Editor} (rot) wird je nach geöffnetem Dateityp den Editor zum Bearbeiten der jeweiligen Datei zeigen.
Hier schreiben wir unseren Code. Es stehen viele hilfreiche Funktionen zur Verfügung, mit denen das Schreiben effizienter wird und Fehler schon vor dem Kompilieren erkannt und korrigiert werden können.
\subsubsection{Autovervollständigung}
Xcode indexiert sowohl Apple's Frameworks als euren eigenen Code und besitzt somit ein umfassendes Verständnis der verwendeten Symbole. Sobald du zu tippen beginnst, werden Vorschläge eingeblendet, die dem aktuellen Kontext entsprechen \abbref{img:xcode_autocomplete}. Die Indexierung ist so vollständig, dass nahezu kein Objective-C Codesymbol komplett ausgeschrieben wird. Stattdessen kannst du meist nach den ersten Buchstaben beginnen, die Vervollständigung zu nutzen. Dabei werden folgende Tasten verwendet:
\begin{description}
\item[Escape] Blendet die Vorschläge aus oder ein.
\item[Tab] Vervollständigt das Symbol bis zur nächsten uneindeutigen Stelle
\item[Enter] Vervollständigt das gesamte Symbol.
\end{description}
\includegraphicsc{img/xcode_autocomplete.png}{img:xcode_autocomplete}{Der meiste Code wird mit der Autovervollständigung geschrieben}
In den meisten Fällen gibt es ein Symbol nicht, wenn es von der Autovervollständigung nicht vorgeschlagen wird! Das betrifft auch selbstgeschriebenen Code.
Swift ist eine sehr deskriptive Programmiersprache und Symbole in Apple's Frameworks sind nach Konventionen benannt. So können mit etwas Übung und Hilfe der Autovervollständigung auch unbekannte Symbole gefunden werden, ohne erst die Dokumentation zu durchsuchen. Suchen wir beispielsweise eine bestimmte Konstante, die das Verhalten einer Animation eines \objcinline{UIView}-Objekts bestimmt, so ist es typisch, die Autovervollständigung folgendermaßen zu verwenden:
\begin{enumerate}
\item Wir beginnen mit dem Tippen von \objcinline{UIVi}, verwenden die Tab-Taste, um zur nächsten uneindeutigen Stelle zu springen und erhalten \objcinline{UIView}.
\item In der Liste der Vorschläge sehen wir unter anderem Symbole, die mit \objcinline{UIViewAnimation} beginnen. Mit den Pfeiltasten wählen wir eines aus und drücken wieder Tab.
\item In dieser Weise ist es sehr einfach, die möglichen Optionen der Animation zu finden, ohne sie zuvor zu kennen \abbref{img:xcode_autocomplete2}. Mit den Pfeiltasten können wir die gewünschte Option auswählen und mit der Enter-Taste einfügen.
\end{enumerate}
\includegraphicsc{img/xcode_autocomplete2}{img:xcode_autocomplete2}{Auch unbekannte Symbole können mit der Autovervollständigung gefunden werden}
\subsubsection{Fehlerkorrektur}
Viele häufig auftretende Syntaxfehler werden schon bei der Codeeingabe von Xcode erkannt und können sofort korrigiert werden \abbref{img:xcode_fixit}. Dazu gehören fehlende Steuerzeichen wie Semikolons, aber auch komplexere Fehler.
\includegraphicsc{img/xcode_fixit.png}{img:xcode_fixit}{Xcode's Fehlerkorrektur erkennt und behebt Syntaxfehler}
\subsubsection{Integrierte Dokumentation \& Links}\label{sec:quickdef}
Mit einem \keys{\Alt}-Klick auf ein Symbol im Code kann jederzeit eine kurze Definition desselben angezeigt werden, sofern es in der Dokumentation enthalten ist \abbref{img:xcode_quickdef}.
\includegraphicsc{img/xcode_quickdef.png}{img:xcode_quickdef}{Alt-Klick auf ein Symbol zeigt eine kurze Definition}
Ein \keys{\cmd}-Klick wirkt wie ein Link im Internet und führt je nach Kontext direkt zur Deklaration des Symbols im Projekt oder zum Ziel des Aufrufs.
\subsubsection{Jump bars \& Open Quickly}
Die Leiste oben im Editor-Bereich (genannt Jump bar) dient der Navigation und zeigt den Pfad der geöffneten Datei im Projekt an. Mit einem Klick auf ein Pfadsegment kann auf die Dateistruktur zugegriffen werden. Sehr praktisch ist, dass hier sofort etwas getippt werden kann, woraufhin die Liste gefiltert wird. Das gilt auch für das letzte Pfadsegment, das die Position im Code der geöffneten Datei anzeigt und der schnellen Navigation innerhalb deren Symbole dient.
\strong{Tipp:} Ist im Code an einer Stelle der Ausdruck \swiftinline{// MARK: Section Title} zu finden, so erscheint \emph{Section Title} als Gliederung in dem Menü der Jump bar. Ein Minus erzeugt eine horizontale Line: \objcinline{// MARK: - Section Title}.
Mit der \emph{Open Quickly} Funktion \keys{\cmd + \shift + O} kann ebenfalls schnell navigiert werden. Es öffnet sich ein Eingabefeld, mit dem auf die gesamte Indexierung des Projekts zugegriffen werden kann.
\subsubsection{Assistent}
Im Assistant-Mode wird der Editor-Bereich zweigeteilt. Während der linke Teil die geöffnete Datei anzeigt, kann im rechten Teil mit Klick auf das erste Segment der Jump bar eine zugehörige Datei geöffnet werden. Häufig werden wir den Assistent verwenden, um Interface und Code nebeneinander anzuzeigen und miteinander zu verbinden.
\subsubsection{Breakpoints}\label{sec:breakpoints}
Mit einem Klick auf eine Zeilennummer in der Leiste rechts vom Editorbereich kann ein Breakpoint in dieser Codezeile gesetzt werden, sodass die App bei der Ausführung an dieser Stelle angehalten wird \secref{sec:debugarea}.
\subsection{Inspektor}\label{sec:inspektor}
Am rechten Bildschirmrand kann der Inspektor eingeblendet werden, dessen Tabs ähnlich wie beim Navigator mit den Tastenkombinationen \keys{\cmd + \Alt + 1} bis \keys{\cmd + \Alt + 6} erreichbar sind. Während Code geschrieben wird, sind hier nur zwei Tabs verfügbar:
\begin{enumerate}
\item File Inspector: Optionen bezüglich der im Editor geöffneten Datei
\item Quick Help Inspector: Kurze Dokumentation des ausgewählten Symbols im Editor, ähnlich den Informationen, die durch \keys{\Alt} - Klick auf das Symbol erreichbar sind
\end{enumerate}
Zur Codeeingabe wird der Inspektor meist ausgeblendet, doch für die Konfiguration von Benutzeroberflächen mit dem Interface Builder ist er unverzichtbar \secref{sec:ib}.
\subsection{Debug-Bereich \& Konsole}\label{sec:debugarea}
Der Debug-Bereich im unteren Bildschirmbereich wird zur Laufzeit einer App verwendet.
In der Konsole werden Ausgaben angezeigt, die von der App generiert werden. Wir werden noch lernen, diese zu nutzen, um den ausgeführten Code während der Laufzeit nachzuvollziehen. Außerdem wird hier die exakte Situation der App angezeigt, wenn diese zur Laufzeit angehalten wird \abbref{img:xcode_debugger}.
\includegraphicsc{img/xcode_debugger.png}{img:xcode_debugger}{Im Debugger werden Konsolenausgaben und Situation der App angezeigt}
Die beiden Bereiche können mit den beiden Schaltern in der rechten unteren Ecke umgeschaltet werden.
In der Leiste im oberen Teil des Debug-Bereichs sind folgende Kontrollelemente zu finden:
\begin{description}
\item[Breakpoints] aktiviert/deaktiviert die Breakpoints im gesamten Projekt \secref{sec:breakpoints}.
\item[Pause/Resume] stoppt die Ausführung App oder startet diese wieder.
\item[Step over] führt die im Editor markierte Codezeile aus.
\item[Step into] folgt dem im Editor markierten Ausdruck, bspw. einem Methodenaufrufen.
\item[Step out] führt den aktuellen Methodenaufruf vollständig aus und zeigt ihn anschließend an.
\end{description}
Während die App angehalten ist, können im Debug-Bereich und im Editor-Bereich relevante Symbole inspiziert werden. Fährt man mit dem Curser über ein Symbol, können Informationen über den aktuellen Status desselben angezeigt und in der Konsole ausgegeben werden. Zusätzlich zu herkömmlichen Werten der Variablen können sogar Bilder, die in den ausführenden Speicher geladen wurden, mit Quicklook angezeigt werden.
Da Code selten sofort so funktioniert wie wir möchten, ist das Debugging eine wichtige Komponente der Programmierung. Wir werden uns daher noch genauer mit den verschiedenen Methoden beschäftigen um Fehler im Programmcode zu finden und auch die Speicherauslastung und Performance unserer Apps zu optimieren.
\section{Interface Builder}\label{sec:ib}
So gut euer Code auch geschrieben sein mag - Benutzer werden nur die Benutzeroberfläche oder User Interface (UI) eurer Apps zu sehen bekommen. Diese ist ein wichtiger Bestandteil der iOS Plattform und wir werden uns noch mit einigen Methoden beschäftigen, sinnvoll gestaltete und dynamische UIs zu erstellen.
In Xcode ist, wie in vielen IDEs, ein graphischer Editor genannt \emph{Interface Builder (IB)} integriert, der bei der UI-Gestaltung hilft. Alles, was mit dem Interface Builder erstellt wird, kann natürlich auch stattdessen in Code geschrieben werden. Doch bereits bei simplen UIs vereinfacht IB die Gestaltung um ein Vielfaches und hilft mit Konzepten wie Storyboarding und Auto Layout zusätzlich bei der Strukturierung der gesamten Benutzerführung und der dynamischen Anpassung des UI's an verschiedene Displaygrößen und -orientierungen. Für komplexere UIs werden wir die Vorteile des Interface Builders daher schnell zu schätzen lernen.
Bei Datein mit der Endung \filename{.xib} oder \filename{.storyboard} wird Xcode's Editor-Bereich automatisch mit dem Interface Builder ersetzt. Wir verwenden dann meist einen neuen Tab mit angepasster Konfiguration, blenden den Navigator- und Debug-Bereich aus und den Inspektor-Bereich ein \abbref{img:xcode_ib}. Im Editor-Bereich wird situationsbedingt der Standard- oder Assistant-Editor verwendet. Im IB-Modus wird im Editor-Bereich dann auf den linken Seite eine Navigationsleiste eingeblendet, die die Elemente in der geöffneten Datei anzeigt. Diese kann mit der Schaltfläche unten ein- und ausgeblendet werden.
\includegraphicsc{img/xcode_ib.png}{img:xcode_ib}{Um möglichst viel Platz zur UI-Gestaltung zu erhalten, verwenden wir für den Interface Builder eine angepasste Konfiguration}
Im unteren Bereich des Inspektors ist die \strong{Object Library} zu finden. Wir können Objekte aus dieser Liste auf ein Element im Interface Builder ziehen und es so hinzufügen. Zu diesen Objekten gehören sowohl Interfaceelemente wie Buttons und Labels als auch strukturgebende Elemente wie Navigation Controller. Diese Objekte lernen wir bei der Erstellung unserer Apps noch kennen.
Anschließend können die Objekte in der Navigationsleiste links oder im Editor ausgewählt werden und mit dem Inspektor konfiguriert werden. Sind mehrere Elemente im Editor übereinander positioniert, hilft ein \keys{\shift}-Klick auf die entsprechende Stelle. Es wird eine Liste der unter dem Curser angeordneten Objekte angezeigt, aus dem das Gesuchte ausgewählt werden kann.
\subsection{XIBs \& Storyboards}
Bevor das \strong{Storyboarding}-Konzept eingeführt wurde, wurden für die Interfacegestaltung einzelne \filename{.xib}-Dateien verwendet (auch aufgrund ihrer ursprünglichen Endung \emph{NIB-Dateien} genannt). Storyboards hingegen vereinen meist die Interfaceelemente der gesamten App und auch ihre benutzerführenden Verbindungen in einer \filename{.storyboard}-Datei.
Das UI einer App wird dann hauptsächlich in seinem Storyboard konfiguriert, während es im Code mit Inhalten gefüllt und gesteuert wird.
In einem Storyboard stellen dann einzelne \strong{Scenes} die verschiedenen Ansichten dar, die dem Benutzer präsentiert werden. Eine der Scenes kann als \strong{Inital Scene} gekennzeichnet werden und wird dem Benutzer zuerst präsentiert.
Zwischen den Scenes vermitteln \strong{Segues}. Diese können eine Verbindung zwischen Scenes darstellen, bspw. wenn durch eine Benutzereingabe eine andere Scene angezeigt werden soll.
\subsection{Inspektor im IB-Modus}\label{sec:ibinspector}
Im IB-Modus stehen im Inspektor zusätzlich zum File- und Quick-Help-Inspektor \secref{sec:inspektor} vier weitere Tabs zur Verfügung, mit denen ein ausgewähltes Objekt im Interface Builder konfiguriert wird:
\begin{description}
\item[Identity Inspector] dient dem Einstellen der Identität des Objekts, also hauptsächlich seiner Klasse.
\item[Attributes Inspector] zeigt alle Konfigurationsoptionen bezüglich der Eigenschaften des Objekts nach Subklasse sortiert an. Dazu gehören bspw. Hintergrund- und Textfarben.
\item[Size Inspector] enthält Optionen zu Größe und Position des Objekts.
\item[Connections Inspector] zeigt die verbundenen IBOutlets und IBActions des Objekts an \secref{sec:iboutletsibactions}.
\end{description}
\subsection{IBOutlets \& IBActions}\label{sec:iboutletsibactions}
\strong{Hinweis:} Für diesen Abschnitt sind Kenntnisse über \emph{Attribute} und \emph{Methoden} der objektorientierten Programmierung notwendig.
\subsubsection{IBOutlets}
Haben wir unser UI im Interface Builder konfiguriert, möchten wir häufig im Code auf die verwendeten Objekte zugreifen. Dazu verwenden wir sog. \strong{IBOutlets}. Dies sind speziell gekennzeichnete \strong{Attribute} eine Klasse:
\begin{swiftcode}
@IBOutlet var label: UILabel! // Dieses Attribut ist als IBOutlet gekennzeichnet.
\end{swiftcode}
In der XIB oder dem Storyboard können wir dann eine Verbindung zwischen dem Objekt und dem IBOutlet herstellen \abbref{img:xcode_iboutlet}.
\includegraphicsc{img/xcode_iboutlet.png}{img:xcode_iboutlet}{IBOutlets verbinden Interfaceelemente mit Properties im Code}
Zur Laufzeit der App wird diesem Attribut dann das verbundene Objekt als Wert zugewiesen, sodass im Code darauf zugegriffen werden kann. Hätten wir das Objekt im Code erstellt, wäre eine IBOutlet-Verbindung also äquivalent zu folgendem Code:
\begin{swiftcode}
// Sei theLabel das zuvor im Code erstellte Interface-Objekt
self.label = theLabel
\end{swiftcode}
Um sehr einfach IBOutlets zu erstellen, wechseln wir in den Assistant-Editor. Im Editor Bereich wird dann rechts der Assistent angezeigt. Hier können wir oben in der Jump bar \menu{Automatic > Klassenname.swift} wählen, also die zugehörige Klasse, in der das Attribut deklariert wurde. Mit gedrückter \keys{\ctrl}-Taste können wir nun eine Verbindung zwischen dem Interfaceelement und dem Attribut ziehen \abbref{img:xcode_iboutlet}.
Alternativ kann der Connection Inspector des Objekts \secref{sec:ibinspector} oder ein Rechtsklick auf das Objekt verwendet werden und ausgehend von dem Kreissymbol neben \emph{New Referencing Outlet} eine Verbindung gezogen wird. Wird die Verbindung nicht zu einem existierenden IBOutlet im Code, sondern auf eine leere Zeile gezogen, wird automatisch eine mit \objcinline{IBOutlet} gekennzeichnete Property erstellt. Anstatt den Assistant-Editor zu verwenden kann das Ziel der Verbindung auch im Interface Builder gesucht werden, also bspw. in der Dokumentübersicht links.
Als IBOutlets markierte Attribute werden häufig als \swiftinline{private} markiert, wenn nur innerhalb der Klasse auf die Interfaceelemente zugegriffen werden muss. Da sie außerdem meist automatisch geladen, mit dem Attribut verknüpft und dann nicht mehr entfernt werden, erhalten sie zusätzlich die Markierung \swiftinline{weak} und werden als \emph{Implicitly Unwrapped Optional} deklariert:
\begin{swiftcode}
@IBOutlet private weak var label: UILabel!
\end{swiftcode}
\subsubsection{IBActions}
\strong{IBActions} funktionieren ähnlich wie IBOutlets und stellen eine Verbindung zu \strong{Methoden} einer Klasse her.
Einige Objekte stellen sog. \strong{Events} zur Verfügung, die in bestimmten Situationen ausgelöst werden. Dazu gehören bspw. Subklassen von \swiftinline{UIControl}, also u.a. \swiftinline{UIButton}, die das Event \emph{Touch Up Inside} auslösen, wenn der Benutzer seinen Finger im Bereich des Objekts anhebt.
Diese Events können mit Methoden im Code verbunden werden, die mit \swiftinline{@IBAction} gekennzeichnet sind \abbref{img:xcode_ibaction}:
\begin{swiftcode}
@IBAction func buttonPressed(sender: UIButton) { } // Diese Methode ist als IBAction gekennzeichnet.
\end{swiftcode}
\includegraphicsc{img/xcode_ibaction.png}{img:xcode_ibaction}{IBActions verbinden Events mit Methoden im Code}
Die verbundene Methode wird dann ausgeführt, wenn das entsprechende Event ausgelöst wird. Das auslösende Objekt wird der Methode als Parameter \swiftinline{sender} übergeben.
Die Zuweisung einer IBAction-Verbindung ist dann äquivalent zu folgendem Code:
\begin{swiftcode}
// Sei theButton das das Event auslösende Objekt und theReceiver das Objekt, das die Methode buttonPressed: implementiert
theButton.addTarget(theReceiver, action: "buttonPressed:", forControlEvents: .TouchUpInside)
\end{swiftcode}
\section{Dokumentation}\label{sec:xcode_documentation}
Die Dokumentation von Apple's Frameworks ist exzellent mit Xcode verknüpft und sollte bei Unklarheiten immer als erste Referenz verwendet werden. In der immer verfügbaren Kurzdefinition per \keys{\Alt}-Klick auf ein beliebiges Symbol im Code, ist immer ein Verweis auf die ausführliche Dokumentation enthalten \secref{sec:quickdef}. Ein Klick darauf öffnet den entsprechenden Abschnitt der Dokumentation im separaten Documentation-Fenster \abbref{img:documentation}.
Dieses ist außerdem immer mit dem Tastenkürzel \keys{\cmd + \Alt + ?} (bzw. \keys{\cmd + \Alt + \shift + ß} auf deutschen Tastaturen) erreichbar.
In dem Such-Eingabefeld oben kann selbstverständlich nach beliebigen Symbolen gesucht werden, während mit den Schaltflächen rechts davon die beiden Seitenleisten 'Bookmarks' und 'Overview' ein- und ausgeblendet werden können.
\includegraphicsc{img/xcode_documentation.png}{img:documentation}{Xcode's integrierte Dokumentation ist beispielhaft strukturiert und sollte immer als erste Referenz verwendet werden}
Die Dokumentation ist außerdem online in der iOS Developer Library \linkref{https://developer.apple.com/library/ios/} verfügbar. Eine Google-Suche nach dem Klassen- oder Symbolnamen führt meist direkt dorthin. Apple bietet online zusätzlich noch viele weitere Ressourcen für iOS Developer an \secref{sec:docs}.
\section{Testen auf iOS Geräten}\label{sec:testondevice}
Der Simulator eignet sich sehr gut zum Testen eurer Apps. Er läuft auf der Architektur eures Mac's und ist daher in den meisten Fällen deutlich schneller als ein iOS Gerät, verfügt jedoch nicht über alle Funktionen eines solchen. Außerdem gibt es einen großen Unterschied zwischen der Bedienung einer App mit Touchpad oder Maus im Vergleich zu einem Touchscreen. Beispielsweise wird die Größe und Position von Schaltflächen beim Testen auf dem Simulator häufig falsch eingeschätzt, da ein Finger auf dem Touchscreen sehr viel ungenauer tippen kann als mit dem Curser geklickt wird und häufig Teile des Bildschirms verdeckt \abbref{img:xcode_sliderpos}.
\includegraphicsc{img/xcode_sliderpos.png}{img:xcode_sliderpos}{Unter einer Schaltfläche positionierte Interfaceelemente sind bei der Bedienung meistens vom Finger bedeckt. Solche Probleme werden häufig erst beim Testen auf realen iOS Geräten entdeckt.}
Auf dem Simulator kann somit kein wirklichkeitsgetreues Testen stattfinden und es ist unumgänglich, Apps auch auf einem realen iOS Gerät zu testen.
Xcode kann Apps nur auf Geräten installieren, deren Softwareversion mit der jeweiligen Version von Xcode kompatibel ist. Bei neueren Versionen von Xcode lassen sich unter \menu{Preferences > Downloads} ältere iOS SDKs zusätzlich installieren, um diese Versionen zu unterstützen und als Base SDK \secref{sec:projkonfig} zu verwenden. Die Umkehrung gilt jedoch nicht: Geräte mit neueren Softwareversionen als die installierte iOS SDK Version können nicht zum Testen verwendet werden.
\subsection{Der Provisioning Prozess}\label{sec:provisioning}
\strong{Hinweis:} Dieser Abschnitt stellt die Grundlagen des Provisioning Prozesses dar. Xcode vereinfacht diesen Vorgang mittlerweile sehr. Zum Testen von Apps auf iOS Geräten ist nicht einmal mehr ein Apple Developer Account notwendig, sodass dieser Abschnitt erst relevant wird, wenn ihr mit dem App Store arbeitet.
Damit Apps auf iOS Geräten installiert werden können, müssen diese digital signiert werden. So wird sichergestellt, dass die App von einer vertrauenswürdigen, bei Apple registrierten Quelle stammen und ausführbarer Code nicht verändert wurde.
Der Developer Mac erstellt zunächst eine \strong{Signing Identity}, die aus einem Public-Private-Key-Paar besteht und im Schlüsselbund (Keychain) eures Macs gespeichert wird. Mit dem Public Key wird dann ein \strong{Certificate} angefordert, das an Apple und das Developer Team übermittelt und bestätigt wird. Dieses wird anschließend ebenfalls im Schlüsselbund gespeichert. Es wird zwischen \strong{Development Certificates}, die einen einzelnen Entwickler identifizieren und das Testen von Apps auf dessen Geräten erlauben, und \strong{Distribution Certificates}, die der Identifikation des Development Teams und zur Veröffentlichung von Apps im App Store dienen, unterschieden. Die Certificates sind online im Member Center \linkref{https://developer.apple.com/membercenter/} einsehbar.
Mit einem gültigen Certificate können nun sog. \strong{Provisioning Profiles} (Bereitstellungsprofile) erstellt werden, die zusammen mit einer App auf das ausführende Gerät geladen werden und die benötigten Informationen bezüglich der Signierung der App liefern. Eine App kann ohne ein gültiges Provisioning Profile nicht installiert werden. Es wird wieder zwischen \strong{Development Provisioning Profiles} und \strong{Distribution Provisioning Profiles} unterschieden.
Ein Distribution Provisioning Profile ist immer direkt auf eine Bundle ID bezogen und ermöglicht die Veröffentlichung genau dieser App mit der Bundle ID.
Für die Entwicklung können ebenfalls Development Provisioning Profiles erstellt werden, die genau auf eine Bundle ID bezogen sind. Diese werden \strong{explicit} genannt und sind erforderlich, wenn Services wie iCloud verwendet werden, die eine exakte Identifikation benötigen. Solange dies nicht erforderlich ist, kann anstatt der Bundle ID auch ein Asterisk (*) verwendet werden. Ein solches Development Provisioning Profile wird auch \strong{wildcard} genannt und kann für beliebige Bundle IDs verwendet werden.
Das Provisioning Profile enthält außerdem Informationen über die Geräte, die für die Installation der App autorisiert sind. Sog. \strong{Team Provisioning Profiles} erlauben die Installation einer App auf allen Geräten, die im Developer Team registriert sind. Es können hingegen auch Provisioning Profiles erstellt werden, die auf bestimmte Geräte beschränkt sind, bspw. um geschlossene Beta-Tests durchzuführen. In jedem Fall müssen die Geräte, die verwendet werden sollen, im Member Center mit ihrer UDID registriert sein.
\chapter{Projektstrukturierung}
\section{Versionskontrolle mit Git} \label{sec:git}
Die Entwicklung von iOS Apps ist wie jedes Programmierprojekt ein fortschreitender Prozess. Während an Features und Mechaniken im Code gearbeitet wird, möchte man häufig mal einen Ansatz ausprobieren oder Code umstrukturieren, doch wenn nötig schnell wieder zum letzten funktionierenden Stand zurückkehren können \abbref{img:mario_checkpoint}.
\includegraphicsc[50pt]{img/super_mario_flag}{img:mario_checkpoint}{Mit Versionskontrolle können wir jederzeit speichern und dort wieder beginnen - nie wieder Game-Over!}
\emph{Versionskontrolle} (SCM / Software Configuration Management) bietet u.a. diese Sicherheit und ermöglicht es uns zusätzlich, einfach an verschiedenen Versionen gleichzeitig und mit anderen zusammen zu arbeiten.
Ein sehr beliebtes System der Versionskontrolle ist \emph{Git}. Git wird automatisch mit Xcode installiert und ist ein Kommandozeilenprogramm, auf das wir auf dem Mac mit der \emph{Terminal} App zugreifen können.
\subsection{Grundlagen der Kommandozeilensyntax}
\begin{description}
\item[Navigation] \shinline{cd path/to/folder} navigiert zu dem angegebenen Pfad. Die Tilde \shinline{~} repräsentiert hier den Benutzerordner. \keys{\tab} vervollständigt das aktuelle Pfadsegment und zweimaliges Drücken von \keys{\tab} zeigt alle möglichen Vervollständigungen an.
\item[Ordnerinhalt] \shinline{ls} listet den Inhalt des aktuellen Verzeichnisses auf. Mit \shinline{ls -a} werden auch versteckte Dateien angezeigt.
\item[Dateien] \shinline{touch filename} erstellt eine neue Datei mit dem angegebenen Dateinamen, während \shinline{mkdir foldername} einen Ordner erstellt. \shinline{rm filename} löscht die angegebene Datei und \shinline{rm -r foldername} den angegebenen Ordner.
\end{description}
\subsection{Git Repository \& Commits}
Während wir an unserem Projekt arbeiten, verwenden wir Git, um Versionen unseres Codes möglichst häufig in Form von \emph{Commits} zu sichern. Dabei werden alle Änderungen an den Projektdateien, die seit dem letzten Commit durchgeführt wurden, in einem versteckten \shinline{.git/} Verzeichnis, dem \emph{Repository}, hinterlegt.
Bevor wir Commits sichern können, muss das Repository angelegt werden. Dazu navigieren wir im Terminal in den Projektordner und führen die Initialisierung durch:
\begin{shcode}
cd path/to/project
git init
>> Initialized empty Git repository in path/to/project/.git/
git status
>> On branch master
>>
>> Initial commit
>>
>> nothing to commit (create/copy files and use "git add" to track)
\end{shcode}
\shinline{git status} ist sehr nützlich, um häufig die Situation des Repositories zu prüfen.
Nun können wir Dateien in unserem Projekt verändern, indem wir Code schreiben oder löschen. Mit \shinline{git status} sehen wir jederzeit, welche Dateien sich in Bezug auf den vorherigen Commit verändert haben. \shinline{git diff} listet die Änderungen sogar detailliert auf. Befindet sich der Code in einem akzeptablen Zwischenzustand, können wir die Änderungen mit einem Commit im Repository sichern:
\begin{shcode}
git add --all # Markiert alle Änderungen für den nächsten Commit ("Staging")
git commit -m "Commit Message"
>> [master (root-commit) a74833f] Commit Message
>> x files changed, y insertions(+), z deletions(-)
git log
>> ...
\end{shcode}
Hier ist zu beachten, dass die zu sichernden Änderungen dem anstehenden Commit zunächst mit \shinline{git add filename} einzeln oder mit \shinline{git add --all} zusammen hinzugefügt werden müssen. \shinline{git commit} führt den Commit anschließend durch und erwartet einen String als kurze Beschreibung der Änderungen des Commits. \shinline{git log} kann verwendet werden, um eine Liste der letzten Commits im Terminal auszugeben.
\subsection{Branches}
Git bietet weiterhin die sehr nützliche Möglichkeit, an verschiedene Versionen eines Projekts gleichzeitig zu arbeiten, indem sich die Commitfolge an einer beliebigen Stelle verzweigt. Dazu können wir mit \shinline{git branch} einen neuen \emph{Branch} erstellen. Es kann dann jederzeit mit \shinline{git checkout} zwischen Branches gewechselt werden. Dabei passt Git die Dateien und deren Inhalt im Arbeitsverzeichnis automatisch an.
\begin{shcode}
git branch new_feature
git checkout new_feature
# Abkürzung:
git checkout -b new_feature
\end{shcode}
Erstellen wir nun weitere Commits, werden diese dem aktiven Branch hinzugefügt, während die anderen Branches unverändert bleiben.
Um Branches wieder zu vereinigen, bietet Git die \emph{Merge} und \emph{Rebase} Mechanismen. Dabei bestimmt \shinline{git merge} die Unterschiede der beiden Branches und erstellt einen Commit, der diese dem aktuellen Branch zusammenfassend hinzufügt. \shinline{git rebase} verändert dagegen die Commitfolge des aktuellen Branches dahingehend, dass die Commits beider Branches so kombiniert werden, als wären sie nacheinander entstanden.
\strong{Achtung:} Da ein \shinline{rebase} die Commitfolge verändert, ist Vorsicht angebracht, wenn man mit anderen zusammenarbeitet!
\begin{shcode}
git checkout master # Wechsle zurück in den Branch master
git merge new_feature # Führe alle Commits aus dem Branch new_feature mit dem aktiven Branch zusammen
\end{shcode}
Git versucht bei einem Merge oder Rebase, die Änderungen der Branches zu vereinigen. Treten dabei Konflikte auf, wird der Vorgang unterbrochen und die Konflikte müssen zunächst im Code behoben werden. An den entsprechenden Stellen im Code sind dann Markierungen zu finden, die über eine projektübergreifende Suche in Xcode schnell gefunden werden.
\begin{objccode}
<<<<<<< HEAD:
// alter Code
=======
// veränderter Code
>>>>>>>
\end{objccode}
Sobald die Konflikte behoben wurden, kann der Vorgang wieder aufgenommen werden:
\begin{shcode}
git add --all
git commit
\end{shcode}
\subsubsection{Feature Branches}
In dieser Form werden bspw. sehr häufig \emph{Feature Branches} verwaltet. Dabei wird die stabile und häufig veröffentlichte Version des Projekts von dem \shinline{master} Branch des Repositories repräsentiert. Für neue Features oder Umstrukturierungen wird dann eine Branchstruktur angelegt. So kann gearbeitet werden, ohne dass die stabile Version des Projekts verändert wird. Erreicht ein Branch einen stabilen Status und soll veröffentlicht werden, wird ein Merge mit dem \shinline{master} Branch durchgeführt. Der Arbeitsbranch kann dabei jederzeit gewechselt werden.
Wenn sich bspw. plötzlich ein User unserer App über einen Fehler beschwert, während wir bereits eifrig in einem Feature Branch an der nächsten Version arbeiten, können wir schnell zurück in den \shinline{master} Branch wechseln, den Fehler beheben und veröffentlichen. Zurück im Feature Branch kann diese Fehlerbehebung mit einem \shinline{merge} direkt integriert und dann weitergearbeitet werden.
\subsection{Zusammenarbeit mit Git \& GitHub}
Git Repositories ermöglichen die reibungslose Zusammenarbeit mehrerer Entwickler an einem Projekt und ermöglichen dadurch erst die Entwicklung vieler komplexer Projekte, an denen Programmierer auf der ganzen Welt zusammenarbeiten.
Befindet sich eine Kopie des Repositories auf einem Server, kann einem Branch ein serverseitiges Gegenstück zugewiesen werden. Mit \shinline{git push} und \shinline{git pull} können dieses \strong{Remote Repository} und die lokale Kopie abgeglichen werden.
\shinline{git pull} besteht dabei prinzipiell zunächst aus einem Aufruf von \shinline{git fetch}, der die serverseitigen Änderungen herunterlädt, und einem anschließenden \shinline{git merge}, um die Änderungen in den lokalen Branch zu integrieren.
Die Git Dokumentation \secref{sec:git_doku} enthält detaillierte Beschreibungen zu Remote Repositories.
An dieser Stelle darf der Service \strong{GitHub\linkref{http://www.github.com}} nicht unerwähnt bleiben, der Entwicklern eine Plattform für ihre Git Repositories bietet und für öffentliche Projekte kostenlos ist. Eine Alternative, die auch auf einem eigenen Server installiert werden kann, bietet bspw. \strong{GitLab\linkref{https://gitlab.com/}}.
Ein Repository kann mit \shinline{git clone} von einem Server heruntergeladen werden, wobei den local Branches automatisch ihr entsprechendes remote Gegenstück zugewiesen wird. Anschließend können wir mit dem local Repository arbeiten und Commits mit dem remote Repository abgleichen.
Entwickler können mit ihrem GitHub Account gemeinsame Repositories erstellen oder solche anderer Entwickler weiterentwickeln. Letztere Mechanik wird \strong{Fork} genannt und bietet die Möglichkeit, an einem Repository eines anderen Entwicklers zu arbeiten und diesem anzubieten, die erstellten Commits in sein Repository zu integrieren. Dazu kann eine \strong{Pull Request} versandt werden, die der Besitzer des Repositories zunächst annehmen muss, damit die Änderungen in sein Repository übernommen werden. In dieser Form sind Entwickler auf der ganzen Welt in der Lage, an Open Source Projekten zusammenzuarbeiten.
\subsubsection{Dieser Kurs auf GitHub}
Git ist für viele Projekte nützlich und bei weitem nicht nur auf Programmcode beschränkt. So befindet sich bspw. neben der Vorlesungswebseite auch dieses Skript in einem Git Repository auf GitHub \linkref{https://github.com/iOS-Dev-Kurs}. Ihr könnt es also leicht klonen und Aktualisierungen herunterladen, wenn diese online verfügbar sind:
\begin{shcode}
cd path/to/directory
git clone https://github.com/iOS-Dev-Kurs/Skript
# zur Aktualisierung:
git pull
\end{shcode}
Ihr seid außerdem herzlich dazu eingeladen, Verbesserungen als Pull Requests vorzuschlagen!
\subsection{Git in Xcode}
Versionskontrolle ist tief in Xcode integriert und in vielen Kontextmenüs und Schaltflächen präsent. Das Menü \menu{Source Control} stellt bspw. einige Benutzeroberflächen zu Git Befehlen zur Verfügung, die die Kommandozeilenbedienung ersetzen können.
Sehr hilfreich ist der \emph{Version Editor} \abbref{img:xcode_version_editor}, der mit der Schaltfläche rechts in der Toolbar alternativ zum Assistant Editor angezeigt werden kann. Im rechten Editorbereich wird dann die Version der links angezeigten Datei zu einem früheren Commit angezeigt und Änderungen visualisiert. Mit der Uhr-Schaltfläche unten in der Mittelleiste können frühere Commits ausgewählt werden.
\includegraphicsc{img/xcode_version_editor.png}{img:xcode_version_editor}{Der Version Editor zeigt die links geöffnete Datei zu einem früheren Commit an und visualisiert Änderungen}
\subsection{Gitignore}
Dateien können von von der Erfassung durch Git ausgenommen werden, wenn es sich bspw. um benutzerspezifische oder temporäre Dateien handelt. Dazu wird dem Repository eine \shinline{.gitignore} Datei hinzugefügt:
\begin{shcode}
touch .gitignore
open .gitignore
\end{shcode}
Mit einem Texteditor wie der TextEdit App oder \shinline{vim} in der Konsole können wir diese Datei nun editieren. Sinnvolle Vorlagen für verschiedenste Programmiersprachen und Plattformen sind im GitHub Repository \emph{gitignore} \linkref{https://github.com/github/gitignore} zu finden. Kopiert für Xcode Projekte die Vorlagen \emph{Xcode} \linkref{https://github.com/github/gitignore/blob/master/Global/Xcode.gitignore} und \emph{OSX} \linkref{https://github.com/github/gitignore/blob/master/Global/OSX.gitignore} in die \shinline{.gitignore} Datei. Letztere Vorlage enthält bspw. die Zeile \shinline{.DS_Store}. So heißen versteckte Dateien in Mac OS X, die dem Betriebssystem verschiedene Informationen über den Ordner bereitstellen \linkref{http://en.wikipedia.org/wiki/.DS_Store} und nicht in das Git Repository übernommen werden sollen.
Die \shinline{.gitignore} Datei muss dem Repository nun zunächst hinzugefügt werden, bevor sie wirksam wird:
\begin{shcode}
git add .gitignore
git commit -m "Added gitignore file"
\end{shcode}
\subsection{Dokumentation}\label{sec:git_doku}
Git \linkref{http://git-scm.com} ist erstklassig dokumentiert und bietet neben Tutorials und einem Übungsmodus \linkref{http://try.github.com/} eine umfassende Dokumentation \linkref{http://git-scm.com/book}. Hier sollte bei Bedarf unbedingt nachgeschlagen werden.
\chapter{iOS App Architektur}
\section{iOS App Lifecycle}
Wir versuchen nun im Detail zu verstehen, wie unsere Apps auf einem iOS Gerät ausgeführt werden. Außerdem sind in einem Xcode Projekt verschiedene Dateien zu finden, die wir hier in Kontext bringen.
Die folgenden Informationen sind im iOS App Programming Guide \linkref{https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/} zu finden und können dort vertieft werden.
\subsection{App States}\label{sec:app_states}
Eine App liegt immer in einem der folgenden \strong{App States} vor:
\begin{description}
\item[Not running] Die App läuft nicht.
\item[Inactive] Ein Zwischenzustand, in dem die App im Vordergrund läuft aber keine Events empfängt, bspw. während des Startvorgangs oder bei Unterbrechungen wie eingehenden Anrufen.
\item[Active] Die App läuft normal im Vordergrund und empfängt Events.
\item[Background] Die App führt Code im Hintergrund aus. Wenn nicht explizit eine begrenzte Laufzeit zur Ausführung eines Hintergrundprozesses angefordert wird, geht die App direkt zum \emph{Suspended} State über.
\item [Suspended] Die App befindet sich im Hintergrund und führt keinen Code aus. Es kann schnell wieder ein Vordergrund-State angenommen werden, da der Speicher noch nicht freigegeben wurde. Das System kann dies jedoch jederzeit tun und damit in den \emph{Not running} State übergehen.
\end{description}
\subsection{Startprozess einer iOS App}\label{sec:launchprocess}
\begin{enumerate}
\item Der Benutzer drückt auf das App Icon oder startet die App auf eine andere Weise.
\item Die Funktion \swiftinline{main} in der Datei \emph{main.swift} wird aufgerufen. Diese Funktion wird auch als \emph{Entry Point} bezeichnet und besitzt entsprechende Äquivalente in anderen Programmiersprachen.
\swiftinline{main} ist dafür zuständig, die \swiftinline{UIApplicationMain} Funktion des \swiftinline{UIKit} Frameworks aufzurufen und ihr die Klassen des \emph{Application Object} und \emph{Application Delegate} zu übergeben (s.u.).
Da die Implementierung der \swiftinline{main} Funktion fast immer gleich ist, stellt das \swiftinline{UIKit} Framework stattdessen das Attribut \swiftinline{@UIApplicationMain} zur Verfügung, mit dem eine Klasse als Application Delegate markiert werden kann. Das Application Object ist dann vom Typ \swiftinline{UIApplication}.
\begin{swiftcode}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
// ...
}
\end{swiftcode}
\item Die \swiftinline{UIApplicationMain} Funktion erzeugt das \strong{Application Object}, also ein Objekt der Klasse \objcinline{UIApplication}, das für die zentrale Koordination der App verantwortlich ist.
\item Anschließend wird das \strong{Application Delegate} als Objekt der zuvor mit dem \swiftinline{@UIApplicationMain} Attribut markierten Klasse erstellt und dem Attribut \swiftinline{delegate} des Application Objects zugewiesen.
Das Application Delegate stellt das Kernstück unseres selbstgeschriebenen Codes dar.
Während das Application Object die Ausführung unserer App koordiniert, bietet es dem Application Delegate im Verlauf des Startvorgangs und während der Laufzeit der App eine Vielzahl von Möglichkeiten, auf Ereignisse zu reagieren. Dazu werden Methoden des \swiftinline{UIApplicationDelegate} Protokolls aufgerufen. Diese informieren bspw. über den Wechsel der oben beschriebenen States und sind im Übersichtsdiagramm dargestellt \abbref{img:lifecycle_overview}.
Die wohl wichtigste Methode ist dabei \swiftinline{application:didFinishLaunchingWithOptions:}. Diese wird am Ende des Startvorgangs aufgerufen und zur initialen Konfiguration der App verwendet. Hier werden bspw. zentrale Datenstrukturen erstellt und das User Interface vorbereitet.
\item Im Xcode Projekt ist eine Datei \emph{Info.plist} zu finden. Diese steht dem Application Objekt zur Verfügung und enthält eine Liste verschiedener Optionen, die wir in der Projekt- und Targetkonfiguration anpassen können. Hier wird u.a. angegeben, wenn es ein \emph{Storyboard} gibt, das als Startpunkt für die Darstellung der Benutzeroberfläche verwendet werden soll.
Dieses Storyboard wird geladen und dessen Inital Scene angezeigt. Die im Storyboard enthaltenen Objekte werden dabei instanziert und die Connections (\swiftinline{IBOutlets} und \swiftinline{IBActions}) hergestellt. Die so erstellte \emph{View Hierarchie} \secref{sec:viewhierarchy} wird einem \swiftinline{UIWindow} Objekt hinzugefügt, das dann dem Attribut \swiftinline{window} des Application Delegate zugewiesen wird.
\item Nun übernimmt die \emph{View Controller Hierarchie} \secref{sec:vchierarchy} die Kontrolle über einzelne Ansichten unserer App, während das Application Delegate auf globale Ereignisse reagiert.
\end{enumerate}
\includegraphicsc{img/lifecycle_overview.png}{img:lifecycle_overview}{Übersicht der Prozesse beim Starten einer iOS App (aus dem iOS App Programming Guide)}
\clearpage
\section{Das Model-View-Controller Konzept}\label{sec:mvc}
Für die Strukturierung komplexerer Apps müssen wir nun zunächst ein wichtiges Konzept der Softwareentwicklung verstehen:
Das \emph{Model-View-Controller (MVC) Konzept} zieht sich konsequent durch die Gestaltungsmuster von iOS Apps und wird auch auf vielen anderen Plattformen verwendet.
Es basiert auf dem Grundsatz, dass \emph{Modell}, \emph{Präsentation} und \emph{Steuerung} eines Programms strikt getrennt implementiert werden \abbref{img:mvc_diagram}. Durch diese Modularisierung bleibt ein Softwareprojekt flexibel und erweiterbar und einzelne Komponenten können leicht wiederverwertet werden.
\includegraphicsc{img/mvc_diagram.png}{img:mvc_diagram}{Das MVC Konzept trennt Modell, Präsentation und Steuerung}
\begin{description}
\item[Modell (Model)] Datenstrukturen und Logik werden dem Modell zugeordnet. Es stellt die darzustellenden Daten zur Verfügung und verarbeitet Anfragen bezüglich deren Modifikation.
In Swift implementieren wir Klassen, Structs und Enumerations mit Attributen, Methoden und Beziehungen, um solche Datenstrukturen zu repräsentieren.
\item[Präsentation (View)] Subklassen von \swiftinline{UIView} repräsentieren Interfaceelemente und werden auf dem Bildschirm dargestellt. Sie werden vom Controller mit Informationen gefüllt und leiten Benutzereingaben an diesen weiter.
\item[Steuerung (Controller)] Der Controller verwaltet die Views und reagiert auf Benutzereingaben. Zur Laufzeit der App übernehmen Subklassen von \swiftinline{UIViewController} die Kommunikation zwischen Model und View. Meist repräsentiert jeweils ein View Controller eine bestimmte Ansicht auf dem Bildschirm. In Reaktion auf Benutzereingaben stellt der View Controller Anfragen an das Model und konfiguriert die Views.
Zusätzlich sind übergeordnete Controller wie bspw. Instanzen von \swiftinline{UINavigationController} für die Koordination der einzelnen View Controller zuständig und verwalten deren hierarchische Strukturen.
\end{description}
\section{View Hierarchie}\label{sec:viewhierarchy}
\mvcindicatorview
Die \strong{View} Komponente jeder iOS App ist für die Anzeige des User Interface auf dem Bildschirm verantwortlich. Wir verwenden dazu Subklassen von \swiftinline{UIView}.
\swiftinline{UIView} wird in Apples UIKit Framework als Subklasse von \swiftinline{UIResponder : NSObject} implementiert. Die Klasse erbt somit zusätzlich zu den Verwaltungsmechanismen von \swiftinline{NSObject} auch die Methoden zur Reaktion auf Benutzereingaben von \swiftinline{UIResponder}.
UIKit stellt viele Subklassen von \swiftinline{UIView} zur Verfügung, wie bspw. \swiftinline{UILabel}, \swiftinline{UIButton} und \swiftinline{UIImageView}. Diese implementieren jeweils Methoden, um ihren Inhalt auf dem Bildschirm zu rendern.
Jedes \swiftinline{UIView} Objekt präsentiert jedoch nicht nur seinen eigenen Inhalt sondern dient auch als Container für andere \swiftinline{UIView} Objekte. Somit erhalten wir eine hierarchische Struktur von \emph{Superviews} mit jeweils beliebig vielen \emph{Subviews} \abbref{img:view_hierarchy}. An oberster Stelle der Hierarchie steht dabei ein Objekt der Klasse \swiftinline{UIWindow : UIView}, das von dem Application Object verwaltet wird \secref{sec:launchprocess}.
\includegraphicsc[\iphonewidth]{img/view_hierarchy.png}{img:view_hierarchy}{Jedes Objekt der \swiftinline{UIView} Klasse dient wieder als Container für weitere Objekte dieser Klasse}
Mit Instanzmethoden wie \swiftinline{addSubview:} und \swiftinline{removeFromSuperview} von \swiftinline{UIView} können wir der View Hierarchie Objekte hinzufügen oder Objekte entfernen.
\subsection{Frame und CGRect}
Jedes Objekt der Klasse \swiftinline{UIView} verwaltet einen Anzeigebereich, der durch das Attribut \swiftinline{frame: CGRect} bestimmt ist. Der Frame bestimmt ein Rechteck im zweidimensionalen Koordinatensystem der Superview.
Der Ursprung des Koordinatensystems liegt dabei immer in der oberen linken Ecke der Superview \abbref{img:view_coordinates} mit einer horizontalen x-Achse und vertikalen y-Achse in positiver Richtung nach rechts unten.
\includegraphicsc[\iphonewidth]{img/view_coordinates.png}{img:view_coordinates}{Der Ursprung des Koordinatensystems von \swiftinline{UIView} liegt in der oberen linken Ecke}
\swiftinline{CGRect} ist ein Struct, das ein Rechteck mit einem Ursprung \swiftinline{origin: CGPoint} und einer Größe \swiftinline{size: CGSize} repräsentiert. \swiftinline{CGPoint} stellt dabei einen Punkt mit den Attributen \swiftinline{x: CGFloat} und \swiftinline{y: CGFloat} dar, während \swiftinline{CGSize} eine Ausdehnung mit Breite \swiftinline{width: CGFloat} und Höhe \swiftinline{height: CGFloat} beschreibt. \swiftinline{CGFloat} ist äquivalent zu \swiftinline{Float} auf einer 32-bit Architektur und zu \swiftinline{double} auf einer 64-bit Architektur.
\begin{swiftcode}
struct CGRect {
var origin: CGPoint
var size: CGSize
}
struct CGPoint {
var x: CGFloat
var y: CGFloat
}
struct CGSize {
var width: CGFloat
var height: CGFloat
}
\end{swiftcode}
\subsection{UIView Objekte}
Wir können ein \swiftinline{UIView} Objekt u.a. mit dem Initializer \swiftinline{init(frame: CGRect)} erstellen und dann der View Hierarchie hinzufügen, sodass es auf dem Bildschirm angezeigt wird:
\begin{swiftcode}
let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
self.view.addSubview(view) // Angenommen, self.view ist bereits Teil der angezeigten View Hierarchie
\end{swiftcode}
Subklassen von UIView erben diesen Mechanismus. Ein \swiftinline{UILabel} lässt sich also bspw. genauso erzeugen und anzeigen.
\section{Auto Layout}
\mvcindicatorview
iOS Apps werden mittlerweile auf einer Vielzahl von Geräten unterschiedlicher Größe ausgeführt. Die Benutzeroberfläche unserer Apps sollte sich dabei dynamisch verschiedenen Anzeigegrößen und Inhalten anpassen können. Dazu gehören neben Displaygrößen und -orientierungen bspw. auch Sprachen mit verschieden Leserichtungen und Wortlängen. %\abbref{img:autolayout_orientation}.
%\includegraphicsc{img/autolayout_orientation.png}{img:autolayout_orientation}{Benutzeroberflächen sollten sich dynamisch an verschiedene Anzeigegrößen anpassen}
Natürlich können wir versuchen, die Parameter des \swiftinline{frame} Attributs einer View in Reaktion auf Änderungen der Größe der Superview oder des Inhalts geschickt anzupassen. Häufig haben wir jedoch ganz bestimmte Vorstellungen, wie die Views einer View Hierarchie \strong{zueinander} positioniert sein sollen. Bei der Konzeption von Benutzeroberflächen treten anstatt von festen Positionen vielmehr Regeln auf wie:
\begin{itemize}
\item Eine View soll möglichst groß genug für ihren Inhalt sein, jedoch immer einen sinnvollen Mindestabstand von den Seiten des Bildschirms haben und den Inhalt wenn nötig komprimieren.
\item Eine View soll wenn möglich zentriert positioniert sein, nie jedoch den Frame einer anderen View überlagern.
\item Zwei Views sollen immer die gleiche Höhe besitzen, die sich an der benötigten Größe des Inhalts der jeweils größeren View orientiert.
\item Eine View soll immer den gesamten Bereich ihrer Superview ausfüllen.
\end{itemize}
Das \emph{Auto Layout} Konzept der iOS Programmierung basiert auf der Definition von Regeln dieser Art, genannt \emph{Constraints}.
\subsection{Constraints}
Jede Constraint ist ein Objekt der Klasse \swiftinline{NSLayoutConstraint} und repräsentiert eine Beziehung zwischen zwei \emph{Attributen} $x$ und $y$ verschiedener Views, die durch den Ausdruck
\begin{equation}
y = m*x + b
\end{equation}
gegeben ist. Dabei beschreiben $m$ und $b$ vom Typ \swiftinline{Float} \emph{Multiplier} und \emph{Constant} der Beziehung.
In dieser Form können wir bspw. eine Constraint definieren, die den horizontalen Abstand zweier Views auf einen Wert von 20pt beschränkt:
\begin{swiftcode}
secondView.left = firstView.right * 1.0 + 20.0
\end{swiftcode}
Die Auto Layout Syntax verfügt u.a. über die Attribute \swiftinline{left, right, top, bottom, leading, trailing, width, height, centerX, centerY} und \swiftinline{baseline}, wobei \swiftinline{leading} und \swiftinline{trailing} abhängig von der Leserichtung der eingestellten Sprache äquivalent oder umgekehrt zu \swiftinline{left} und \swiftinline{right} sind.
Es ist darüber hinaus möglich, neben Gleichheitsbeziehungen auch Ungleichungen zu definieren und Constraints ein Prioritätslevel zwischen $1$ und $1000$ zuzuweisen.
Zur Laufzeit ist die Superview für die automatische Positionierung ihrer Subviews entsprechend der definierten Constraints zuständig.
Für Views können beliebig viele, sich überlagernde Constraints definiert werden. Stehen einige Constraints in Konflikt zueinander und können nicht gleichzeitig erfüllt werden, wird eine Warnung ausgegeben und die Superview hebt Constrains nacheinander auf, bis das Layout gültig ist. Dabei werden nicht erfüllbare Constraints trotzdem so weit wie möglich einbezogen.
Enthält eine Superview keine Constraints, wird auf die expliziten Frames der Views zurückgegriffen.
Bei der Konstruktion der Constraints sollte unbedingt auf ein eindeutiges Layout geachtet werden. Ein solches liegt vor, wenn jeder Subview ein eindeutiger Wert für alle vier Parameter des \swiftinline{frame} Attributs zugewiesen werden kann.
\subsection{Intrinsic Content Size}
Subklassen von \swiftinline{UIView} repräsentieren häufig Inhalt, dessen Darstellung eine bestimmte Größe erfordert. Dazu gehören bspw. \swiftinline{UILabel} oder \swiftinline{UIImageView} Objekte, deren Größe durch ihren Text- oder Bildinhalt bestimmt werden.
Diese \emph{Intrinsic Content Size} wird verwendet, um die Größe einer View zu berechnen. In den meisten Fällen sollte die Intrinsic Content Size \strong{nicht} durch explizite \swiftinline{width} und \swiftinline{height} Constraints beschränkt werden.
Die beiden Parameter \emph{Content Hugging Priority} und \emph{Compression Resistance Priority} einer View legen fest, mit welcher Priorität sich das Auto Layout System an der Intrinsic Content Size zu orientieren hat. Dabei führt eine geringe Content Huging Priority bspw. dazu, dass eine View verfügbaren Platz über die Intrinsic Content Size hinaus eher einnimmt, als eine View mit höherer Content Hugging Priority. Die Compression Resistance Priority bestimmt hingegen, welche View zuerst auf eine geringere Größe als ihre Intrinsic Content Size gestaucht wird, wenn Constraints den verfügbaren Platz einschränken.
\subsection{Auto Layout im Interface Builder}
Auto Layout kann für Interface Builder Dateien wie Storyboards einzeln aktiviert oder deaktiviert werden. Dazu ist im File Inspector eine Schaltfläche \emph{Use Autolayout} zu finden \abbref{img:autolayout_activate}.
\includegraphicsc[\iphonewidth]{img/autolayout_activate.png}{img:autolayout_activate}{Auto Layout kann auf Basis einzelner Interface Builder Dateien ein- oder ausgeschaltet werden}
Ist Auto Layout eingeschaltet, können wir Constraints auf verschiedene Weise erstellen. Eine Möglichkeit besteht in der Verwendung der Schaltfächen am unteren rechten Rand des Editorbereichs \abbref{img:autolayout_menus}. Hier finden wir die interaktiven Menüs \emph{Align} und \emph{Pin}, mit denen den ausgewählten Views Constraints hinzugefügt werden können.
\includegraphicsc[\iphonewidth]{img/autolayout_menus.png}{img:autolayout_menus}{Die Schaltflächen am unteren rechten Rand des Editorbereichs bieten Zugriff auf viele Auto Layout Optionen}
Alternativ kann das von IBOutlets und IBActions bekannte Ziehen einer Verbindungslinie bei gehaltener \keys{\ctrl}-Taste auch zum Erstellen von Constraints zwischen zwei Interfaceelementen verwendet werden \abbref{img:autolayout_drag}. Dabei wird abhängig von der Richtung der gezogenen Linie ein kontextabhängigeg Menü gezeigt. Ziehen wir bspw. eine horizontale Linie, werden Constraint-Optionen bezüglich der Horizontalrichtung angezeigt.
\includegraphicsc[\iphonewidth]{img/autolayout_drag.png}{img:autolayout_drag}{Constraints können ähnlich wie IBOutlets und IBActions durch Ziehen einer Verbindungslinie erstellt werden}
Drei Farben markieren den Status der Constraints einer ausgewählten View im Interface Builder \abbref{img:autolayout_blue}. Solange das Layout für die View noch uneindeutig ist, werden die Constraints in gelb angezeigt. Eindeutige Layouts werden blau und Layouts mit Konflikten rot markiert.
\includegraphicsc[\iphonewidth]{img/autolayout_blue.png}{img:autolayout_blue}{Ist das Layout einer View durch Constraints eindeutig festgelegt, werden diese blau gekennzeichnet}
Wenn die Position der ausgewählten View im Interface Builder nicht ihrer berechneten Position gemäß ihren Constraints zur Laufzeit entspricht, wird sie diese gelb gestrichelt markiert \abbref{img:autolayout_ambig}. Im \emph{Resolve Auto Layout Issues} Menü am unteren rechten Bildschirmrand ist die Option \emph{Update Frames} zu finden, mit der die Position entsprechend angepasst werden kann.
\includegraphicsc[\iphonewidth]{img/autolayout_ambig.png}{img:autolayout_ambig}{Uneindeutige Layouts oder Views, deren Position von vom berechneten Layout abweichen, werden gelb gekennzeichnet und zeigen ihre Position zur Laufzeit als eine gestrichelte Markierung}
Alle Constraints einer bestimmten View sind im Size Inspector aufgelistet. Eine Constraint kann wie jedes andere Objekt ausgewählt und mit dem Attributes Inspector bearbeitet werden. Die wichtigsten Optionen sind außerdem mit einem Doppelklick auf eine Constraint im Editorbereich erreichbar \abbref{img:autolayout_popup}.
\includegraphicsc[\iphonewidth]{img/autolayout_popup.png}{img:autolayout_popup}{Ein Doppelklick auf eine Constraint im Interface Builder zeigt die wichtigsten Optionen in einem Popup}
\subsection{Auto Layout im Code}
Der Interface Builder stellt die effektivste Möglichkeit dar, ein eindeutiges und dynamisches Layout zu konzipieren. Im Code werden häufig nur die Konstanten einzelner Constraints zur Laufzeit angepasst und selten ganze Layouts konstruiert.
Trotzdem können wir Constraints als Objekte der \swiftinline{NSLayoutConstraint} Klasse im Code erstellen und einer View hinzufügen. Dazu implementiert \swiftinline{UIView} die Instanzmethoden \swiftinline{addConstraint:} und \swiftinline{removeConstraint:} und gewährt über das Attribut \swiftinline{constraints} Zugriff auf alle Constraints.
Einzelne Constraints können mit der Klassenmethode \swiftinline{constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:} instanziert werden. Da jedoch meist mehrere Constraints benötigt werden, stellt \swiftinline{NSLayoutConstraint} zusätzlich eine Klassenmethode \swiftinline{constraintsWithVisualFormat:options:metrics:views:} zur Verfügung, die die \emph{Visual Format Language} verwendet.
Dabei übergeben wir der Klassenmethode einen String im ASCII-Art Stil, der die zu erstellenden Constraints beschreibt. So können wir bspw. einen Abstand von 10pt zwischen zwei Views \swiftinline{view1} und \swiftinline{view2} mit dem String
\begin{swiftcode}
"[view1]-10-[view2]"
\end{swiftcode}
darstellen. Sollen beide Views zusätzlich den Standardabstand von der Begrenzung durch die Superview besitzen, schreiben wir:
\begin{swiftcode}
@"|-[view1]-10-[view2]-|"
\end{swiftcode}
Die Syntax der Visual Format Language ist im Auto Layout Guide\linkref{https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage/VisualFormatLanguage.html} einsehbar.
In dieser Form erstellte Constraint können wir dann einer Superview hinzufügen:
\begin{swiftcode}
let constraints = NSLayoutConstraint.constraintsWithVisualFormat("|-[view1]-10-[view2]-|", options: .allZeros, metrics: nil, views: [ "view1" : self.view1, "view2" : self.view2 ])
self.view.addConstraints(constraints)
\end{swiftcode}
\subsection{Size Classes}
Mit Auto Layout werden Constraints zunächst für beliebige Displaygrößen definiert. Mit dem Konzept der \emph{Size Classes} können dann einzelne Constraints oder das ganze Layout für bestimmte Displaygrößen angepasst werden.
Es gibt in horizontaler und vertikaler Richtung jeweils die Size Classes \emph{Compact} und \emph{Regular}. Jede Klasse, die das \swiftinline{UITraitEnvironment} Protokoll implementiert, also u.a. \swiftinline{UIView}, stellt unter dem Attribut \swiftinline{traitCollection} neben anderen Eigenschaften ihre \swiftinline{horizontalSizeClass} und \swiftinline{verticalSizeClass} zu Verfügung.
Size Classes werden in der View Hierarchie vererbt, beginnend beim \swiftinline{UIScreen} des ausführenden Geräts.
\begin{itemize}
\item Alle iPhones in Portrait Orientierung sind horizontal Compact und vertikal Regular.
\item In Landscape Orientierung sind iPhones sowohl horizontal als auch vertikal Compact. Nur iPhones mit 5.5-inch Bildschirm sind horizontal Regular.
\item iPads sind sowohl horizontal als auch vertikal Regular.
\end{itemize}
Weiterhin verändern einige View Controller Container \secref{sec:vc_container_uikit} die Size Class ihrer View Hierarchie, wenn einer View nur ein Teil des Bildschirms zur Verfügung steht.
Wenn für eine Interface Builder Datei Size Classes aktiviert sind \abbref{img:autolayout_activate}, kann am unteren Rand des Editorbereichs \emph{Any} oder eine bestimmte Size Class ausgewählt werden \abbref{img:autolayout_sizeclasses}. Das Layout wird zunächst für \emph{Any} konfiguriert und dann für bestimmte Size Classes überschrieben, wobei Interfaceelemente und Constraints beliebig verändert, hinzugefügt oder entfernt werden können.
\includegraphicsc[\iphonewidth]{img/autolayout_sizeclasses.png}{img:autolayout_sizeclasses}{Am unteren Rand des Editorbereichs kann eine bestimmte Size Class ausgewählt werden, um das Layout für diese anzupassen.}
%\section{Ereignisgesteuerte Programmierung}
%
%Im Unterschied zu sequentiell ablaufenden Skripten ist die Ausführung von iOS Apps \strong{ereignisgesteuert} oder \strong{event-driven}. Zur Laufzeit der App wird auf System- und Benutzerereignisse reagiert. Die App muss diese Ereignisse verarbeiten und das UI entsprechend anpassen.
%
%*** add UIControlEvent section
\section{View Controller Hierarchie}\label{sec:vchierarchy}
\mvcindicatorcontroller
Eine App besteht im Allgemeinen aus verschiedenen Bildschirmansichten, die jeweils eine Komponente der Benutzeroberfläche repräsentieren. Daher verwalten wir die View Hierarchie unserer App nicht zentral, bspw. im Application Delegate, sondern verwenden einzelne Klassen, die jeweils einen Teil der View Hierarchie verwalten.
Diese Subklassen von \swiftinline{UIViewController} sind der \emph{Controller}-Komponente des Model-View-Controller Konzepts zugeordnet \secref{sec:mvc}.
Jeder View Controller ist für die Verwaltung seiner eigenen \emph{Content View} zuständig. \swiftinline{UIViewController} besitzt dafür ein Attribut \swiftinline{view}.
Zu den Aufgaben eines View Controllers gehören:
\begin{itemize}
\item die dynamische Positionierung der Views in der View Hierarchie seiner Content View
\item die Kommunikation mit der Model-Komponente, um die Views mit Daten zu füllen
\item die Reaktion auf Benutzereingaben
\end{itemize}
Nach dem Prinzip des \emph{View Controller Containment} gibt es auch hier, ähnlich wie bei der View Komponente, eine hierarchische Struktur \abbref{img:vc_hierarchy}. Demnach gibt es übergeordnete View Controller, die die Content Views anderer View Controller der View Hierarchie ihrer eigenen Content View hinzufügen \abbref{img:vc_containment}.
\includegraphicsc{img/vc_hierarchy.png}{img:vc_hierarchy}{Container View Controller präsentieren Content Views anderer View Controller, sodass eine View Controller Hierarchy entsteht. Abbildung aus der UIViewController Class Reference}
\clearpage
\includegraphicsc{img/vc_containment.png}{img:vc_containment}{Jeder View Controller verwaltet seine Content View, die einen Teil der View Hierarchie darstellt}
Die oberste Instanz der View Hierarchie ist ein Objekt der \swiftinline{UIWindow} Klasse. Analog besitzt \swiftinline{UIWindow} ein Attribut \swiftinline{rootViewController}, das die oberste Instanz der View Controller Hierarchie darstellt. Weisen wir diesem Attribut ein View Controller Objekt zu, so wird dessen Content View der View Hierarchie des \swiftinline{UIWindow} Objekts hinzugefügt. Wird ein Storyboard verwendet, geschieht dies automatisch mit der Option \emph{Initial View Controller} im Attributes Inspektor des entsprechenden View Controllers.
Während nur in wenigen Fällen Subklassen von \swiftinline{UIView} implementiert werden müssen, wird man kaum eine iOS App ohne mindestens eine Subklasse von \swiftinline{UIViewController} finden. Häufig stellt die Implementierung der View Controller den Großteil der Programmierung von iOS Apps dar.
\subsection{View Controller Lifecycle}
Im Verlauf der Präsentation eines View Controllers können verschiedene Instanzmethoden überschrieben werden, um auf Änderungen der Darstellung zu reagieren:
\begin{itemize}
\item \swiftinline{viewDidLoad} wird aufgerufen, sobald die Content View geladen wurde. An dieser Stelle können Attribute, die im Storyboard nicht hinreichend konfiguriert werden können, dynamisch angepasst werden, um einen Ausgangspunkt für die Präsentation zu schaffen.
\item \swiftinline{viewWillAppear:}, \swiftinline{viewDidAppear:}, \swiftinline{viewWillDisappear:} und \swiftinline{viewDidDisappear:} können verwendet werden, um die Inhalte der Content View zu aktualisieren oder Vorgänge zu beginnen/anzuhalten.
\item \swiftinline{didReceiveMemoryWarning} wird aufgerufen, wenn das System die App auffordert, Speicher freizugeben. Hier sollten nicht mehr benötigte Objekte oder solche, die leicht wieder zu erstellen sind, freigegeben werden.
\end{itemize}
\subsection{Präsentation von View Controllern}
Die \swiftinline{UIViewController} Klasse implementiert bereits eine einfache Möglichkeit, einen anderen View Controller temporär zu präsentieren. Die Instanzmethode \swiftinline{presentViewController:animated:completion:} fügt die Content View des entsprechenden View Controllers der View Hierarchie hinzu, während \swiftinline{dismissViewControllerAnimated:completion:} die Präsentation wieder beendet.
\begin{swiftcode}
// Präsentiert einen View Controller
self.presentViewController(modalViewController, animated: true, completion: nil)
// Beendet die Präsentation
self.dismissViewControllerAnimated(true, completion: nil)
\end{swiftcode}
\swiftinline{UIKit} implementiert darüber hinaus eine vielseitige API zur Präsentation von View Controllern. Dabei wird die View Controller Hierarchie traversiert und nach einem übergeordneten View Controller gesucht, der die Präsentation übernimmt. Die Container View Controller in \swiftinline{UIKit} sind sehr einfach zu verwenden \secref{sec:vc_container_uikit}
\subsection{Container View Controller in UIKit}\label{sec:vc_container_uikit}
Das \swiftinline{UIKit} Framework stellt einige nützliche Subklassen von \swiftinline{UIViewController} zu Verfügung, die in vielen Apps strukturgebend verwendet werden.
Insbesondere werden wir uns noch genauer mit dem äußerst vielseitigen \swiftinline{UITableViewController} befassen. Dieser gehört mit \swiftinline{UICollectionViewController} zu den inhaltsbasierten Subklassen von \swiftinline{UIViewController}.
\swiftinline{UINavigationController} und \swiftinline{UITabBarController} hingegen sind als \emph{Container View Controller} konzipiert. Sie enthalten selbst nur wenige Subviews, bspw. in Form von Leisten. Stattdessen verwalten sie eine Hierarchie von weiteren View Controllern und präsentieren deren Content Views.
\subsubsection{Navigation Controller}
\swiftinline{UINavigationController} implementiert eine \emph{Stack}\linkref{http://de.wikipedia.org/wiki/Stapelspeicher} Datenstruktur, die in Form eines Attributs \swiftinline{viewControllers} nach außen repräsentiert wird.
Einem Attribut \swiftinline{rootViewController} kann zunächst ein View Controller zugewiesen werden, dessen Content View an erster Stelle der Hierarchie stehen soll. Anschließend kann dem Stack mit Aufrufen der Instanzmethode \swiftinline{pushViewController:animated:} ein View Controller hinzugefügt und \swiftinline{popViewControllerAnimated:} der oberste View Controller entfernt werden.
Jeder von einem Navigation Controller verwaltete View Controller kann über das Attribut \swiftinline{navigationController} auf diesen zugreifen.
\begin{swiftcode}
// Präsentiere einen View Controller
self.navigationController.pushViewController(nextViewController, animated: true)
// Zurück zum vorherigen View Controller
self.navigationController.popViewControllerAnimated(true)
\end{swiftcode}
Die in den Subviews der Content View des Navigation Controllers präsentierten Elemente, bspw. der Titel und die Buttons in Navigationsleiste und Toolbar, sind von dem jeweils präsentierten View Controller abhängig. Jeder View Controller besitzt dafür ein Attribut \swiftinline{navigationItem: UINavigationItem}, das entsprechend konfiguriert werden kann und diese Informationen dem Navigation Controller bereitstellt.
\begin{swiftcode}
self.navigationItem.title = "Titel"
self.title = "Titel" // äquivalente Abkürzung
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "doneButtonPressed:")
\end{swiftcode}
\subsection{View Controller in Storyboards}
Die beschriebenen Mechanismen der View Controller Hierarchie können und sollten zum Großteil in das verwendete Storyboard ausgelagert werden.
Ein Storyboard ist in \emph{Scenes} strukturiert, die jeweils einen View Controller und seine Content View Hierarchie repräsentieren. Aus der Object Library können dem Storyboard View Controller hinzugefügt werden.
Wird ein View Controller ausgewählt, können wir dessen Identität anpassen und die entsprechende Subklasse von \swiftinline{UIViewController} auswählen, die wir implementiert haben \abbref{img:vc_identity}. Zur Laufzeit wird der View Controller dann als Objekt dieser Subklasse instanziert.
\includegraphicsc[\iphonewidth]{img/vc_identity.png}{img:vc_identity}{Im Identity Inspector kann bei ausgewählten View Controller dessen Subklasse ausgewählt werden}
Anschließend stehen die im Code definierten IBOutlets und IBActions der \swiftinline{UIViewController} Subklasse im Interface Builder zur Verfügung und können mit Interfaceelementen verbunden werden.
\subsection{Segues}
Zwischen Scenes vermitteln \emph{Segues}. Diese stellen Beziehungen zwischen View Controllern dar und können zu deren Präsentation verwendet werden \abbref{img:segues}.
\includegraphicsc{img/segues.png}{img:segues}{Segues vermitteln zwischen Scenes}
Ähnlich wie bei IBOutlets, IBActions und Auto Layout Constraints gibt es mehrere Möglichkeiten, Segues im Interface Builder zu erstellen. Dazu gehören bspw. das Ziehen einer Verbindungslinie mit gehaltener \keys{\ctrl}-Taste und die Verwendung des \emph{Triggered Segues} Abschnitts des Connection Inspectors.
Zwischen View Controllern kann es direkte Beziehungen geben, die durch eine \emph{Relationship Segue} gekennzeichnet werden. In dieser Form wird bspw. einem Navigation Controller sein Root View Controller zugewiesen.
Außerdem können Subklassen von \swiftinline{UIControl} wie bspw. \swiftinline{UIButton} bei einem \swiftinline{UIControlEvent} Segues auslösen. So lassen sich Übergänge zwischen View Controllern bereits innerhalb des Storyboards erstellen.
\begin{itemize}
\item \emph{Modal Segues} sind das Äquivalent zum Aufruf der \swiftinline{presentViewController:animated:completion:} Methode.
\item Befindet sich der View Controller des Ausgangspunkts der Segue in der View Hierarchie eines Navigation Controllers, kann eine \emph{Push Segue} als Äquivalent zu der \swiftinline{pushViewController:animated:} Methode erstellt werden.
\item Außerdem kann jeder View Controller \emph{Unwind Segues} zur Verfügung stellen, die innerhalb dessen View Controller Hierarchie verwendet werden können, um zu diesem zurückzukehren.
Dazu ist lediglich die (ggf. leere) Implementierung einer \swiftinline{IBAction} Methode mit genau einem Argument vom Typ \swiftinline{UIStoryboardSegue} im Interface des View Controllers erforderlich. Der Methodenname sollte das Ziel der Unwind Segue beschreiben, also den implementierenden View Controller.
\begin{swiftcode}
@IBAction func unwindToXYZ(segue: UIStoryboardSegue) { ... }
\end{swiftcode}
Die Unwind Segue ist anschließend im Storyboard in jedem View Controller in der Hierarchie des implementierenden View Controllers unter der \emph{Exit} Schaltfläche verfügbar und kann mit einem \swiftinline{UIControlEvent} verbunden werden \abbref{img:unwind_segue}.
\includegraphicsc[\iphonewidth]{img/unwind_segue.png}{img:unwind_segue}{Die \emph{Exit} Schaltfläche stellt alle Unwind Segues in der View Controller Hierarchie zur Verfügung}
\end{itemize}
Im Attributes Inspector können Optionen bezüglich Übergangsanimation und Präsentationsmodus verschiedener Segues gewählt werden. Zusätzlich sollte hier ein Identifier vergeben werden.
Im Code können wir auf das Auslösen einer Segue reagieren, um den View Controller des Ziels entsprechend zu konfigurieren. Dazu überschreiben wir die \swiftinline{prepareForSegue:sender:} Instanzmethode und verwenden den Identifier wie im folgenden Beispiel:
\begin{swiftcode}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
{
if let identifier = segue.identifier {
switch identifier {
case "showDetailSegue":
if let detailViewController = segue.destinationViewController as? DetailViewController {
detailViewController.detailObject = ...
}
default:
break
}
}
}
\end{swiftcode}
\section{Das Delegate Konzept}\label{sec:delegate_konzept}
In der Programmierung komplexerer Apps ist es häufig notwendig, dass Klassen Informationen untereinander austauschen. Das \emph{Delegate Konzept} ist ein einfacher und viel verwendeter Mechanismus der \strong{einseitigen} Kommunikation zwischen Klassen, das einem einfachen \strong{Frage-Antwort-Prinzip} folgt \abbref{img:delegation}.
\includegraphicsc[10cm]{img/delegation.png}{img:delegation}{Das Delegate Konzept funktioniert nach einem einfachen Frage-Antwort-Prinzip}
Die aktive (fragende) Klasse stellt ein \emph{Protokoll} zur Verfügung, das die benötigten Methoden definiert. Außerdem besitzt sie ein öffentliches Attribut, häufig \swiftinline{delegate} genannt, das eine Referenz zu einem Objekt der passiven (antwortenden) Klasse hält. Diese implementiert die Methoden des Protokolls, die in entsprechenden Situationen von der aktiven Klasse aufgerufen werden können. Die aktive Klasse muss dabei keine weiteren Informationen über die passive Klasse haben, außer, dass diese die benötigten Methoden des Protokolls implementiert.
Methoden in einem Protokoll dienen in den meisten Fällen einem von zwei Zwecken:
\begin{itemize}
\item Von dem Delegate Objekt werden Informationen angefordert
\item Das Delegate Objekt wird benachrichtigt, wenn eine bestimmte Situation eintritt.
\end{itemize}
Viele Klassen in Frameworks implementieren Kommunikationsmechanismen in Form von Delegates. Dazu gehört bspw. die im folgenden Abschnitt beschriebene \swiftinline{UITableView}. Wir können jedoch auch eigene Protokolle und Delegates definieren. Protokolle sind tatsächlich so tief in Swift integriert, dass Swift auch die erste \emph{protokollorientierte} (statt \emph{objektorientierte}) Programmiersprache genannt wird.
Betrachten wir dazu das Beispiel eines View Controllers \swiftinline{SelectionViewController: UIViewController}, der präsentiert wird und zur Auswahl eines bestimmten Objekts aus einer Liste dienen soll. Der \swiftinline{SelectionViewController} erhält dafür ein Attribut \swiftinline{delegate}, das die Bedingungen eines \swiftinline{SelectionDelegate} Protokolls erfüllen muss. Dazu gehört eine Methode, die alle Objekte zurückgibt, aus denen ausgewählt werden kann. Der \swiftinline{SelectionViewController} kann diese dann darstellen.\\
Sobald eine Auswahl getroffen wurde, muss diese dem präsentierenden View Controller mitgeteilt werden. Das \swiftinline{SelectionDelegate} definiert dazu eine weitere Methode, die das Delegate implementieren muss und bei einer Auswahl aufgerufen wird. Das Delegate kann sich dann darum kümmern, den \swiftinline{SelectionViewController} wieder zu entfernen und mit dem ausgewählten Objekt weiterarbeiten.
\subsubsection{Aktive Klasse}
\begin{swiftcode}
// SelectionViewController.swift