forked from iOS-Dev-Kurs/Skript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ios_dev_kurs_app_katalog.tex
1695 lines (1155 loc) · 108 KB
/
ios_dev_kurs_app_katalog.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}{App Katalog}
\renewcommand{\shortdoctype}{App Katalog}
\begin{document}
\maketitle
\tableofcontents
\chapter{Einleitung}
\section{Über dieses Dokument}
Dieser App Katalog enthält Schritt-für-Schritt Anleitungen für die im Rahmen unseres Kurses erstellten Apps sowie die wöchentlich zu bearbeitenden Übungsaufgaben und wird im Verlauf des Semesters kapitelweise auf der Vorlesungswebseite \linkref{http://ios-dev-kurs.github.io/} zur Verfügung gestellt.
Er dient jedoch nur als Ergänzung zum parallel verfügbaren \strong{Skript}, auf das hier häufig verwiesen wird. Dort sind die Erläuterungen zu den verwendeten Technologien, Methoden und Begriffen zu finden.
\section{Workflow mit Git}\label{git_workflow}
Wir arbeiten in diesem Kurs mit der Versionskontroll-Software Git und der Software\-entwicklungs-Plattform GitHub \linkref{https://github.com/}. Mit diesen Werkzeugen kann ich euch Beispielprojekte und Aufgaben bereitstellen, die ihr bearbeiten und mir für Kommentare wieder zur Verfügung stellen könnt. Gleichzeitig lernt ihr dabei direkt den Umgang mit zwei der wichtigsten Werkzeuge in der modernen Softwareentwicklung.
Mit Git können wir Änderungen an einem Projekt, oder \emph{Repository}, in regelmäßigen Abständen in \emph{Commits} speichern. Dann können wir jederzeit zu vorherigen Commits zurückkehren und Änderungen vergleichen. Wer die Speicherpunkte bei Super Mario kennt weiß so etwas zu schätzen.
Außerdem ermöglicht uns Git mit anderen Entwicklern zusammenzuarbeiten. Dazu können wir das Repository auf einem Server wie GitHub bereitstellen. Speichert ein anderer Entwickler Commits in dem Repository, können wir dessen Änderungen mit einem \emph{Merge} mit unseren zusammenführen und dabei gegebenenfalls Konflikte beheben. So wird an Softwareprojekten weltweit zusammengearbeitet.
Da Git ein Kommandozeilenprogramm ist, bedarf es sicherlich einer Eingewöhnung. Wenn ihr noch wenig Erfahrung im Umgang mit der Komandozeile habt könnt ihr zum Einstieg die GitHub Desktop App \linkref{https://desktop.github.com} verwenden, mit der ihr Git über eine graphische Oberfläche bedienen könnt.
\subsection{Ein Repository forken, klonen und bearbeiten}\label{fork_and_clone}
Ich stelle Beispielprojekte und Aufgaben in Repositories wie diesem \linkref{https://github.com/ios-dev-kurs/helloworld} bereit. Verfahrt wie folgt, um es zum Bearbeiten herunterzuladen:
\begin{enumerate}
\item Erstellt einen GitHub Account \linkref{https://github.com/join}, wenn ihr noch keinen habt. Ladet euch die GitHub Desktop App herunter, wenn ihr eine graphische Oberfläche der Kommandozeile vorzieht.
\item Ihr habt nur Lese-Zugriff auf mein Repository. Ihr müsst daher erst einen \emph{Fork} \linkref{https://guides.github.com/activities/forking/} des Repositories erstellen und es damit auf euren Account kopieren. Klickt dazu einfach auf den \emph{Fork} Button auf der Repository-Seite.
\item Euren Fork könnt ihr nun nach Belieben bearbeiten. In diesem Beispiel ist das Fork-Repository unter der URL \url{https://github.com/dein-username/helloworld} verfügbar. Ihr könnt die Kommandozeile oder die GitHub Desktop App verwenden um das Repository herunterzuladen, oder zu \emph{klonen} \linkref{http://gitref.org/creating/\#clone}. Im Terminal lautet der Befehl dazu:
\begin{shcode}
git clone https://github.com/dein-username/helloworld
\end{shcode}
\item Nun könnt ihr an dem Projekt arbeiten. Mit folgendem Befehlt könnt ihr jederzeit überprüfen, welche Dateien sich geändert haben:
\begin{shcode}
git status
\end{shcode}
\item Speichert in regelmäßigen Abständen \emph{Commits} \linkref{http://gitref.org/basic/\#commit}. Jeder Arbeitsschritt sollte durch einen Commit repräsentiert werden. Achtet darauf, dass das Projekt bei jedem Commit funktionsfähig ist. In der Kommandozeile erstellt ihr einen Commit wie folgt:
\begin{shcode}
# Status überprüfen
git status
# Alle Änderungen dem nächsten Commit hinzufügen
git add --all
# Commit durchführen
git commit -m "Kurze Beschreibung der Änderungen"
\end{shcode}
\item Ihr könnt euer lokales Repository jederzeit mit eurem Repository auf GitHub abgleichen. Daher könnt ihr auch problemlos auf verschiedenen Rechnern an einem Projekt arbeiten. Führt einen \emph{Push} \linkref{http://gitref.org/remotes/\#push} oder \emph{Pull} \linkref{http://gitref.org/remotes/\#pull} in der Kommandozeile aus, oder klickt den \emph{Sync} Button in der GitHub Desktop App:
\begin{shcode}
# Fortschritt auf GitHub veröffentlichen
git push
# Änderungen von GitHub herunterladen
git pull
\end{shcode}
\end{enumerate}
\subsection{Eine Aufgabe per Pull-Request einreichen}\label{pull_request}
Habt ihr eine Aufgabe fertig und möchtet Sie einreichen, oder wenn ihr Hilfe benötigt, erstellt eine \emph{Pull-Request} \linkref{https://help.github.com/articles/creating-a-pull-request/}. Damit erhalte ich eine Benachrichtigung mit den Änderungen eures Forks im Vergleich zu meinem Original-Repository. Geht wie folgt vor:
\begin{enumerate}
\item Speichert eure Änderungen in einem Commit und veröffentlicht sie auf GitHub, wenn ihr es noch nicht getan habt.
\item Klickt auf der Repository-Seit den Button \emph{New Pull Request}, überprüft die Änderungen und klickt dann \emph{Create Pull Request}.
\item Gebt der Pull-Request einen Titel und beschreibt kurz die Änderungen. Erwähnt, wenn etwas nicht funktioniert, sodass ich euch helfen kann. Klickt schließlich auf \emph{Create Pull Request}.
\end{enumerate}
\begin{lecture} % Lecture 1
\chapter{Hello World}
Was ist schon ein Programmierkurs, der nicht mit einem klassischen \emph{Hello World} Programm beginnt? Wir werden jedoch noch einen Schritt weitergehen und diesen Gruß graphisch vom iOS Simulator und, soweit vorhanden, direkt von unseren eigenen iOS Geräten ausgeben lassen. Dabei stoßen wir auf unseren ersten \emph{Swift} Code und lernen die IDE \emph{Xcode} kennen. Wir arbeiten außerdem direkt mit der Versionskontroll-Software \emph{Git}, einem der Grundbausteine nahezu jedes Softwareprojekts.
\skriptref{Xcode, Programmieren in Swift, Versionskontrolle mit Git} sowie das Buch \emph{The Swift Programming Language} \linkref{https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/}
\section{"{}Hello World!"{} auf Simulator und Gerät}
\begin{enumerate}
\item Ich habe ein Beispielprojekt bereitgestellt, anhand dessen wir einen ersten Blick auf die Programmierung einer iOS App werfen. Das Ziel ist, das Projekt herunterzuladen, eine erste, einfache App zu schreiben und mir das Ergebnis für etwas \emph{konstruktive Kritik} zur Verfügung zu stellen. Dazu verwenden wir die Versionskontroll-Software \emph{Git} und die Softwareentwicklungs-Plattform \emph{GitHub}, die zu den Werkzeugen gehören, auf denen Softwareprojekte weltweit aufbauen und ohne die moderne Programmierung kaum noch denkbar ist.
Die erste Anweisung lautet:
\strong{\emph{Klont} einen \emph{Fork} des \emph{Repositories} \url{https://github.com/ios-dev-kurs/helloworld}.}
Wenn ihr noch mit keinem dieser Begriffe etwas anfangen könnt, seid beruhight: Wir werden noch so viel mit Git und GitHub arbeiten, dass ihr am Ende dieses Kurses Experten im Umgang damit seid. Befolgt zunächst einfach die Anweisungen in \autoref{fork_and_clone} \emph{Workflow mit Git} bis ihr das Beispielprojekt heruntergeladen habt.
\item Öffnet das Xcode-Projekt \filename{HelloWorld.xcodeproj} und macht euch mithilfe des Kapitels \emph{Xcode} im Skript mit der Benutzeroberfläche vertraut. In der Toolbar oben findet ihr auf der linken Seite die Steuerelemente des Compilers. Wählt das \emph{Target} \emph{HelloWorld} und ein Zielsystem, bspw. den \emph{iPhone 6s} Simulator, und klickt die \strong{\emph{Build \& Run}} Schaltfläche. Das Target wird nun kompiliert und generiert ein \emph{Product}, also unsere App, die im Simulator ausgeführt wird. Das Tastenkürzel für \emph{Build \& Run} in Xcode ist \keys{\cmd + R}.
\item Besonders spannend ist diese App natürlich noch nicht. Das ändern wir jetzt spektakulär, indem wir unseren \strong{ersten Swift Code} schreiben um eine Ausgabe hinzuzufügen. Wählt die Datei \filename{AppDelegate.swift} links im \emph{Project Navigator} aus.
\item Die Methode \swiftinline{application(_:didFinishLaunchingWithOptions:)} wird zu Beginn der Ausführung der App aufgerufen. Ersetzt den Kommentar dort mit einem Gruß zur Ausgabe in der Konsole:
\begin{swiftcode}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
print("Hello World!")
return true
}
\end{swiftcode}
\item Wenn wir unsere App nun erneut mit \emph{Build \& Run} \keys{\cmd + R} kompilieren und ausführen, sehen wir den Text \str{Hello World!} in der Konsole. Dazu wird der zweigeteilte Debug-Bereich unten automatisch eingeblendet \abbref{img:helloworld_helloworld}. Ist der Konsolenbereich zunächst versteckt, kann er mit der Schaltfläche in der rechten unteren Ecke angezeigt werden. Außerdem wird links automatisch zum Debug Navigator gewechselt, wenn eine App ausgeführt wird, in dem CPU- und Speicherauslastung überwacht werden können und Fehler und Warnungen angezeigt werden, wenn welche auftreten.
\includegraphicsc[\screenshotwidth]{img/helloworld_helloworld.png}{img:helloworld_helloworld}{In der Konsole des Debug-Bereichs werden Ausgaben der laufenden App angezeigt}
\item Wenn ihr ein iOS Gerät dabei habt, verbindet es per USB-Kabel mit eurem Mac und wählt das Gerät in der Toolbar als Zielsystem aus. Mit einem \emph{Build \& Run} wird die App nun kompiliert, auf dem Gerät installiert und ausgeführt. In der Konsole erscheint wieder die Ausgabe \str{Hello World!}, diesmal direkt vom Gerät ausgegeben.
\end{enumerate}
\section{Graphisches "{}Hello World!"{}}
Natürlich wird ein Benutzer unserer App von den Ausgaben in der Konsole nichts mitbekommen. Diese dienen bei der Programmierung hauptsächlich dazu, Abläufe im Code nachzuvollziehen und Fehler zu finden. Unsere App ist also nur sinnvoll, wenn wir die Ausgaben auch auf dem Bildschirm darstellen können.
\skriptref{Xcode / Interface Builder}
\begin{enumerate}
\item Zur Gestaltung der Benutzeroberfläche oder \emph{User Interface (UI)} verwenden wir ein \emph{Storyboard}. Wählt im Project Navigator die Datei \emph{main.storyboard} aus.
\item Der Editor-Bereich zeigt nun den Interface Builder. In diesem Modus möchten wir häufig eine angepasste Konfiguration des Xcode-Fensters verwenden, es bietet sich also an, mit \keys{\cmd + T} einen neuen Tab zu öffnen. Blendet dann mit den Schaltflächen auf der rechten Seite der Toolbar den Navigator- und Debug-Bereich links und unten aus und den Inspektor rechts ein. Wählt dort außerdem zunächst den Standard-Editor, also die linke der drei Schaltflächen \abbref{img:helloworld_ib}.
\includegraphicsc[\screenshotwidth]{img/helloworld_ib.png}{img:helloworld_ib}{Für den Interface Builder verwenden wir eine angepasste Fensterkonfiguration mit dem Inspektor anstatt des Navigators}
\item Unser UI besteht bisher nur aus einer einzigen Ansicht, oder \emph{Scene}. Ein Pfeil kennzeichnet die Scene, die zum Start der App angezeigt wird. Im Inspektor rechts ist unten die Object Library zu finden. Wählt den entsprechenden Tab aus, wenn er noch nicht angezeigt wird \abbref{img:helloworld_ib}.
\item Durchsucht die Liste von Interfaceelementen nach einem Objekt der Klasse \swiftinline{UILabel}, indem ihr das Suchfeld unten verwendet, und zieht ein Label irgendwo auf die erste Scene. Doppelklickt auf das erstellte Label und tippt \str{Hello World!}.
\item Ein \emph{Build \& Run} mit einem iPhone-Zielsystem zeigt diesen Gruß nun statisch auf dem Bildschirm an.
\item Habt ihr das Label im Interface Builder ausgewählt, zeigt der Inspektor Informationen darüber an. Im \emph{Identity Inspector} könnt ihr euch vergewissern, dass das Objekt, was zur Laufzeit erzeugt wird und das Label darstellt, ein Objekt der Klasse \swiftinline{UILabel} ist. Im \emph{Attributes Inspector} stehen viele Optionen zur Auswahl, mit denen Eigenschaften wie Inhalt, Schrift und Farbe des Labels angepasst werden können.
\item Natürlich möchten wir unser UI zur Laufzeit mit Inhalt füllen und den Benutzer mit den Interfaceelementen interagieren lassen können. Zieht ein \swiftinline{UIButton}- und \swiftinline{UITextField}-Objekt auf die Scene und positioniert sie passend \abbref{img:helloworld_ui}. Mit dem Attributes Inspector könnt ihr dem Button nun den Titel \str{Say Hello!} geben und für das Text Field einen Placeholder \str{Name} einstellen.
\includegraphicsc[.6\textwidth]{img/helloworld_ui.png}{img:helloworld_ui}{Mit einem Text Field, einem Button und einem Label erstellen wir ein simples UI}
\item Damit sich das Layout an jede Bildschirmgröße automatisch anpasst, verwenden wir nun \emph{Auto Layout}. Die Schaltflächen dazu findet ihr in der unteren rechten Ecke des Interface Builder Editors. Markiert mit gedrückter \keys{\cmd}-Taste die drei Interfaceelemente und klickt auf das Linke der Symbole mit dem Titel \emph{Stack}, sodass die Elemente in eine \emph{Stack View} eingebettet werden. Dieses praktische Objekt positioniert die enthaltenen Elemente automatisch relativ zueinander. Wählt die Stack View aus und konfiguriert im Attributes Inspector \emph{Axis Vertical}, \emph{Alignment Fill}, \emph{Distribution Fill} und \emph{Spacing 8}. Zusätzlich müssen wir Regeln aufstellen, wie die Stack View auf dem Bildschirm positioniert werden soll. Dazu erstellen wir \emph{Constraints} mit den beiden mittleren der Auto Layout Schaltflächen. Befestigt die Stack View links und rechts am Rand und zentriert sie vertikal.
\item Zur Laufzeit der App wird für jedes im Storyboard konfigurierte Interfaceelement ein Objekt der entsprechenden Klasse erstellt und dessen Attribute gesetzt. Um nun im Code auf die erstellten Objekte zugreifen und auf Benutzereingaben reagieren zu können, verwenden wir \emph{IBOutlets} und \emph{IBActions}.
Blendet den Inspektor aus und wählt stattdessen den Assistant-Editor (mittlere Schaltfläche) in der Toolbar. Stellt den Modus in der Jump bar auf \emph{Automatic}. Im Assistant wird automatisch die Implementierung des übergeordneten View Controllers eingeblendet \abbref{img:helloworld_assistant}.
\includegraphicsc[\screenshotwidth]{img/helloworld_assistant.png}{img:helloworld_assistant}{Mithilfe des Assistants können Interface-Builder und Code nebeneinander angezeigt werden.}
\item \emph{View Controller} sind Objekte einer Subklasse von \swiftinline{UIViewController}, die jeweils einen Teil der App steuern. Diese sind zentrale Bestandteile einer App, mit denen wir uns noch detailliert beschäftigen werden. Ein erster View Controller zur Steuerung dieser ersten Ansicht ist im Projekt bereits enthalten.
Fügt dieser Klasse \swiftinline{ViewController: UIViewController} Attribute für das \swiftinline{UILabel} und das \swiftinline{UITextField} hinzu und kennzeichnet diese mit \swiftinline{@IBOutlet}. Implementiert außerdem eine mit \swiftinline{IBAction} gekennzeichnete Methode, die aufgerufen werden soll, wenn der Benutzer den \swiftinline{UIButton} betätigt:
\begin{swiftcode}
import UIKit
class ViewController: UIViewController {
@IBOutlet var nameTextfield: UITextField!
@IBOutlet var greetingLabel: UILabel!
@IBAction func greetingButtonPressed(sender: UIButton) {
print("Hello World!")
}
}
@end
\end{swiftcode}
\item Nun zieht mit gedrückter \keys{\ctrl}-Taste eine Linie von dem Textfeld und dem Label im Interface Builder auf das jeweilige Attribut im Code. Die Codezeile wird dabei blau hinterlegt. Zieht außerdem genauso eine Line von dem Button auf die zuvor definierte Methode. Im Connection Inspector könnt ihr die IBOutlets und IBActions eines ausgewählten Objekts betrachten und wieder entfernen. Dieser Prozess ist im Skript noch detaillierter beschrieben.
\item Versucht nun einen \emph{Build \& Run}. Betätigt ihr den Button, wird die Methode ausgeführt und der Gruß \str{Hello World!} in der Konsole ausgegeben!
\item Um die App nun alltagstauglich zu gestalten, muss dieser Gruß natürlich personalisiert und auf dem Bildschirm angezeigt werden. Dazu verwenden wir das Attribut \swiftinline{text} der Klassen \swiftinline{UITextField} und \swiftinline{UILabel} und zeigen einen personalisierten Gruß an, wenn im Text Field ein Name geschrieben steht:
\begin{swiftcode}
@IBAction func greetingButtonPressed(sender: UIButton) {
if let name = nameTextfield.text where !name.isEmpty {
greetingLabel.text = "Hello \(name)!"
} else {
greetingLabel.text = "Hello World!"
}
}
\end{swiftcode}
Nach einem \emph{Build \& Run} erhalten wir unser erstes interaktives Interface, in dem ihr im Textfeld einen Namen eintippen könnt und persönlich begrüßt werdet \abbref{img:helloworld_final}!
\includegraphicsc[\iphonewidth]{img/helloworld_final.png}{img:helloworld_final}{Drücken wir auf den Button, werden wir persönlich begrüßt. Sehr praktisch!}
\item Die App ist fertig! Eure Eltern werden stolz auf euch sein. Gebt mir nun die Gelegenheit, eure Arbeit zu kommentieren. Wir verwendet dazu wieder Git, um die Änderungen, an denen ihr gerade gearbeitet habt, zu speichern, hochzuladen und mir zur Verfügung zu stellen. Befolgt dazu die weiteren Anweisungen in \autoref{pull_request}, bis ihr mir eine \emph{Pull-Request} geschickt habt.
\end{enumerate}
\begin{exc}
\begin{excitem}{simpleui}{Simple UI}{2}
Erstellt einen Fork des Repositories \url{https://github.com/ios-dev-kurs/simpleui} und schreibt eine App mit einigen Interfaceelementen, die etwas sinnvolles tut. Stellt mir das Ergebnis anschließend als Pull-Request zur Verfügung.
Implementiert eines der folgenden Beispiele oder eine eigene Idee. Ich freue mich auf kreative Apps!
\begin{description}
\item[Counter] Auf dem Bildschirm ist ein Label zu sehen, das den Wert eines Attributs \swiftinline{var count: Int} anzeigt, wenn eine Methode \swiftinline{updateLabel} aufgerufen wird. Buttons mit den Titeln \str{+1}, \str{-1} und \str{Reset} ändern den Wert dieses Attributs entsprechend und rufen die \swiftinline{updateLabel}-Methode auf.
\item[BMI] Nach Eingabe von Gewicht $m$ und Größe $l$ wird der Body-Mass-Index\linkref{http://de.wikipedia.org/wiki/Body-Mass-Index} $BMI=m/l^2$ berechnet und angezeigt.
\item[RGB] In drei Textfelder kann jeweils ein Wert zwischen 0 und 255 für die Rot-, Grün- oder Blau-Komponenten eingegeben werden. Ein Button setzt die Hintergrundfarbe \swiftinline{self.view.backgroundColor} entsprechend und ein weiterer Button generiert eine zufällige Hintergrundfarbe. Ihr könnt noch einen \swiftinline{UISwitch} hinzufügen, der einen Timer ein- und ausschaltet und damit die Hintergrundfarbe bei jedem Timerintervall zufällig wechselt (s. Hinweis).
\end{description}
\begin{exchinweise}
\item In der nächsten Vorlesung lernen wir die Objektorientierte Programmierung in Swift systematisch. Orientiert euch für diese Aufgabe an der \emph{HelloWorld} App und versucht die Funktionalität mit den folgenden Hinweisen zu implementieren. Wenn ihr nicht weiter kommt, schickt mir eine Pull-Request mit einem Kommentar und ich helfe euch.
\item Das Attribut \swiftinline{text} von \swiftinline{UILabel} und \swiftinline{UITextField} gibt eine \emph{optionale} Zeichenkette \swiftinline{String?} zurück. Ihr werdet euch mit solchen \emph{Optionals} solange herumärgern, bis ihr sie zu schätzen lernt. Verwendet die \emph{Optional Binding} Syntax um das Optional zu entpacken:
\begin{swiftcode}
if let name = nameTextfield.text {
// name existiert und kann verwendet werden
} else {
// nameTextfield.text hat keinen Wert
}
\end{swiftcode}
\item Einen \swiftinline{String} könnt ihr schnell in eine ganze Zahl \swiftinline{Int} oder eine Dezimalzahl \swiftinline{Double} umwandeln. Da dies fehlschlagen kann, gibt auch diese Operation einen Optional \swiftinline{Int?} bzw. \swiftinline{Double?} zurück, den wir entpacken müssen:
\begin{swiftcode}
// Sei text ein String
if let number = Double(text) {
// text konnte in eine Zahl number umgewandelt werden
}
\end{swiftcode}
\item Definiert ein Attribut wie \swiftinline{var count: Int} mit einem Startwert:
\begin{swiftcode}
class ViewController: UIViewController {
var count: Int = 0
// ...
}
\end{swiftcode}
\item Natürlich gibt es die grundlegenden Rechenoperationen \swiftinline{+-*/} in Swift. Diese Operationen können mit der Zuweisung zu einer Variablen verbunden werden, um bspw. eine Variable \swiftinline{count} um \swiftinline{1} zu erhöhen:
\begin{swiftcode}
count += 1
\end{swiftcode}
\item Eine Farbe wird durch die Klasse \swiftinline{UIColor} repräsentiert. Der \emph{Initializer} \swiftinline{UIColor(red:green:blue:alpha:)} akzeptiert jeweils Werte zwischen 0 und 1:
\begin{swiftcode}
let color = UIColor(red: 1, green: 0, blue: 0, alpha: 1) // rot
\end{swiftcode}
\item Die Funktion \swiftinline{arc4random_uniform(n)} gibt eine Pseudozufallszahl $x$ mit $0<=x<n$ aus.
\item Wenn ein \swiftinline{UISwitch} betätigt wird, kann das Event genauso mit einer IBAction verbunden werden wie das eines \swiftinline{UIButton}. Mit einem Attribut \swiftinline{var randomTimer: NSTimer?} können wir dann die Methode für das zufällige Wechseln der Hintergrundfarbe implementieren:
\begin{swiftcode}
var randomTimer: NSTimer?
@IBAction func switchValueChanged(sender: UISwitch) {
if sender.on {
randomTimer = NSTimer.scheduledTimerWithTimeInterval(0.15, target: self, selector: "randomButtonPressed:", userInfo: nil, repeats: true)
} else {
randomTimer?.invalidate()
randomTimer = nil
}
}
\end{swiftcode}
Somit wird periodisch die Methode \swiftinline{randomButtonPressed(_:)} aufgerufen, die natürlich implementiert sein muss.
\end{exchinweise}
\end{excitem}
\end{exc}
\end{lecture}
\end{document} %%% MARKER %%%
\begin{lecture}
\section{Grundlagen der Programmierung in Swift}
Anhand des ersten Kapitels \emph{A Swift Tour} des Buches \emph{The Swift Programming Language} lernen wir zunächst die Grundlagen der Programmierung in Swift kennen.
\begin{enumerate}
\item Öffnet Xcode und erstellt zunächst einen \emph{Playground} mit \keys{\cmd + \shift + \Alt + N}. Playgrounds sind interaktive Skripte, mit denen sich ideal Code ausprobieren lässt. Gebt der Datei einen Namen wie \str{01 - Grundlagen der Programmierung in Swift} und speichert sie in einem Verzeichnis für diesen Kurs.
\item Ein Playground besteht aus einem Editor- und einem Inspektorbereich und führt geschriebenen Code automatisch aus. Ausgaben und Laufzeitinformationen werden im Inspektor angezeigt. In nur einer Zeile Code können wir den traditionellen \emph{Hello World!}-Gruß ausgeben lassen \abbref{img:playground_helloworld}.
\begin{swiftcode}
println("Hello World!")
\end{swiftcode}
\item Nun lernen wir anhand des ersten Kapitels \emph{A Swift Tour} des Buches \emph{The Swift Programming Language} zunächst die Grundlagen der Programmierung in Swift.
Auf der Vorlesungswebseite findet ihr den Playground aus der Vorlesung, der in diese Konzepte einführt. Macht euch dabei mit folgenden Begriffen vertraut:
\begin{itemize}
\item Variablen (\swiftinline{var}) und Konstanten(\swiftinline{let})
\item Einfache Datentypen (\swiftinline{Int}, \swiftinline{Float}, \swiftinline{Double}, \swiftinline{Bool}, \swiftinline{String}, \swiftinline{Array}, \swiftinline{Dictionary} und \swiftinline{Set})
\item Type Inference
\item String-Formatierung
\item Einfache Operatoren (\swiftinline{+}, \swiftinline{-}, \swiftinline{*}, \swiftinline{/}, \swiftinline{\%})
\item Abfragen (\swiftinline{if}, \swiftinline{switch}) und Schleifen (\swiftinline{for}, \swiftinline{while})
\item Optionals
\item Funktionen
\end{itemize}
Im zweiten Kapitel \emph{Language Guide} in \emph{The Swift Programming Language} werden diese Konzepte noch einmal detailliert erklärt. Informiert euch dort gerne genauer darüber. Zunächst genügt es jedoch, einen Überblick zu erhalten. Im Verlauf des Kurses werden wir noch viel Übung im Umgang mit diesen Konzepten bekommen.
\includegraphicsc[\screenshotwidth]{img/playground_helloworld.png}{img:playground_helloworld}{Playgrounds eignen sich ideal zum Ausprobieren von Swift Code.}
\end{enumerate}
\begin{exc}
\begin{excitem}{fibonacci}{Fibonacci}{1}
\begin{enumerate}
\item Schreibt einen Algorithmus, der alle Folgenglieder $F_n < 1000$ der Fibonaccifolge
\begin{equation}
F_n = F_{n-1} + F_{n-2}
\end{equation}
\begin{equation}
F_1=1, F_2=2
\end{equation}
in der Konsole ausgibt.
\item \excextra{Bei jeder geraden Fibonaccizahl $F_j$ ist der Abstand $\Delta n=j-i$ zum vorherigen geraden Folgenglied $F_i$ auszugeben.}
\end{enumerate}
\end{excitem}
\begin{excitem}{primenumbers}{Primzahlen}{2}
\begin{enumerate}
\item Schreibt eine Funktion \swiftinline{primeNumbersUpTo:}, die ein Argument \swiftinline{maxNumber: Int} annimmt und alle Primzahlen bis \swiftinline{maxNumber} als Liste \swiftinline{[Int]} zurückgibt.
\exchinweis{Mit dem Modulo-Operator \swiftinline{|\%|} kann der Rest der Division zweier Integer gefunden werden:}
\begin{swiftcode}
let a = 20%3 // a ist jetzt Int(2)
\end{swiftcode}
\item \emph{Optionals} sind eines der elegantesten Konzepte in Swift, und sind auch in anderen modernen Sprachen zu finden. Informiert euch darüber im Kapitel \emph{Language Guide > The Basics > Optionals} in \emph{The Swift Programming Language}. Dieses Kapitel (bis einschließlich \emph{Implicitly Unwrapped Optionals}) ist sehr wichtig, da wir in der iOS App Programmierung häufig mit Optionals arbeiten werden!
\item Verwendet eure Liste von Primzahlen aus der vorigen Aufgabe, um effizienter zu prüfen, ob eine Zahl eine Primzahl ist.
Schreibt dazu eine Funktion \swiftinline{isPrimeNumber:cachedPrimeNumbers:}, die eine Zahl \swiftinline{n: Int} und eine \strong{optionale} Liste von Primzahlen \swiftinline{cachedPrimeNumbers: [Int]?} annimmt. Verwendet die \emph{Optional Binding} Syntax \swiftinline{if let} um mit dieser Liste zu arbeiten, wenn eine solche übergeben wurde und lang genug ist. Dann genügt es zu prüfen, ob die Zahl in der Liste enthalten ist. Wenn kein Liste übergeben wurde, soll die Primzahl wie in a) manuell geprüft werden.
\begin{exchinweise}
\item Dem Argument \swiftinline{cachedPrimeNumbers} können wir einen \emph{default} Wert \swiftinline{nil} zuweisen:
\begin{swiftcode}
func isPrimeNumber(n: Int, cachedPrimeNumbers: [Int]? = nil) -> Bool {
// ...
}
\end{swiftcode}
So kann die Funktion auch ohne dieses Argument aufgerufen werden:
\begin{swiftcode}
isPrimeNumber(7, cachedPrimeNumbers: [ 1, 2, 3, 7 ]) // vollständiger Funktionsaufruf, verwendet übergebene Liste zum Nachschlagen
isPrimeNumber(7) // äquivalent zu:
isPrimeNumber(7, cachedPrimeNumbers: nil) // Prüft Primzahl manuell
\end{swiftcode}
\item Die globale Funktion \swiftinline{contains} prüft ob eine Element in einer Liste enthalten ist:
\begin{swiftcode}
contains([ 1, 2, 3, 7 ], 7) // true
\end{swiftcode}
\item Testet eure Funktion, indem Ihr bspw. folgenden Code ans Ende des Storyboards setzt:
\begin{swiftcode}
//: ## Testing
let n = 499 // Number to test
let cachedMax = 500 // Prime numbers up to this number will be cached
import Foundation
var startDate: NSDate
startDate = NSDate()
let cachedPrimeNumbers = primeNumbersUpTo(cachedMax)
println("Time for caching prime numbers up to \(cachedMax): \(-startDate.timeIntervalSinceNow)s")
startDate = NSDate()
if isPrimeNumber(n) {
println("\(n) is a prime number.")
} else {
println("\(n) is not a prime number.")
}
let withoutCacheTime = -startDate.timeIntervalSinceNow
println("Time without cache: \(withoutCacheTime)s")
startDate = NSDate()
isPrimeNumber(n, cachedPrimeNumbers: cachedPrimeNumbers)
let withCacheTime = -startDate.timeIntervalSinceNow
println("Time with cache: \(withCacheTime)s (\((1 - withCacheTime / withoutCacheTime) * 100)% faster)")
\end{swiftcode}
\end{exchinweise}
\end{enumerate}
\end{excitem}
\end{exc}
\end{lecture}
\begin{lecture} % Lecture 2
\section{Objektorientiertes "{}Hello World!"{}}
\begin{enumerate}
\item Nun versuchen wir uns an der objektorientierten Programmierung und möchten den \objcinline{Hello World!} Gruß von virtuellen Repräsentationen einzelner Personen ausgeben lassen. Erstellt dazu einen Xcode Playground bspw. mit Titel \str{02 - Objektorientierte Programmierung in Swift}.
\item Verschafft euch anhand \emph{The Swift Programming Language} und dem Playground aus der Vorlesung (auf der Vorlesungswebseite) einen Überblick über folgende Konzepte der objektorientierten Programmierung in Swift:
\begin{itemize}
\item Klassen und Objekte
\item Attribute mit oder ohne Startwert
\item Initializer
\item Instanz- und Klassenmethoden
\item Subklassen und Überschreiben von Methoden
\item Structs und Enums
\end{itemize}
\end{enumerate}
\begin{exc}
\begin{excitem}{scientists}{Scientists}{1}
\begin{enumerate}
\item Erstellt (am besten in einem neuen Playground, in den ihr die Klasse \swiftinline{Person} aus der Vorlesung einfügt) eine weitere Klasse \swiftinline{Scientist} als \emph{Subklasse} von \swiftinline{Person}.
Wissenschaftler können rechnen, fügt dieser Klasse also eine Methode \swiftinline{sayPrimeNumbersUpTo:} hinzu, die ein Argument \swiftinline{maxNumber: Int} annimmt und alle Primzahlen bis zu dieser Zahl in der Konsole ausgibt. Verwendet dazu den Algorithmus aus der vorherigen Übungsaufgabe \excref{exc:primenumbers}.
\begin{hinweis}
Wie in \emph{The Swift Programming Language} beschrieben, erbt eine Subklasse die Attribute und Methoden ihrer Superklasse und kann diese überschreiben:
\begin{swiftcode}
class Scientist: Person {
...
}
\end{swiftcode}
\end{hinweis}
\item Wir wollen uns vergewissern, dass die Klasse \swiftinline{Scientist} die Attribute und Methoden ihrer Superklasse \swiftinline{Person} erbt. Erstellt ein \swiftinline{Scientist}-Objekt, gebt ihm einen Namen und lasst den \swiftinline{Hello World}-Gruß ausgeben.
\item Nach dem Prinzip des \emph{Überschreiben} soll ein Wissenschaftler einen anderen Gruß ausgeben als eine "{}normale"{} Person. Überschreibt in der \swiftinline{Scientist}-Klasse die Methode \swiftinline{sayHello}, sodass zusätzlich \str{Ask me for prime numbers!} ausgegeben wird.
\end{enumerate}
\end{excitem}
\begin{excitem}{emails}{Poker}{3}
In dieser Aufgabe berechnen wir die Wahrscheinlichkeit für einen \emph{Flush} beim Poker.
\begin{enumerate}
\item Zunächst modellieren wir die Spielkarten. Eine Karte hat immer eine \emph{Farbe/Suit} (\emph{Karo/Diamonds}, \emph{Herz/Hearts}, \emph{Pik/Spades} oder \emph{Kreuz/Clubs}) und einen \emph{Rang/Rank} (\emph{2} bis \emph{10}, \emph{Bube/Jack}, \emph{Dame/Queen}, \emph{König/King} oder \emph{Ass/Ace}).
Schreibt zwei Enums \swiftinline{enum Suit: Int} und \swiftinline{enum Rank: Int} mit ihren entsprechenden Fällen (\swiftinline{case Diamonds} usw.). Bei den Rängen \emph{2} bis \emph{10} schreibt ihr am besten die Zahl aus. Implementiert jeweils eine \emph{Computed Property} \swiftinline{var symbol: String}, in der ihr mithilfe einer \swiftinline{switch}-Abfrage für jeden Fall ein Symbol zurückgebt. \strong{Tipp:} Für die Farben gibt es Unicode-Symbole\linkref{http://en.wikipedia.org/wiki/Playing_cards_in_Unicode}!
Schreibt dann einen \swiftinline{struct Card} mit zwei Attributen \swiftinline{let suit: Suit} und \swiftinline{let rank: Rank}, sowie einer \emph{Computed Property} \swiftinline{var description: String}, die einen aus Farbe und Rang zusammengesetzten String zurückgibt.
\item Nun können wir eine Poker Hand modellieren. Schreibt den \swiftinline{struct PokerHand} mit einem Attribut \swiftinline{let cards: [Card]} und einer \emph{Computed Property} \swiftinline{var description: String}, die die \swiftinline{description} der Karten kombiniert.
Um einfach zufällige Poker Hände generieren zu können, implementiert einen Initializer \swiftinline{init()}, der eine Hand aus fünf zufälligen Karten erstellt. \strong{Wichtig:} Da aus einem Deck von paarweise verschiedenen Karten gezogen wird, darf keine Karte doppelt vorkommen.
\begin{exchinweise}
\item Da wir \swiftinline{Suit} und \swiftinline{Rank} von \swiftinline{Int} abgeleitet haben, können wir Zufallszahlen generieren und die Enums daraus erstellen:
\begin{swiftcode}
let rndSuit = Suit(rawValue: Int(arc4random_uniform(4)))!
let rndRank = Rank(rawValue: Int(arc4random_uniform(13)))!
let rndCard = Card(suit: rndSuit, rank: rndRank) // Eine zufällige Spielkarte
\end{swiftcode}
\item Die Funktion \swiftinline{contains} könnte hilfreich sein, um das Vorhandensein von Karten zu überprüfen. Um diese mit \swiftinline{Card} verwenden zu können, müsst ihr erst eine Äquivalenzrelation implementieren: Schreibt \swiftinline{struct Card: Equatable { ... }} und dann außerhalb des Struct:
\begin{swiftcode}
func ==(lhs: Card, rhs: Card) -> Bool {
return lhs.suit == rhs.suit && lhs.rank == rhs.rank
}
\end{swiftcode}
\end{exchinweise}
\item Erstellt ein paar Poker Hände und lasst euch die \swiftinline{description} ausgeben. Habt ihr etwas gutes gezogen?
Implementiert nun ein weiteres Enum \swiftinline{enum Ranking: Int} mit den Fällen \swiftinline{case HighCard, Flush, StraightFlush} usw., die ihr bspw. auf Wikipedia\linkref{http://en.wikipedia.org/wiki/List_of_poker_hands} findet.
Fügt dann dem \swiftinline{struct PokerHand} eine Computed Property \swiftinline{var ranking: Ranking} hinzu. Implementiert hier einen Algorithmus, der prüft, ob ein \emph{Flush} vorliegt. Dann soll \swiftinline{.Flush} zurückgegeben werden, ansonsten einfach \swiftinline{.HighCard}.
\item Wir können nun einige tausend Hände generieren und die Wahrscheinlichkeit für einen Flush abschätzen. Fügt einfach folgenden Code am Ende des Playgrounds ein:
\begin{swiftcode}
var rankingCounts = [Ranking : Int]()
let samples = 1000
for var i=0; i<samples; i++ {
let ranking = PokerHand().ranking
if rankingCounts[ranking] == nil {
rankingCounts[ranking] = 1
} else {
rankingCounts[ranking]!++
}
}
for (ranking, count) in rankingCounts {
println("The probability of being dealt a \(ranking.description) is \(Double(count) / Double(samples) * 100)%")
}
\end{swiftcode}
Die Ausführung kann etwas dauern, justiert ggfs. \swiftinline{samples}. Stimmt die Wahrscheinlichkeit etwa mit der Angabe auf Wikipedia überein?
\item \strong{Extra:} Ihr könnt das Programm nun noch erweitern und versuchen, die anderen Ränge zu überprüfen. Dabei könnten Hilfsattribute wie \swiftinline{var hasFlush: Bool} oder \swiftinline{var pairCount: Int} nützlich sein. Bekommt es jemand es jemand hin, eine Funktion zu schreiben, die zwei Hände vergleicht und den Sieger bestimmt? \strong{Tipp:} Dazu könnte es hilfreich sein, die Fälle des \swiftinline{enum: Ranking} um \emph{Associated Attributes} zu erweitern.
\end{enumerate}
\end{excitem}
\end{exc}
\end{lecture}
\begin{lecture} % Lecture 4
\chapter{Versionskontrolle mit Git}
Im Skript sind die Vorzüge der Versionskontrolle mit Git beschrieben, deren Grundlagen wir anhand einer unserer Apps anwenden können.
\skriptref{Versionskontrolle mit Git}
\begin{enumerate}
\item Da Git in erster Linie ein Kommandozeilenprogramm ist, verwenden wir zunächst die Konsole. Später könnt ihr stattdessen auch bspw. die in Xcode integrierten Benutzeroberflächen verwenden. Öffnet die \emph{Terminal} App und navigiert in den Ordner, der das Xcode Projekt der \emph{Hello World} App beinhaltet. Dazu kann es hilfreich sein, zunächst \shinline{cd} zu tippen und den Ordner dann auf das Terminal Fenster zu ziehen, wobei der Pfad automatisch eingegeben wird.
\begin{shcode}
cd path/to/project
\end{shcode}
\item Bei Erstellung des Projektes wurde möglicherweise bereits die Option ausgewählt, ein Git Repository zu initialisieren. Mit \shinline{git status} können wir prüfen, ob hier bereits eines existiert und es ansonsten mit \shinline{git init} erstellen.
\begin{shcode}
git status
# Git Repository vorhanden:
>> On branch master
>> nothing to commit, working directory clean
# kein Git Repository vorhanden:
>> fatal: Not a git repository (or any of the parent directories): .git
git init
\end{shcode}
\item Wir fügen dem Repository nun zunächst eine \shinline{.gitignore} Datei hinzu, um verschiedene benutzerspezifische und temporäre Dateien des Xcode Projekts auszuschließen.
\begin{shcode}
touch .gitignore
open .gitignore
\end{shcode}
Kopiert 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.
Nun können wir einen Commit ausführen, um die Datei dem Repository hinzuzufügen.
\begin{shcode}
git add .gitignore
git commit -m "Added .gitignore file"
\end{shcode}
\item Jederzeit kann es hilfreich sein, die Situation des Git Repositories mit \shinline{git status} zu überprüfen. Zeigt ein Aufruf dieses Befehls noch ungesicherte Änderungen an, könnt ihr diese in einem weiteren Commit sichern:
\begin{shcode}
git add --all
git commit -m"Committed unsaved changes"
\end{shcode}
\shinline{git log} zeigt die letzten Commits in der Konsole an.
\item Nun können wir an unserem Projekt weiterarbeiten und Änderungen an Dateien vornehmen. Öffnet bspw. die Datei \emph{ViewController.swift} und fügt ihrer Implementierung folgendes Codesegment hinzu:
\begin{swiftcode}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.redColor()
}
\end{swiftcode}
Führt ihr die App nun aus, seht ihr einen roten Bildschirmhintergrund.
In der Konsole sehen wir mit \shinline{git status}, dass nun Änderungen vorliegen. Diese können wir in Form eines Commits im Git Repository speichern.
\begin{shcode}
git add ViewController.swift # oder git add --all
git commit -m "changed background color"
\end{shcode}
\item Es gibt viele verschiedene Möglichkeit der Commitnavigation und -manipulation. Wir können bspw. mit \shinline{git checkout} einen bestimmten Commit laden, mit \shinline{git reset} Commits entfernen oder sie mit \shinline{git revert} in Form eines neuen Commits rückgängig machen. Dabei können wir einen bestimmten Commit anhand seines SHA hashs identifizieren, der bspw. mit \shinline{git log} angezeigt wird, oder mit \shinline{HEAD~x} den x-letzten Commit auswählen. Setzen wir den soeben ausgeführten Commit nun also bspw. zurück:
\begin{shcode}
git reset --hard HEAD~1 # Verwendet diesen Befehl nicht leichtfertig, denn hier gibt es keine Undo-Funtion!
\end{shcode}
In der Dokumentation \linkref{http://git-scm.com/book/} kann sich ausführlich über die verschiedenen Möglichkeiten informiert werden.
\item Häufig wird Git zur Projektstrukturierung in Form von \strong{Feature Branches} eingesetzt. Nehmen wir also an unser Projekt liegt in seiner veröffentlichten Form vor. Möchten wir nun ein neues Feature implementieren oder Umstrukturierungen vornehmen, erstellen wir zunächst einen neuen Branch mit \shinline{git branch}. Mit \shinline{git checkout} wechseln wir das Arbeitsverzeichnis in diesen neuen Branch.
\begin{shcode}
git branch new_feature
git checkout new_feature
\end{shcode}
\item Nun können wir in diesem Branch an unserem Projekt arbeiten, ohne andere Branches wie den zuvor verwendeten \shinline{master} Branch zu verändern. Implementiert wieder eine Änderung und führt einen Commit durch:
\begin{swiftcode}
override func viewDidAppear(animated: Bool) {
super.viewDidLoad()
let alertController = UIAlertController(title: "New Feature!", message: "Is it a bug or a feature?", preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "Bug", style: .Destructive, handler: nil))
alertController.addAction(UIAlertAction(title: "Feature", style: .Default, handler: nil))
self.presentViewController(alertController, animated: animated, completion: nil)
}
\end{swiftcode}
\begin{shcode}
git commit -a -m "implemented new feature" # Der -a Flag fügt dem Commit automatisch alle veränderten Dateien hinzu
\end{shcode}
\item Erhalten wir nun plötzlich eine Email eines aufgebrachten Benutzers unserer App, der einen Fehler gefunden hat, können wir problemlos zurück zum \shinline{master} Branch wechseln und diesen schnell beheben:
\begin{shcode}
git checkout master
\end{shcode}
Nachdem wir zurück zum \shinline{master} Branch gewechselt haben, sehen wir, dass die Änderungen des \shinline{new_feature} Branches verschwunden sind. Stattdessen befindet sich der Code wieder in seinem ursprünglichen Zustand. Wir können den Fehler also beheben, einen Commit ausführen und die App in der neuen Version veröffentlichen, um den Emailschreiber zu besänftigen.
Anschließend wechseln wir wieder in unseren Feature Branch und arbeiten dort weiter, wo wir unterbrochen wurden.
\begin{shcode}
git checkout new_feature
\end{shcode}
\item Befindet sich der Feature Branch in einem Zustand, der veröffentlicht werden soll, muss er nur mit dem \shinline{master} Branch vereinigt werden. Dazu führen wir einen Merge durch.
\begin{shcode}
git checkout master
git merge new_feature
\end{shcode}
Wurden im \shinline{master} Branch keine weiteren Commits hinzugefügt, können die Commits des Feature Branches einfach angehängt werden (\emph{Fast-Forward}). Gehen die Branches jedoch auseinander, versucht Git, die Änderungen zusammenzuführen. Mögliche Konflikte müssen dabei wie im Skript beschrieben im Code behoben werden.
Der Feature Branch kann anschließend gelöscht werden:
\begin{shcode}
git branch -d new_feature
\end{shcode}
\item Versionskontrolle ist nicht nur für größere Projekte essentiell und hilft bei der Entwicklung jeder iOS App, sondern ermöglicht auch die Zusammenarbeit mehrerer Entwickler an einem Projekt. Im Skript ist dieses Thema kurz beschrieben und auch auf den Service GitHub \linkref{http://www.github.com} verwiesen, der weltweit von Entwicklern verschiedenster Plattformen verwendet wird, um an Projekten zusammenzuarbeiten.
Auf GitHub ist bspw. auch dieses Skript zu finden. Ihr könnt das Repository auf der Webseite einsehen \linkref{https://github.com/iOS-Dev-Kurs/Skript} und herunterladen.
Navigiert dazu in der Konsole zu dem Verzeichnis eurer Projekte für diesen Kurs und führt einen \shinline{git clone} aus:
\begin{shcode}
cd path/to/directory
git clone https://github.com/iOS-Dev-Kurs/Skript
\end{shcode}
Das Repository wird dabei in das angegebene Verzeichnis heruntergeladen. Im Unterverzeichnis \shinline{dist/} findet ihr die gesetzten Dokumente dieses Kurses in aktueller Version. Auch die Links auf der Vorlesungsseite verlinken auf diese Dateien.
Prinzipiell könnt ihr nun lokal an dem Repository weiterarbeiten und Commits durchführen, diese jedoch nicht hochladen. Zur Zusammenarbeit an Programmierprojekten werden entweder entsprechende Berechtigungen vergeben, oder das \strong{Fork} System auf GitHub verwendet.
Wenn ich nun Änderungen am Remote Repository auf Github vornehme, also bspw. eine neue Version dieses Skript veröffentliche, können ihr diese mit nur einem Befehl laden und mit dem lokalen \shinline{master} Branch zusammenführen:
\begin{shcode}
git pull
\end{shcode}
\end{enumerate}
\begin{exc}
\begin{excitem}{chatter}{Chatter}{2}
In dieser Aufgabe schreiben wir zusammen an einer App!
\begin{exchinweis}
Aus naheliegenden Gründen funktioniert es insgesamt besser, wenn ihr diese Aufgabe nicht alle erst am Sonntagabend erledigt…
\end{exchinweis}
\begin{enumerate}[label=\roman*.]
\item Auf GitHub verwalte ich das Repository der \emph{chatter} App \linkref{https://github.com/iOS-Dev-Kurs/chatter}. Ihr könnt es zwar in dieser Form mit \shinline{git clone} herunterladen, jedoch keine Änderungen im Original-Repository auf dem Server veröffentlichen. Stattdessen benötigt ihr einen eigenen \strong{Fork}, mit dem ihr arbeiten könnt. Erstellt dazu zunächst einen GitHub Account \linkref{https://github.com/join}, wenn ihr noch keinen habt.
\item Loggt euch mit eurem GitHub Account ein und befolgt die Anweisungen der GitHub Dokumentation \linkref{https://help.github.com/articles/fork-a-repo}, um einen Fork des \emph{chatter} Repositories \linkref{https://github.com/iOS-Dev-Kurs/chatter} zu erstellen. Dort ist insbesondere auch beschrieben, wie ihr das Repository anschließend lokal klont und das Original-Repository als Remote \shinline{upstream} hinzufügt.
\item Ihr habt das Projekt nun lokal auf eurem Mac. Es ist sowohl mit eurem Fork des Repositories (\shinline{origin}) als auch mit meinem Original-Repository (\shinline{upstream}) verbunden. Ihr könnt jetzt jederzeit die Änderungen herunterladen, die ich oder andere Teilnehmer unseres Kurses am Original-Repository vornehmen, indem ihr von \shinline{upstream} pullt:
\begin{shcode}
git pull --rebase upstream master # Ein Rebase ist hier angebrachter als ein Merge, im Skript ist der Unterschied kurz beschrieben
\end{shcode}
Dies solltet ihr häufig tun, unter anderem immer bevor ihr beginnt, an dem Projekt zu arbeiten.
\item Falls ihr es noch nicht getan habt, solltet ihr euren Namen und eure GitHub Email der Git Konfiguration hinzufügen, sodass die Commits ordentlich eurem Account zugeordnet werden
\begin{shcode}
git config --global user.name "__dein_name__"
git config --global user.email __deine_github_email___
\end{shcode}
\item Öffnet nun das Projekt in Xcode. Die \emph{chatter} App ist in der im Repository enthaltenen \emph{README.md} Datei beschrieben. Diese und die Kommentare im Code sollen euch bezüglich der App als Referenz dienen. Ihr könnt euch die Projektdateien anschauen und die App im Simulator oder auf euren Geräten ausführen und ausprobieren.
\item Ihr habt nun sicherlich erkannt, worum es in der App geht: Instanzen verschiedener Subklassen von \swiftinline{Chatter} chatten miteinander. Dabei überschreiben die Subklassen jeweils nur die Implementierung weniger Methoden, die in der \swiftinline{Chatter} Klasse dokumentiert sind.
Eure Aufgabe ist es nun, eine eigene Subklasse zu schreiben und sie mithilfe von Git den anderen zur Verfügung zu stellen! Ihr könnt bspw. versuchen, einen bekannten Charakter darzustellen, oder etwas völlig neues erschaffen. Lasst eurer Kreativität freien Lauf!
Erstellt dazu einfach eine neue Subklasse von \swiftinline{Chatter} mit dem Namen eures Charakters und platziert die \shinline{.swift}-Datei im Xcode Projekt Navigator unter \menu{chatter > Model > Chatters}.
\item Überschreibt nun die relevanten Methoden wie in der \emph{README.md} Datei beschrieben. Hier könnt ihr einfach zufällige Chatnachrichten generieren, oder auch komplexere Mechaniken einbauen, sodass eine etwas natürlichere Konversation zustande kommt.
In eurer eigenen Subklasse könnt ihr dabei beliebig Code schreiben und bspw. Attribute einführen, um den Zustand eures Charakters darzustellen, wenn ihr möchtet. Wenn es nötig ist, könnt ihr auch die \objcinline{Message} Klasse leicht anpassen. Achtet dabei jedoch unbedingt darauf, dass der Code für die anderen ebenfalls noch funktionieren muss!
\item Sichert eure Änderungen regelmäßig in Commits, wenn der Code fehlerfrei kompiliert:
\begin{shcode}
git add filename # Achtet bitte darauf, nur Änderungen eurer Subklasse und nur wenn nötig Änderungen in anderen Dateien zu committen. Die project.pbxproj Datei enthält Informationen zu den Projektdateien - da ihr neue Dateien hinzugefügt habt, müsst ihr diese auch committen.
git status # häufig den Status prüfen
git commit -m"describe your changes here"
\end{shcode}
\item Die neuen Commits entstehen zunächst nur lokal. Mit eurem Fork des Repositories auf GitHub könnt (und solltet) ihr diese jedoch jederzeit abgleichen. Bei der Gelegenheit bietet es sich an, wie zuvor beschrieben zunächst die neuesten Änderungen aus dem Original-Repository herunterzuladen:
\begin{shcode}
git pull --rebase upstream master
\end{shcode}
Das Repository enthält dann sowohl den aktuellen Stand des Original-Repositories, als auch eure Änderungen. Dies könnt ihr auf euren Fork auf GitHub hochladen:
\begin{shcode}
git push origin master
\end{shcode}
\item Wenn ihr mit eurer neuen \swiftinline{Chatter} Subklasse zufrieden sein, schickt mir eine \emph{Pull Request}. So werden eure Änderungen in das Original-Repository integriert und tauchen auch bei den anderen Teilnehmern auf, wenn diese das nächste mal einen \shinline{git pull} durchführen.
GitHub beschreibt das System der Pull Requests recht ausführlich in ihrer Dokumentation \linkref{https://help.github.com/articles/using-pull-requests}. Wie dort beschrieben, müsst ihr den Prozess dazu mit Klick auf den \emph{Compare \& review} Button initiieren, noch einmal die Änderungen prüfen und anschließend absenden. Ich erhalte dann eine Benachrichtigung und muss dem Merge mit dem Original-Repository nur noch großzügig zustimmen.
Ich bin gespannt auf eure Implementierungen!
\end{enumerate}
\end{excitem}
\end{exc}
\end{lecture}
\begin{lecture} % Lecture 5
\chapter{View Hierarchie}
Die iOS App Entwicklung orientiert sich konsequent am \emph{Model-View-Controller Konzept} der Programmierung. Es ist nicht nur in Apples Frameworks wie \swiftinline{UIKit} rigoros umgesetzt sondern stellt auch die Grundlage für die weitere Konzeption unserer Apps dar und wird auch in vielen anderen Bereichen der Softwareentwicklung verwendet. Das Konzept ist im Skript beschrieben und sollte bei Entscheidungen zur Architektur einer App stets als Referenz verwendet werden.
\skriptref{Das Model-View-Controller Konzept}
Wir betrachten nun zunächst die \emph{View} Komponente des Konzepts. Im Skript wird die \swiftinline{UIView} Klasse des \swiftinline{UIKit} Frameworks vorgestellt, die mit ihren Subklassen die Anzeige von Interfaceelementen auf dem Bildschirm übernimmt. Wir werden in diesem Kapitel die \emph{View Hierarchie} kennenlernen, \swiftinline{UIView} Objekte erstellen und anzeigen, sowie mithilfe des \emph{Auto Layout} Konzepts das User Interface dynamisch anpassen.
\section{Handgeschriebene View Hierarchie}
\mvcindicatorview
\skriptref{View Hierarchie}
\begin{enumerate}
\item Ihr könnt das MVC-Konzept noch nicht im Schlaf rezitieren? Unbedingt erstmal im Skript nachlesen!
\item Zuvor haben wir für Xcode Projekte ein Template mit vorkonfiguriertem Target, Storyboard und View Controller verwendet. Für den allgemeinen Gebrauch sind solche Templates sehr sinnvoll, doch nun möchten wir die Architektur einer iOS App einmal genauer untersuchen. Verwendet dafür das \shinline{viewhierarchy_bare} Projekt, das auf der Vorlesungsseite zur Verfügung steht.
\item Das Projekt beinhaltet nur ein Target \shinline{viewhierarchy} und zwei Dateien: eine Konfigurationsdatei \shinline{Info.plist} und eine mit dem \emph{Entry Point} Attribut \swiftinline{@UIApplicationMain} als \emph{Application Delegate} markierte Klasse \swiftinline{AppDelegate}. Im Skript könnt ihr euch über den Startprozess einer iOS App informieren.
Implementieren wir die \swiftinline{application:didFinishLaunchingWithOptions:} Methode des Application Delegates, wird diese am Ende des Startvorgangs aufgerufen:
\begin{swiftcode}
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSLog("Hello World!")
return true
}
}
\end{swiftcode}
\item Da wir in der \shinline{Info.plist} Datei kein Storyboard angeben, wird beim Start der App einfach ein leeres \swiftinline{UIWindow} erstellt und angezeigt. Das können wir auch selbst tun:
\begin{swiftcode}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.backgroundColor = UIColor.whiteColor()
window.makeKeyAndVisible()
self.window = window
return true
}
\end{swiftcode}
\item Nun können wir die View Hierarchie des angezeigten \swiftinline{UIWindow} mit weiteren Objekten der Superklasse \swiftinline{UIView} füllen und diese somit auf dem Bildschirm anzeigen:
\begin{swiftcode}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.backgroundColor = UIColor.whiteColor()
window.makeKeyAndVisible()
self.window = window
let label = UILabel(frame: CGRect(x: 0, y: 50, width: window.frame.size.width, height: 50))
label.text = "Hello World!"
label.backgroundColor = UIColor.redColor()
window.addSubview(label)
let button = UIButton(frame: CGRect(x: window.frame.size.width / 2 - 40, y: 120, width: 80, height: 44))
button.backgroundColor = UIColor.blackColor()
button.setTitle("Click!", forState: .Normal)
button.addTarget(self, action: "clickButtonPressed:", forControlEvents: .TouchUpInside) // Diese Methode ist das Äquivalent einer IBAction Verbindung
window.addSubview(button)
return true
}
func clickButtonPressed(sender: UIButton) {
println("Click!")
}
\end{swiftcode}
\end{enumerate}
\begin{exc}
\begin{excitem}{view_hierarchy}{Color Match}{2+1}
Verwendet das \shinline{viewhierarchy_bare} Projekt als Ausgangspunkt um (ohne den Interface Builder zu verwenden) eine einfache App zu schreiben. Denkt euch etwas eigenes aus (das kann jedoch schnell komplex werden), oder implementiert das folgende einfache Spiel:
Auf dem Bildschirm werden Buttons angezeigt, die jeweils eine Farbe als Hintergrund und den Namen einer (ggfs. anderen!) Farbe als Titel tragen. Man muss nun versuchen, auf solche Buttons zu tippen, deren Farbe und Titel übereinstimmen.
\strong{Bonus [+1]:} Verwendet dabei Git, um euer Projekt in Form von Commits zu sichern und stellt es zur Abgabe auf GitHub zur Verfügung.
Nach dem MVC-Konzept trennen wir die Implementierung von Modell, Präsentation und Steuerung:
\begin{description}
\item[Model] Erstellt eine Swift Datei \shinline{ColorWord.swift} und implementiert ein Struct \swiftinline{ColorWord} mit zwei Attributen \swiftinline{color: UIColor} und (Überraschung!) \swiftinline{word: String}. Das Struct benötigt zusätzlich einen Initializer \swiftinline{init()}, der eine zufällige Kombination erstellt, sowie ein Computed Attribute \swiftinline{isCorrect: Bool}, das prüft, ob Farbe und Wort zusammenpassen.
\item[View] Implementiert eine Subklasse \swiftinline{ColorButton: UIButton} in einer weiteren Swift Datei. Diese benötigt nur eine Methode \swiftinline{configureForColorWord:}, die eine Instanz \swiftinline{colorWord: ColorWord} als Argument annimmt und die \swiftinline{backgroundColor} und \swiftinline{title} Attribute, die von \swiftinline{UIButton} geerbt werden, entsprechend setzt.
\item[Controller] Der Steuerungscode wird normalerweise in Subklassen von \swiftinline{UIViewController} geschrieben, doch hier verwenden wir einfach die \swiftinline{AppDelegate} Klasse.
Fügt der \swiftinline{AppDelegate} Klasse die Attribute \swiftinline{var score: Int}, \swiftinline{var rounds: Int}, \swiftinline{var timer: NSTimer?}, sowie \swiftinline{var scoreLabel: UILabel!} und \swiftinline{var colorButtons: [ColorButton]} hinzu.
Das Spiel kann nun bspw. so ablaufen:
\begin{enumerate}
\item In \swiftinline{application:didFinishLaunchingWithOptions:} wird das \swiftinline{window} und das \swiftinline{scoreLabel} erstellt und konfiguriert.
\item Dann wird (ggfs. mehrmals) eine Methode \swiftinline{addColorButton} aufgerufen, die einen solchen Button erstellt und dem Array \swiftinline{colorButtons} sowie der View Hierarchie hinzufügt. Überlegt euch, wie ihr den Button positioniert. Wenn der Button betätigt wird, soll \swiftinline{colorButtonPressed:} aufgerufen werden.
\item Die Methode \swiftinline{colorButtonPressed(sender: ColorButton)} muss dann natürlich implementiert werden. Prüft bspw., ob die Kombination des betätigten Buttons korrekt ist, um entsprechend Punkte von \swiftinline{score} abzuziehen oder hinzuzufügen. Anschließend soll eine Methode \swiftinline{prepareNextRound} aufgerufen werden.
\item \swiftinline{prepareNextRound} fügt zunächst gegebenenfalls weitere Buttons mit \swiftinline{addColorButton} hinzu, um den Schwierigkeitsgrad zu erhöhen. Dann soll jeder angezeigte Button mit einem neuen \swiftinline{ColorWord} konfiguriert werden und ein Timer gestartet werden:
\begin{swiftcode}
func prepareNextRound() {
self.rounds++
// ...
if let timer = self.timer {
timer.invalidate()
}
self.timer = NSTimer.scheduledTimerWithTimeInterval(/* Zeit in sec */, target: self, selector: "timerFired:", userInfo: nil, repeats: false)
}
\end{swiftcode}
\item Die Methode \swiftinline{timerFired(timer: NSTimer)} wird nun nach Ablauf des Timers aufgerufen und muss implementiert werden. Hier können bspw. Punkte von der \swiftinline{score} abgezogen werden, wenn ein Button die korrekte Kombination trägt. Anschließend soll wieder \swiftinline{prepareNextRound} aufgerufen werden.
\item \swiftinline{prepareNextRound} könnt ihr auch am Ende der \swiftinline{application:didFinishLaunchingWithOptions:} Methode bereits aufrufen, um den ersten Timer zu starten.
\item Das \swiftinline{scoreLabel} muss an entsprechenden Stellen aktualisiert werden, am besten mithilfe einer Methode \swiftinline{updateScoreLabel}.
\end{enumerate}
Um das Spiel interessant zu gestalten, gibt es natürlich an vielen Stellen noch Erweiterungspotential!
\end{description}
\end{excitem}
\end{exc}
\end{lecture}
\begin{lecture}
\section{Auto Layout}
\mvcindicatorview
Eine View Hierarchie können wir offenbar ebenso im Code schreiben wie im Storyboard konfigurieren. Selbst für ein simples Interface wie im vorherigen Abschnitt implementiert ist jedoch viel Code notwendig, da jeder Parameter als Attribut gesetzt werden muss. Der Interface Builder bietet hier effiziente Möglichkeiten, Benutzeroberflächen ohne Code zu konfigurieren und trotzdem mit dem Code zu verknüpfen.
Eine große Stärke des Interface Builders zeigt sich auch bei der Implementierung von dynamischen Benutzeroberflächen. Um auf Änderungen der Darstellung, wie bspw. Orientierungswechsel von Portrait auf Landscape, zu reagieren, müssten wir extensiv Code schreiben und die Frames der Views unserer View Hierarchie berechnen.
iOS Apps verwenden das \emph{Auto Layout} Konzept von \swiftinline{UIKit}. Anstatt manuell Frames zu berechnen, definieren wir Regeln, die das Layout unserer Views erfüllen soll. Dieses Konzept der \emph{Constraints} ist im Skript detailliert beschrieben. Mit \emph{Size Classes} kann das Interface dann für bestimmte Displaygrößen genauer angepasst werden.
\skriptref{Auto Layout}
\begin{enumerate}
\item Betrachten wir die RGB App als Beispiel für eine der zuvor konfigurierten einfachen Benutzeroberflächen. Ihr könnt auch das Projekt einer anderen App mit vergleichsweise einfachem Interface öffnen. Rotieren wir den Simulator mit \keys{\cmd+\arrowkeyright} oder \keys{\cmd+\arrowkeyleft} in die Landscape Orientierung, werden die Frames der einzelnen Views nicht verändert und die Benutzeroberfläche wird nicht wie gewünscht angezeigt \abbref{img:autolayout_rgb_pre}.
\includegraphicsc[.6\textwidth]{img/autolayout_rgb_pre.png}{img:autolayout_rgb_pre}{In der Landscape Orientierung bleiben die absoluten Frames einfach erhalten.}
\item Um das Problem zu lösen, können wir nach dem Auto Layout Konzept nun Constraints definieren und damit \swiftinline{NSLayoutConstraint} Objekte der Superview hinzufügen. Zur Laufzeit positioniert die Superview ihre Subviews dann automatisch, sodass diese Constraints erfüllt sind. Die einfachste Möglichkeit zur Erstellung von Constraints bietet der Interface Builder.
Im Storyboard stellen wir zunächst sicher, dass Auto Layout für diese Interface Builder Datei aktiviert ist. Dazu muss die Option \emph{Use Autolayout} im File Inspector markiert sein.
\item Nun können wir Constraints zwischen Interfaceelementen nach unseren Vorstellungen definieren. Dazu verwenden wir die Schaltflächen am rechten unteren Bildschirmrand oder ziehen Verbindungslinien zwischen Objekten bei gehaltener \keys{\ctrl}-Taste. Im Skript sind die Möglichkeiten bei der Erstellung von Constraints beschrieben.
\item In einem eindeutigen Layout werden die Constraints blau markiert. Entsprechen nur die Frames nicht den Constraints, erscheinen diese in gelb und ihr könnt das \emph{Resolve Auto Layout Issues} Menü am unteren rechten Bildschirmrand verwenden, um mit einem Klick auf \emph{Update Frames} alle Views entsprechend ihrer Constraints zu positionieren. Bei einem eindeutigen Layout werden die Frames automatisch angepasst, wenn ihr die Konstanten der Constraints verändert. Sind Constraints rot gefärbt, gibt es Konflikte!
Fügt so lange passende Constraints hinzu, bis das Layout dadurch eindeutig beschrieben wird. \strong{Überlegt euch dabei für jede Subview genau, welche Constraints ihr benötigt, um die vier Parameter \swiftinline{x}, \swiftinline{y}, \swiftinline{width} und \swiftinline{height} von \swiftinline{frame} eindeutig festzulegen.} Beachtet die \emph{Intrinsic Content Size}!
\item Je nach Layout kann es nun sinnvoll sein, die Constraints für bestimmte \emph{Size Classes} anzupassen. Sollen einige Interfaceelemente bspw. auf einem breiteren Display nebeneinander statt untereinander positioniert werden, wählen wir zunächst die \emph{horizontal Regular, vertikal Any} Size Class mithilfe der Schaltfläche am unteren Rand des Editorbereichs.
\item Änderungen, die wir nun am Layout durchführen, betreffen nur die Anzeige in dieser Size Class. Wir können also die Interfaceelemente verschieben und Constraints verändern, bis uns das Layout für breite Displays gefällt.
\item Führt die App nun auf den iPhone und iPad Simulatoren aus und testet euer Layout bei verschiedenen Displaygrößen und -orientierungen.
\end{enumerate}
\begin{exc}
\begin{excitem}{autolayout}{Auto Layout}{3}
Fügt eurer Counter, BMI oder RGB App oder einer vergleichbar einfachen App im Storyboard passende Constraints hinzu, sodass die Benutzeroberfläche sowohl in Portrait und Landscape Orientierung als auch bei verschiedenen Displaygrößen sinnvoll angezeigt wird. Dabei sollte das Layout eurer View Hierarchie eindeutig durch Constraints definiert sein. [1 P.]
Löst dann die folgenden Layouts durch geschickte Definition von Constraints. [2 P.]
\begin{exchinweise}
\item Ihr könnt ein neues Projekt \shinline{AutoLayout} nach dem \emph{Single View} Template erstellen und dem Storyboard einfach für jedes Problem ein \emph{View Controller} Objekt aus der Object Library hinzufügen. Dessen View Hierarchie könnt ihr dann im Storyboard mit Views und Constraints konfigurieren. Mit den Schaltflächen im \emph{Simulated Metrics} Abschnitt des \emph{Attribute Inspectors}, könnt ihr bei ausgewähltem View Controller eine Gerätegröße und -orientierung simulieren.
\item In einigen Situationen kann die Verwendung von unsichtbaren Views als Platzhalter hilfreich sein. Dafür kann das Attribut \objcinline{hidden} verwendet werden, das auch im Interface Builder verfügbar ist.
\end{exchinweise}
\begin{enumerate}
\item Eine \swiftinline{UISegmentedControl} und eine \swiftinline{UIProgressView} sind am oberen Bildschirmrand positioniert, eine \swiftinline{UIView} füllt den verbliebenen Platz.
\begin{minipage}{.5\linewidth}
\centering
\includegraphics[width=\linewidth, height=\linewidth, keepaspectratio]{img/al_21.png}
\end{minipage}
\begin{minipage}{.5\linewidth}
\centering
\includegraphics[width=\linewidth, height=\linewidth, keepaspectratio]{img/al_22.png}
\end{minipage}
\item Zwei Buttons im Abstand von 20pt sind am oberen Bildschirmrand \strong{zusammen} horizontal zentriert. Ein \swiftinline{UISlider} mit zugehörigem Label und eine füllende View befinden sich darunter. Ändern wir den Text der Label, passt sich das Layout an.
\begin{minipage}{.5\linewidth}
\centering
\includegraphics[width=\linewidth, height=\linewidth, keepaspectratio]{img/al_31.png}
\end{minipage}
\begin{minipage}{.5\linewidth}
\centering
\includegraphics[width=\linewidth, height=\linewidth, keepaspectratio]{img/al_32.png}
\end{minipage}
\item Zwei Views haben (Platzhalter-) Intrinsic Content Sizes von 300x300pt und 100x100pt. Die größere wird wenn möglich vertikal zentriert, hat jedoch immer einen Abstand von mindestens 20pt zur darunter befindlichen kleineren View. Beide sind horizontal zentriert. Die kleinere View ist außerdem immer mindestens 20pt vom unteren Bildschirmrand entfernt. Wird der verfügbare Platz kleiner, wird die größere View vor der kleineren gestaucht.
\begin{minipage}{.5\linewidth}
\centering
\includegraphics[width=\linewidth, height=\linewidth, keepaspectratio]{img/al_11.png}
\end{minipage}
\begin{minipage}{.5\linewidth}