From 12e558a44dcd61a3528d510c79747fb3e7c58b53 Mon Sep 17 00:00:00 2001 From: Seungbo Shim Date: Tue, 31 Oct 2023 03:24:25 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[5=EC=A3=BC=EC=B0=A8]=20Shimmy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../week5-mission.xcodeproj/project.pbxproj | 18 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 + .../INHA-UMC-5th.imageset/Contents.json | 33 +++ .../INHA-UMC-5th.imageset/INHA-UMC-5th.png | Bin 0 -> 111689 bytes .../week5-mission/MainViewController.swift | 204 ++++++++++++++++++ .../week5-mission/SceneDelegate.swift | 26 ++- .../WorkbookViewController.swift | 1 + 7 files changed, 269 insertions(+), 19 deletions(-) create mode 100644 Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/Contents.json create mode 100644 Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/INHA-UMC-5th.png create mode 100644 Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift diff --git a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj index cfacd61..b8a6aa2 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj +++ b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 03887C312AE7919F008C0635 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C302AE7919F008C0635 /* AppDelegate.swift */; }; 03887C332AE7919F008C0635 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C322AE7919F008C0635 /* SceneDelegate.swift */; }; 03887C352AE7919F008C0635 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C342AE7919F008C0635 /* ViewController.swift */; }; - 03887C382AE7919F008C0635 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03887C362AE7919F008C0635 /* Main.storyboard */; }; 03887C3A2AE791A0008C0635 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03887C392AE791A0008C0635 /* Assets.xcassets */; }; 03887C3D2AE791A0008C0635 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03887C3B2AE791A0008C0635 /* LaunchScreen.storyboard */; }; 03887C482AE791A0008C0635 /* week5_missionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C472AE791A0008C0635 /* week5_missionTests.swift */; }; 03887C522AE791A0008C0635 /* week5_missionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C512AE791A0008C0635 /* week5_missionUITests.swift */; }; 03887C542AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C532AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift */; }; + 03EABFFB2AEF95AA00C80B72 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -40,7 +40,6 @@ 03887C302AE7919F008C0635 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 03887C322AE7919F008C0635 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 03887C342AE7919F008C0635 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 03887C372AE7919F008C0635 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 03887C392AE791A0008C0635 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 03887C3C2AE791A0008C0635 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 03887C3E2AE791A0008C0635 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -49,6 +48,7 @@ 03887C4D2AE791A0008C0635 /* week5-missionUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "week5-missionUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 03887C512AE791A0008C0635 /* week5_missionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week5_missionUITests.swift; sourceTree = ""; }; 03887C532AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week5_missionUITestsLaunchTests.swift; sourceTree = ""; }; + 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -102,10 +102,10 @@ 03887C302AE7919F008C0635 /* AppDelegate.swift */, 03887C322AE7919F008C0635 /* SceneDelegate.swift */, 03887C342AE7919F008C0635 /* ViewController.swift */, - 03887C362AE7919F008C0635 /* Main.storyboard */, 03887C392AE791A0008C0635 /* Assets.xcassets */, 03887C3B2AE791A0008C0635 /* LaunchScreen.storyboard */, 03887C3E2AE791A0008C0635 /* Info.plist */, + 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */, ); path = "week5-mission"; sourceTree = ""; @@ -233,7 +233,6 @@ files = ( 03887C3D2AE791A0008C0635 /* LaunchScreen.storyboard in Resources */, 03887C3A2AE791A0008C0635 /* Assets.xcassets in Resources */, - 03887C382AE7919F008C0635 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,6 +260,7 @@ 03887C352AE7919F008C0635 /* ViewController.swift in Sources */, 03887C312AE7919F008C0635 /* AppDelegate.swift in Sources */, 03887C332AE7919F008C0635 /* SceneDelegate.swift in Sources */, + 03EABFFB2AEF95AA00C80B72 /* MainViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -297,14 +297,6 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - 03887C362AE7919F008C0635 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 03887C372AE7919F008C0635 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; 03887C3B2AE791A0008C0635 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -442,7 +434,6 @@ INFOPLIST_FILE = "week5-mission/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( @@ -470,7 +461,6 @@ INFOPLIST_FILE = "week5-mission/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..aab7eaa --- /dev/null +++ b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/Contents.json b/Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/Contents.json new file mode 100644 index 0000000..9b391c7 --- /dev/null +++ b/Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/Contents.json @@ -0,0 +1,33 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + }, + { + "filename" : "INHA-UMC-5th.png", + "idiom" : "iphone", + "scale" : "1x" + }, + { + "idiom" : "iphone", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/INHA-UMC-5th.png b/Week 5/Shimmy/week5-mission/week5-mission/Assets.xcassets/INHA-UMC-5th.imageset/INHA-UMC-5th.png new file mode 100644 index 0000000000000000000000000000000000000000..4c5a42146a0f006e1641a13a74b5c0f9c2971750 GIT binary patch literal 111689 zcmeFY^;gsX`#(NFLQ0hG6c7*wNK3bp!azzIWTd1tNOws$N|%76Mt6rWVlcX41C&zg zGoQZxgYWtN_C7n?&Uu{ki2L=pUAOCY#XU+(LzxJl1|I+b5UHvtya52P_WpkHFwsYP zy5$?te-B+$4BP+!Ue>=~jE}|Ao&b!7YgGkV-H$m(x8BQa^9Cz%rkCXvsDQ@B7s)nk z-m7@R$;YFx}oJQ*NSa~PvhA#B@9KcT_nGqV5+dv?*rnDRx1 z)IFwJ#5smS>|Y2@{&)?_b-TY4E!D_HUvnpOT17Cq=C~AAfvTwy-*q1!^FXyu_+y2V z$l)>n-_QTq;Qw&&e{S&q8x<^9Cb}U2=*w?84R_QA?yI@kho_H8ofkjqKpKlgkho4p z^yH!uxX7L0J8XYU%K|4%oo5r=cckc+jt!pLyEo8GK!t|+&Y&5^@R#Hw#iwi;ebsqDH2+Rt-7 z5pHE40yj2xU^5%Edc#<7PkZx*<4ps6wKC*S>c zr{DD{Zk!0iH`i|K%~LA&2&%}?KpDkwZhLS3jJT#lS4GsLwR)n|(&7+7X zqUScimZghl7f5OE-z%E_TT!bz%a_v0fEkW=R z<(7Y4IDG4Dbk7xgQA^+9?rTq)EUT8bJ7*{}cfnXuX&|i*DbI-VLtAQz+1PHw}`-xZ4F|E2-}|)Z627+2TrrNaPoKc1aFWv+E97>4<7H3 zM{WDKzPs@IxLRKQgy!z>EV|@wD=r)oe)dsUTYC`5_9!27s zF8?1gzP~&)n5>_xIPl&2u+Xd!L%zkDLC0=?#QkHGbLOu0F^c(&0U}}XZ8^$NCa&^< zR!aHS1R1d970%<7N4lU~EGBkAgOZ8z2AUouvamqU_<7@41@hFGET69%Q}$K;(?|{;qWY1DTX4I|)Xct)xy$<+E8bVN=NOEJ68n zB6Iu}I)+Q4L8cSF#CKnYn zU;QREAALmTEm>IJmfz1CP!Y%td8-E0(8~Pf5lYcrU6`0i1N-SfbK;PqMPIuCQ>#i5 z$Jii3Xkn$e>4)xP2Qh)&&zi}w7d|GI#T=dT3lxzbO1@Q6B9dg8LUSg88-EE25ac$2 zi{MUe{A2g|xIIz=xSJNm<7>+F{?!X?)s>faW5zSy??nz)a||WC02}5HSTyR-OKYLrZ|#0ALtcN;myQq%QNR;R|DO$HOy&nN>UrFr*-j96 zrN1Pc;?a&XKQ&8j!Fr4eh`+cn0aVv(${%ZYm(D~DP}Eica;S5>Q_%jz{U6lv0Ff#t z^WJ&89uJLVpXY=%04cJt6QI$i*l@7dGBcthLqn9&Q8;^|2=)=T%cJRXuZ1IY`Su?u zg4j0RWjtvNtz17?bEmuqjz0l05kW3fD)LJbO_?A%!|@aKv$HBQA1zz;sBw375&Mu} z5?8OF|7Z#WU>OS$p8Q4N{;Sc4eF9ATLG#IIWYaiLY%{?W`38$GaWDrIV!BUldGmVy zW%8cHk3DEugRMAWXyPx*|03Cg_bjM8>htfM^*@xC+sWn#xw@Z|LL?|k!qr$%uR+(# z*ZvLA3B~1^7Xme@`}D)8EHzQ|qL;D$_v+X;YAWUo_Z$+ftqr>8fD{{>s5qC1hwyfx zu@{+i*uUm7i(6JGtJEY@RdGxtt}z?X_wR|6V7^rNkJ3p1-{zttE24+G)NPFh6KlIc zF4$@~wisBOVG>V}yjnzX$t4w-u9vC28{WG=c6<1q4B@q-F0eQBXF_~7Z@hi+BGbj;8EnFUjsoqnm|+rV zara}Rp~co9+y_%4guaKU06Qnlj*H)$`neRvibt!w&Ig8s)-pM8J>qndZS>8x>}?s` zx6V@tGwhe0OW%Z5qX$PU5%U(^|3Zc6Zks2gnQ@xs7WX%D0Rqc)o<12zOE=|Bcgn~Y zU9F?iB%M=)hr0c6{-7$6ztitCAE)U9XkU(w*=h)-!iMVxQy z+G^$xZY3s1W+SC)8V=lp!H=v9f4-cIt%DIZJ~2|26iV6L{Ydwa*^4;#KaKG`q`ON@ zgo;oXQr1-ITGjnrC!zpKRThe`%u=K!8p#%;hp|Q$p4rb%efCS57dY(xW5_D=Su}>j z{>Zn_dIwRl&h05UXr1Tu53`1CgY6X+FDI3qp!iOiPn;G$jD}jM;V3$lZsK8vyp@nf2FMrA{S>v-8bnC zK(_L0es)v-2F>)PFNV7o6V=!!jfKkDjmk>C2|jCT zSd2Qgy+4>>K)ZEI*gu`v0D(Z9SEZ5)I=IJksF`WM!|8DbxIb#c+Nh{fWc&22D|@gI zPbzZ9lHjRFd-#{w74%?1EC@)F-d;#nGS!EtVf-PVIF80w<)1(-# z3`pO-?F%d6oS8sI31P~M$6f##J;K7jHcNT2sIQ|~RG-LZh*b7$uEDbK&U}8a+{#4AT)FEiXzC%N+c>Y*hpnyh3W^qCfEr|wO?50d>7ezTS2w( zYEfK>3j~f|^C?$|J+s>3@DJ3_3>{8!l>X%_A4{RD6WZyVFKBUK3?V4<)QU->%>oVo zw2b%3k9i^?SOjzelIskk&%M2TRoG1bKAx&%>f*4&?#^j?EwpkVgv2%e-?G!5gh>EC zc$8yD!v;1P`~;BlFeCf#UuHKALF$BpF@6HMLgo07lVDHClOI}oWv@Sa`L@{EWRuz? zGjFXt_>a2lKUY^*$HftK=6H?T$vugJX2Ex_w40hz(s6@0HHrDc=njp3DMl}eBVYo8yn&h&xXmR>LbTA@?^ zLzzw@__FYGilJ7l+0R6y5_O`7?lI3jV)*Uurw!sN91Ge#hGtznlp&KES*O)0e7CnI z@IbWc!@)@ge$l5B~91sp2G%wWgwK#&7oHvO2WV zOm;^(J<{Vm*t3%c^2Deot?czI>1QK>B>@SM-I}jpj?j<)9&?5jh6>ZxpFDsUQF(Bc zVlwBe4KbvIF627S480IzG=jZzv}}C3?b+zjtHog?;(wQOmPF#pAos6OA&fXm1XpH4 zD1PGsUQlSp4O=C9l+{chHmc4zy{>!e5C)C~8;v*$N+!iQ=Pv(TTqM?MB$YG#cWc4I z1}&kP)*SJ}M4}LWC3JT3u&GzL0?`AKg+O2Vna_f8x8pxA33NQ?7U2*HyfD6qBXNEA zzdJLRdop4p-?7qSzJt8>!mjXqRn&^#NM`y3vMeixHcf+@eQ?&(G<5CZpNmiAlk@zd0`6O=N(Ai?E)dH~jE!Kx%fm(Qdw8|EZ5g|OqXkxp}KWozSwx9ht z+qOv^uXJ$=W%fm-7^Qmcq+USyhVa{6k;5qd=aue9XwWE+~$aXf2d)Bi_JkcH)q zmB84CC4DI`jcirCmh1o3k^Fg~M*=V425 zl0_f*@96*cpL#s8c_OC2Ft&49w7;K>lE180KE$Mt)y%uGeIz>noF#Nw@t;@iN~~V` zukPktGfw)Y)}ZFQN!TOs6biT|x?zj%zCXt)a}RCM1tjBtQ2CWb*>Mu)960Os+4MXk z(_<)N$c?-qz+71Dp}wKaIvKdQreX17%P1l5@B)1n&;QQ)?0kRW>^0H;D`{1#W3t!7 z1FK)F9&>>60j3N9o2f#!0}7wrKDlqR{+0f&?IeiLfB$vhecgTQN+9!3Oas&^nNcQK z_%4mr%b*jF*%s$Na(0#a*|sY=DR#LM5jZ-B#aLW<4SXw`)=QN^xN-m6>FzP546~Lf zu5G`bfmmMKTEMB+U(??Y&bO=awIy(eYh=GoU^io@B&{Mj9@v1hMss~CFhpk5-WH3fZF@k z^mf!qu`nYOx0wJ{5|61Qo{@QLxuAy@b^&`szspr=)2`8==iRpa1gQ2QGqekxR-vWY z%4w|a%U-d|yIOy3|FiD5(n0I^KQYvBEScD=)WTjAdI3(3@m@H@;F;-~@N3~Q0Wl*n zwax_roPaPcT}2bTg?Z}^H;WtkN0K6vXkUqV>?JppDd>k3?2pU^)zlx%-tJvbc*Nkf zL;!O&K(Kh>0(^Vzc@iwpdmK_$)DIt<2U@|Am`^^pYg<+go5x1VurU_pVR%$mR1gFF znoYca_&r ziVI9og*~PU`?dOtC6r4`JJnEIjC#JoNLcyJk>8P0ySZ}UR{L&|W@wIY2kNk+hG$fB zb(af-Yr76^rqRpVYYuK!g3yMFLW4P(NH*p7pXk-#Xy*|%fGuF&#SG`IAzVG#6$c9R zRiIr5DdT&Xm-GFQ&cwMH$v--?9fAuo3M^#~DWQawPDD0(v0giTv^foa@CJbW4PUHH z80%-CD^<|VVXfoE)YVWUIMXomts2+S>`cOnZs2@=?rq0oSB+<*Z$N|g5p7=Bjp5bu zpAnq14|)j!AL86_RyK$?Z}K4|I!!^^G6#10Ro8Lx@m^XW+kS&G9T*t7)C)2 z9!W=<$D4;2$|glUE+oqenu6iFkvu;nn7M)orK<a0eqaxU7< zMNd&La6fl5J+N1d21ibX5lv@9ncptPA^;>|6gENHVdamo@MAUAcZ+3}Q<|<83h-hd zoEN^=m2c48jlpS=Bd0Qh6*Y3w^Tf)3VCO!X&-+dC)|QWMxDbn?QE+e}A(d76wc1uv z#4q?kzW+L5e7->K1Z%>d(e%ZNyP<{vNz}=7LV)|azV3Nag5MUs!^0L@?Rf}y5-J0?TKYAO3wRC zMN`v$0bg0nzlsiImj0lA`|Xy>hRWs*6&Vf?Gh<}fGmNxhk%%LPn!WZ~g-+7-ZVg?U zEzH73I@vGa8{lxLkjh4f0{wskFJ!$U*bKhfUtmm;qN2>%%RC8o&FnoO3-}&!6~?}c zwMRGUfo}0ipGbZLqjR`={WeJ@j?_8gFj*wiR8?x*io{IYQJvcKNKHPk9x|w+LNtu! zzL;Y}TH5ZUpZ;z8SyR5(?PZ(YdNAsK;8=@wBght^KT5M1g+W$hAaLj}$_gN77d#5R z4FBDbe|yx_d5b))DuvdK5|`0e*$k2Kmpuc}8HX7JYlqbtjq(psJQ`0{?IATioY{vl5;d?%$AoF&0HlOju`PZWz%iNtj4$C6&SbK90X$1Jh-UbTn>AhA|% zSgPs35hMP068C1#w}vCBeAn!{6pG>SLQfnq!}S`hDk>`-hibDgJqtwR3=j{~!Fl|{ zueqFMy*q*tTIX+m}?=xIee9t7}c$LQ-&A?xtXPlo6O*8rH^^4Smup z9ly?`+kP3kHnA!!H^pzRC6Rbd%1`S=K6Z}P*0x*&Xh${{dL5@l5vT?U&r5t;*kPzf z);vaeU>i}F&7~T)TH}eC{w;1_ zLpFbru`X6)ah^uM8-gmMdoUD7&2uM#WL~{hczMB+Yv4e}dl?4K!F1;DQc z#+5sm5w4Bk)?_m_%dzWm?cPnEFJv>?tV`+NohKaJ3ZHE1t0Dml*VU{k_&V4ERzYud z1!D!b0PG8A<3kLdvT4>CF^iQ41g2L#tG{zsRW(oh^lu1s(V*Sdve#^;q|(dKs7tZb zvNSXi?E*q?evV52OiIY9S1uhr5M&?(q=vwrJ3JI1ct9%5lqhsWo1Nl>Fq0gW_MV$u zwcXx-G;)Xh;{Vo1JU6OBPlLO%SaDm+5O4WW9-qhqzdjR<74WkBfot?S{=pC5WyRQG zg2)dmD3$;?VKbGFvVOftuy;adG)0xBW2o2?oY}9hucWf9X-wechvRAX^|PUo&?480po+30 zS1o$t2tYm0YatYt>DkB6PAB?Jvy(mmFfLUjJ29h+VvVy`q5?&0a<_^U-B)>Z zSn!%YOZcV zR^GFX@amU|_cDg!Om9HL?~olwQVOP*Q^5*b_4 zSie61AfAZfS*o(<;gyXwhZVX^ADViVH$qg|ht8Gynka3bxF=j>2yZx<+m~}Ma1G*Z zD>(iBv52H(-irO3B;{oN1vQ7w0u^A&h-0A!I1=JgYiwUL{uJ+0crS0ni|MnQL!!dC z@ALTC!FXZquSD7xC(ldE=xpW&*I*T6Rjb{&r4m=LG61^}825#g+2-UH#R|#P8nM%> z4nvq)y*aUrf)D2HXZ|bYyQv0%nX%=YN*y7OnLq%})RWEMI7~xcUC+SPymXN|!xRg_ z440OZe1J<$#`lcgREoSn90Nh_DItg0x^k@ubgq{J9awa-|X9&wgK|B(>Bcf%{Y0_n^j0 zL-Gw2a)%Wr&o{Qa1kOhc>Ipo(ZKIqnI5S(~jK#XkXpbmB`^afBtF*lPBU>DZK>Gu7VfdEra;3*xvVDT6@7;$6dh z?s?5)xe3C*iicsL!o@6dgqd_%U_dYIoJ9pZ!}fKQ2tR}e|B{_>73@{J12Hg(xZsth zd1{vp^d*6*yCB$US<2{N5mW~F+b+wF2CQ8K^ZC3KklhCYGiD9NIL#gy=zO(l8SZc_ z!(iH=yLdF|0>}*k9qCf(m(kb1IK0@%ly7cXn_m+TJC_XGqF-Oh6jZbStmmd(N9-Zk zS0Azo$h!ICh}%yfdT0j9g`c;*Wh@!=JN8{Y6v;dL%7Lu=QaSk%cy|jsm%r%d(rbY9 zrgK$1(Fr34a`9JcgYh085-!~tZ5MM_irlQ7HQg~3!!i}^&hPI|7$%!JvQ&38JfLLU zr#M=#Qsk$fn{pA!+Asx(lQ{!04)h51Chs~+7l(}wmz=u(VCdR2^L-s=R${K<`jrZ^ z0J^8pgXXW`V@g7FySL4t-kk5#> zJ?ye^5~pl}&=?Y!Eh{X$VSz*$-4o!FTrGOX@7taxMj0;1hg{#!Hgufm9E(8VGfMfjB$>>dmc7;yYQ>GR=JwM`9n5A`h&Q9Si` zb;G%2&r}ngClu^=AIxbxueyvOaUJ-Npk;KCVcm5PH6~m>787A%QCT188tXZbfn%%f zJ&0KjUYBQ;s8wjIkRmU#)LZ3LvS$?6^Imz*hc4LfB3Lut>gv%YKCC)uW?g1H=@~?W zD2C(%*Wxm z4@HiA(t01EM5@MC%0yx)&(M=tn*J$KH(l`B`;0k$>_raAqR&DRctzk zhXhBi6WEr?JcHqK+g&HK_Gy=6=kun4`^D)s@cj!b+J=WEn`>#0H{C`T_BA98#+54# z@|G$HLAvruvuu@W?<;wL&3tdu(DNB@#=q9sW)FVzxZX;*A6-18VBU)VTl*3tF9+As z#ZS7__f=cYzEhGoqV+MahE&=MaVtu3&2|~0g{%URnO~?;)^gIgfR6K`OcA`j=j6vr zq9&tV9lFNdYVs7C6dI*O0`M0JL?m?nu)5}a+DXy;lVyj{swMAET)a6ukByLF%g zlf2(6P~AG$G`14bTTOZ!m#}imFm#%IKBXNy2Q-%DS9sSoNAksDq&h{5%2I*+KyC`L zI?>Y;>FZ-(No${gx@c7Z73CCWH6v3NPqQh6qDfpQ{;Ip2Xk7@UQq{pK;?tp%JdSQO ze0_-_R+Xr-3L(ruPr3rpzjDsW$*dVuWEDg5q&!yOgt!6_?nwJ2=K zn0|JL-@bv{i7{a27LFyKld-Sg7*a-FFeLYG^gVSn2Pib&{%1VpRDhaKr$$cZ(N!c= zj;!Tj%F`%=+s9BqX%u5&cQ*qoPZF0OlI?|`SpV)ih2h&?JmE55kgFVm+!Sj=K-Af$C@Y~=p%)Vl))r$pHa zWVXtgs0t~H&7G#jitMK_Z~6T#%DZy4fu;F-4TDJqBQrWXTF7}RYr%H>Lu#jMqPR(Q^f~A;KF{zaC!>gnn9pZfP+GYc@zR zJK%#$>zVrUY?5jZ#hsKtfpLLDX5^b(YFlle$G(``Bq>}tajT0TlHb#tp#}3#AzZCz z;LdjqyUK=C_Nykfd^g`SZKIh8$h!~xy|u-2z;BwAiRYG@#@I9B__NKvJy{lDA<^b1 zPBEmfrMP`h^1%%m(Rp@S#egxh2d&)An-f6kpkS3m0cphskk{^RB# zeWV2W9R7?60PGC!M(P-Ur|GjWzNIE>lXZGQNhm-^hsi$|Z^KNVRdJ-<$1&33xlm&W zd)K1ZEW)CBcfc-4oJ#*CkL+=5t%VygWDGF4uHV?9k%T#m%&N3~kC*QJC4605>?@)vSQr)g`BiX4FX0!x6 zOMAwOT!)^H`Nf_UmmhzOkueedh;*Md{SBYl<6qhteRolBdbe#Fu8%}>HZkFPsf&bA z3B???*b=psyMmk1U1J^A!`aeZX&v-4Q|an~(^Z|amE38k9Mxvl`v;Moh=~_3YRuAr zM_B|rNgg<|>PFb2^uBaU-mJfR#RrL6 z@^AKMN1OFwiA22{3-o-{>l;C6VgvEtVu$XLt~O-JGS_|6AZZD{k@k?d_WLoH*>f9; z673VkaDw`iB+vdgV3p|>D#4&}fxe%Xee~L91XZJ@yo|)K@$&XK*&&Wign(X%3!{x{ z2g!ZT*UVKAG(v~=hC$VqtPFH7WxuiG%X3npS{K^!)g_7H zDyEOY2P7XR1w>6_I5b4lus^4S=uZf(-yld~l?S!A@LI@mIz^?}MqK)bFgtoWi2w|z z!QxK^KE9dPCO=|Ba^>`WIlelqK3sfVx|Gu9j*TN+UI)7nCqIOXMozsr|C|!>RR`Ip z@Id46HZ1n11n_z-YfK3#|rx=-9TxQHi#S~L>+5K+RCcB!N3blSBoV_ zSL+?C5OZa(%569~G91j?o(?)Q+~+st7Biqbh%|qzkvi)j=seoe7JVb@B_Fc85no%F zu$5fQ_2=wKy_Aa#B$C^s2z=rv#aB>?EdAW<*k5^eX>hv0hfbFtE;IYbES?YRZ(5W| z5)JagP+W-vP1RuwAt4bhrZj#QGtd+1pI!OKM&)XbMKx2h@7UXQLZM{jU9Ee)4^2f={q9TduaeiKem?dxEw<Bo9ZwrYQNUxAr;;WS?-w<xS5Hj1Tz>9z z%_5#8jKhp_q8}hHQFqOrOPD>L{*+F0fQv|ad%m_#3DSROW_kr9*$c+db9>M^v0SvE z2j!qlb#pxGRJNB=Ws`Vee8R#t&SF!!Q~3yr9U&@d*fi!4o0~#aT}UOY%B+sp8WW5O z{^XfHaFjRkc|$#Wj)U~@D-MhoM%eI>-KgH51-kuO;{K#iV%ZOEzQ2>7^|UklXP_B6kDe&e+bel`_1|j~(p39fl{`a?<>9PBC=ANH>#R;&^lQl4HKUbeyLBF$8!cW|d4Yru#*qnUnH1>x0V;&7Ll_v^>s_Kt0ynz6hQT z>Q@q%&Usu$@u*WPaC6_s;S=0$`UD@crSQ}kL5iqLx%fOCKo7!D|8-jIF`EW9*CQ<| z-x<$V`L|!2`4fc+`Eu_u09A4I4)1FE3w-B;>8k~Zda>;F1g{qAOn;W*6I?yu7>7go z?gJSWX&%M5FnyUpK({2;FBI0o&A{TtZ`Bf;%?m50>E0O9Zx2BaAA224jOYd~f2$W( z_j`t}mL-_bBoeDbe`0G=oA?x*4;KvI2-^FJ!g=J>2lad+QYIRO2kjoL=53(tN^YpU zE_>Qtt2NmVe)e~tO&CvdO!7Yx+mrJ5o1P$epDZn``kJ<~l^ub~;#prA zFyRs^?_-bNQ&8A;b8VU!nYV-{{n0Y-xUFPLcAS56c-DD`WVrs(i;ECBI8|^xa1+b_G`(hJ-2X1>?5-)*ix+LU{fW$nowm{&ZM%z-&bC&yJJjzx&1X%g6Vy%txI2w z*wg6h-;%g%Zq`Guao;-T(m`V*o=Sf4l0{kBbCK0SH*(I3f{zUPr1dA-a;x)tY_@OytrHygRH z%hd5L9s7Z&Xp;UPok=qd#~qy57}+`I8{ltX1$^84(Ws{97w z@~q3PL=o7k4}0%7Fm)ah8Ac9k+ zQ^s7r9Z=SV3===giqX};anqyjl!9?zLFi;uVrM3YGYrLiDrjyI58gh53zhFTNPh(P zoK~`bdBmY`M%zMdZ7Xgq3haA!?$b9J^Cmxb^7-bW8c~DQcjzUkH3WV}*FEUmXWWXC z^0?ke;q9~KfiT;?kE2e!qbmn%VvECnzDw=ZKvaQXo{WGNCXpevN(3%@6~I%q=xj%s z(B_iohQ!GVHDm~4beGn0T&=hK=8yRs;2q4PoW@)UKQJ7}4_0axamtJbdzl-6O(5H8 z>=IPYBRh3R1Ctu>9XYmc!h3QPeI;Ua>Y1K#%@VO?voE94DlfY9p0$(-FZO-8BHbt0 z%&R)aLD*(*BHSzjw-Jm+1w5AL#d~S0k|;@21#s&0z~j1t4-emA#Pr~~hRDS*cKl>^ zzHzFSpDs&X8JQ&&q#I&mFHCsDCq+RQxNg9Fjnh?hUVe>RmaO(HlK#lXec#)a?RQtC z@l*-Guw?%Bx}vp?j5%pz60dNRsXliF!?fMD*2_UwsoOYRcoq@V% zA|6rD3nJ7{d}Jo@RU}IwOgw`y(K8AxouXEiXjoKJ1TksU%%GJP{Jmhhda8tO`s<>b z{w+szz|u)p4{-!2~?O^hA@wCqjEB>G=V%R1Fp`&^W0x?3Rm zvwhnsz}}wXjPp`Oj8VsVdTk(^dlEr+GPmt-{O^g*cn1v95#oDsGyo>js;zRDV-d3s z$6r#Jn}UP_!wT(9gHK7nUZ~{;G$HI)Dd}i#PZ{s|gPgA$otKK&R}gu(L#gI^dX*^d z8-McL&MjRd?xgf1i2S1N`g3}&n3SilBG^wDZ-5=;=;5$zv!T;`@!%O3@1{9cP-KRu zA!ns+@|wU#2+LtU`dEs3k&&fMwe93d9)4yV~QL{mW1tGJ|g;U!5}ZEC@byqYPCw+s-?4 zYTK?YW2C>q-9}Zahc|Q|tiI-GWkg7frk8>*1NpJcoMua3XoIj$txJm<-;HKFpxPsS zS@9*dUETK=u}KIYIXCm=IM55!L_IB)%K2_uHx7;c`Z!jA-n#e8^|j8S?}^h~eBZ%%2k5?mqr5K&#r~DWz5G+TQ+c zgi`&rP%%u(@ zyl8EMg=Uwso6N0=EJ&hx6{TZCc7WbGQO`UjKQtK~S|ZCzG8`=1!cI$CZc8k#J$Y50 zZaHC~bA(HlgH}}3H(@EFf~2ETE6sIJR?gV6hk4*6y$W8onuJ&uRoe2VE!z3cT}F#d zUwVwGVeEZ`0NS}sp2TT{px`xIF;6$r7nhUp#?3r02|$XuNk3YXWX<6`iTi)F9iL>J z{ozH-=Vq!|IzH}-#Q&y!CfIzvTcG@SjA0L<`qo14VVxDWK&&erO`}G}_ay?;_-vv2 z4p;C?EB53l3Y;+6yyh;i7L3F_U!jdK-&ZJk&zQ*W19qMbQ#3^m*R%^4V1CcuB_k)xLx(1nbO_Yr?QtYAAx7WM@SJnsV*0617d#WXNkmX zbOQ4>p=X;lb{iM)(xjv^xbq)BDXErm?DEiMb#w`4>z%C0e1K(e7xp*Kkq(E+cGqsa z)Rg5|;u_5#8Bbfqg z>KT<1<#YLUxI>OwS8rmt-`x-vq>P(HS23!mm!$k)_$H(}xSnj&tZ=qgR-KbIr+Hp; z`8(3LRQW?V*-Ro9wQgKlFK2JJc4;8g6pJ<_aghG~CMt+3WX%23oXw+n`c)vt1JnsH zPu%GSUCeqMMA^264fqB!XFW3nS97FR@fhPLp|^l38UaSZcy+!osRmB^oGi^e&Y#AR z?-SyOJ-O5}wH;KMGQlIMP0qE(o+{A};(1*v*^0A(=x^CC5d2fx!oEF8M^Vc4&$%^h zo&WSv-%qy|z*`lW2c7585d>EU>eaJ_B^txy9O~P&kzB0}Dk~A7w`_~bP1zZkq=0>{ z$W4Sk_KimO9#9>#9`vB=m-_F#i}`K%f<5g%pXK!+-zS494O#?GSDnefq}wD{WgLaB zKdor8mi?ns>B+PesOBmg4P&bu%gJQ>^X+!PFRbKu#HvJ8M=zf1bdS}f9{@l`?Rjvq zSV2Y|PK{%5wIl+DHo~o)*;AZ^iZ0dwC+HF1yy+_Di}jlq+P88KhB5q$jbv?rV+Roz z)dDJC-Ao6K`n%BGphWCaXae;5!ES(TsmQC}^#?%h* zvu$#qP?R#JE_5}Yb~7fWTVk35ej$K?kR8i4Ha7m>uDljg4{JMDoT~~4K9X`c_0N5X zZ2~TJJH)z1yFU3@KTm!@{pvo$Lb%)E(>~RVW2S@U?gQb3%*nvnyqAr)<%bgmPtgO< z*ytT0%a(Ox@nI14%O2LZ#jSc`ioq@6Ds{eC+g1m zaY0UM$oe~oMzj7@WsW$ox}oKKm`yJeUE&LvYUL8F@VkGN^-R^~h5pvhlOyT<7>0uq zNZR?{xG7$$P7GlP;TOU$bc$&om$N$}?(Hsftr%HbJFmKG+kV&a1-|PH0Gof+^N87D zBcK;JHC8IXnJ`amA&6*RCu40Z7)hN!+=NRY`a{Lk25;ya^7!0NN*zhHL8kmnkdMFP zj{j8Ws|X$peOg9ly~K+4bPQpykhBV}0z%5F9u4sQ*<>LJHOSB3`dlL})!5*({UfBl zQ6N`c{-^TH14K$~#np%!W~CM^nEKuKI(LC~ZgeI5);is!(W%tBQO|LuM9az`g@2%% zXx*6f!zI1*8=1MM6`5lLXqG+nbQ?{#$-mvB7D8`i;zhS7w-_Q3lAIkPtk?9`r9Fns zEf{Y=aD~slk3H2JjWqGvDwhJc!!PeFZw8#^E`kztmSVcCc~$ z>D{4IWxh3)qLVw`7*C!LBi3526CUIxH+NW)Y)LQS-TLNKxzfD*gC|XDeUZchfZLa- zytj%fl+UNt90aOkFLp<=?ugTOR*cQu_~?U#6QKGhAv_s({wOz6O)u~;1AK4W*)1*4 z&lv?G#8g}5qm91~{%V^VZ;B2VslPqWBio@@h zHdpW1U5dgUS5n!8q$Ujz3TCP#shYcIn3LEFl}io~IN1@G@|`UT8_QU3143EBhQaVK9-(Xc^8DXFpZb z2_G)sdBd?S(CR1lVG9F1 z@roZVFQ!!8-+bj;iydM(m_N(93~RbY-kP3dtZl!~MWGW-uMiY^+rba*lU4<9Pdm$j zmT9$qqjvP*_@AVuE!yLVWivCT67I5W^JJTf6rboPm1>bnmwF|C78)M0L6syd=57CO3RwJZV`3regcEG3nC<8k?(h$Wxm?b?=LsMRdS;&N?#qQ^rWLIEAadC zl*)?AiZd(P%*8Ea(cywlz;3$uRiWX{xZ|nOut@foYVyV4!fb=KUonLr!Ms$~A5kG% zEk5?*@W3gJD#I}C&%8XtM7J6}$4_GK&ckdsJ|tWfNCd?kAdTuh92j(rELX8fy3q~q zAfAw6jDjpxv2H6`qe1ImjVz;HW$=eB66if=Lx{XlzRsK0!y2QKI?wbYFE)=cr&SO5 z79U@h*83;k$d)FkvZKS)6Ocsg15j&(&CB1znoiOZv2*(YW_SRP6cVi&O_~8)t{{p&tYH5322?#=!PM zuMdAt#IY$Tm@CkaO-gEX)^De19~|GYYNY5vmykvc9wIBVtvwE(U>;lOIq})3Nx0Nv z+V~P7@b*TUmp2)`&;J@d@I8c};#<7EXiM3uf@#BP=`oP7t$~8!#-t2JA9%a%>ytOa zMD4WN-Fs7icuNC*q~v|S3h{1=Ro5B$8Y8;fyB{@bp4sGI?jB*ol9|x(%KLXW%kT~_ zvq+Iw`&m5Q?sL^hL^eWart>oYNS8i;Yyc26t5G#G%h7Osod37NC4<;_Nb$1qDrKPSk!9i%=z~8Pef{Wpo;qIadmO$i3ELMNtf+-@V7ZZREaRv(DXAjsqKRpT ze%u5~gjr0F{Vli4sOTu%_iC%$qTdEQMuFXe^_P%r2

YDKS$){&XXLg|uiAaOy9wOirM>)NUS^Nq@Ae1$&lQ#JE!`PZh< z2wyUjdl5LS_5ZPSmSIu0UE8KbQbanHyg|A_Lb^jry1R3bl#*`gMmhx?=FDti#`y-nycP7kQa7VACs94YPp++1zW1C*askG7>L* z#_6tb%vP~dxf~??AWGeQ{j{^^f8Q($pW+R^k080mR@D{#jg*wX-_XGq9%awv;-8o$ zt7&wzL?!W0{7NUcYm!$z2|^1Z*j2`hI|}d?<=3Ng%r&*`(VdF@Tr8wzhZ?JLukfiAg7PCNfm*7{UPEAj$0L?s4rU}g?!JpS-shtlN2=L}{KUQaagvN~7mJGx$ z`;Q;`)7o+zjh<1pr08}ik%2o6B$^_3&0O8IMMEVZ#gaUI)qmWf?{P3$5P&=+KXICk z%3wq119U`-cKa~lHP3f7Mhk7~?$j;IDiC(!NNg7~>Ig6!dl#*I1vJ-FW^ED>Oxh^4DZPez+N`dkxN`o-P8n-Epiv&wKi5mZMHdNi#>uRL8;}FiX zk3-Lrs%cG>bFZF9FFJIo(bBv9ke|K?(tPk*!pso%0pV&Ge=KkIfB&$O0n56?GXaDV zC!HrTqR*MKK+Km-dXHhBx=0@wcXM+NRL^*J!Z^0{75#>?07WN~YXD}4$>gy1Oix;){GdJjukC9Xw% z-PbVQIo<5ZW$u`&$+eaA<;Y{4lZf-As^`9yD`7Z$MP|$*!O_Q?;r(aytLz^CXeP$a zASRZbM$D++kQDgT0Wms_CPH+$^;YHQf_7KAqxM2|kh;`FrM$xuzLR?Idncz2bc7dy zxv&7HiBAQFVs$HC*Dwv6&P)a?auS6B$goKPzmF}uQ*wFXn-D{^113#A&ReTEaImZ_ zhx@C%uM9hDFIF@VE{wC{j^cgkVK#=H9GY}R_RPYCJZ_oy5j~sJ$5p{$DdhkQgF{EB z!xH*P&sb$=zXY{nBIzoyi#9_;ZYiD~nQF$V%r4H7PXMS>Ms+3(m#V*(4bkmL{YZ1B zgeh}Lg3zAFuwWvJq~TY%(F{9cVV9V(d~|zdjoKU|5$+`ndTzze=?&ymu~ec@HBTFR zCbT!^l?Cj8IIUW|ODIY8U{2O~q);?z{3)W}lTM_yZNIR4Sp*jmCz>6iJX_5Aq?@6m zfddw&(1<7bNr?Q%*dvhluYZh3o(6%#O1qW-O$8QU;d^m*>vBWU?+&)Onq;7k zKr1?Rwnc`SXt}(gfY~RJr`&sIFyZF>&n{;6a9NR^l*68#by9V293hVG{eM9!J*_mR zzU_9ZPM%6WX}S*HF$?WZqZ}x2zFo+y3%9K2qkdaic8|Y^vy`7mnLRE$3$4BNA`>q% zQh1GI*3E>Xk>;t;aGlXn+IhjnD96ics5u*O_2ZdX<7L;sLMtV2V2-xaAp8(7&CDzw zB821Yy(xvMML&qj{LQ}y1;H^*e!#$u={i&~mh;bDe&%UoBXz2(?I8&qS}2-Gzt4K0 zGC}e7S^3}j?53mUH2QlKv4{B|fRU@992B_Qp`KDJ4aIS1-u@_<%KgFlv)XLGXPuqE z_{*KK6~)%E70Db+E^wYP--gx|@HGBRf*>D;ED>Fix5SlTO>@9Q?O4d!s6?~hO=C%L ziAnDF)!VfJc-jT^-^P%scQo)a0RfPC?c zmc$*|Axr7Xg69YS@KPlQ-k0eoaK!===a&PYRkHFe@ezmVNZ}JMwgxQ+PbpwcKn)1>B|v+(t52pPD`jbzTS|w(U!?dEbs0`Cp9kJ+}tHPafPR zj>wR+(olh7a#Ii;qKAt9;z5D0j7;7AlJHx&J&KOv1~&FL2BN6>oJgp{c+3yps>on7 zN!G8gw^w|s{iN~>9rh_DTm9mKIIr6ezQMmO_L!)6KWdz}uREyGI6fmJqrO>zPBOTe zq9JUT@>YA*tGyrq|`<7UCX>zXSW@W-x(9Ti>PcpAE%3H@JI&N&ZOS@cjB*OJk3RZp9(7cWimW`DxCL^=T#B$Qz<_0&Q- zpSHvcw;-K`dGHEYem!bS47sJQr z9%p?-KC@5Eo#)IlvE<(-qYQU`9rLZaPmdTqou96j&XYApd!-JDPw~oYqucUn9cC0{ zZsyCqm9A%dlyNldE@s;VBEL*eO9gvxecHJ& zgR=v7naE)UMNy6f9g))dkdmrgwJ5xEaMecqu%Mk+_m^Q?Q|JwB$EzaR0cSta$oA}A zZJ9yxYw6}1EQF2Dw&Rj%3WoXBc#*pSo#G`#Vru+%BpAUTFcnfB--bU)773M{cWyov zfdgN^rv;i;gSXw;MAn@OQ5=0(49S?u@<%zs)I4%3 zjP!2{|BA@-+X#pfX$s7`mx9!joD&Gq)iovu*0DuT&czD)A6JT9OY5Oh3)Z`|RR^LX z+>4$QyA(#h>gPN~;+3&-Frd$=7+U7v-T7o`8~ zGNwa5wG(}?)qEUXe;5UF65k=BmX8`xqAG-V1-|&qyrn<%%=^AMluCx)zWK{|e*iv= zGPZUPU@mngdbOmIZo6B5$m(HOOtKa`ihC998o9pcGxG1F&KZHJB@`N1HYrs5QK+ZIt*(&@1WlbPq-fqzWb0NwywVi$%Tac^kQ^ZIV1VIr6Ltxa?w_Eo@;-xchrcYnN@J=d&8Ts!JL@wB=3 zc33uvzv$wl!|9d#S+aV&=Q^=*#ZT`EE!S+Bq@-)B&6RhCuDM>zJ5O4s9d9t005vLj}!0Vs3_i-FsK`KZ%fz(j|N!Sx!2y zFA-+3H`w^CYpf4;OrK%;ap?m_|ZzFTb&-B*Y>RYl)=zFh>I&)pPvHRQzFlVQQo9gheDVtMHu=ndSh|~( zol}l{uRH1RJZ)C|U~Au{7mqzQm1k?RFx;hm{>I?YD#NAzdZeysI?M4AJItK2nhJNE zoO}xJVx%qIZ1`{KU1=mu*(#!p8%k7ct1u9Tf|Ho$ln!m-VkDU({<6?`Mo=CdC_6OJ zGO?o|dXLo4o*keegjVh{%i!))XTqX4-zj&`OD$((i_dCKjvY)fdXZhjmRJe|2ExN3 z+RE4@?|stUwmv^-e2!;a-4*aYqF7o+o%E#IhAn8;45&qBG_5KJoHYxG-6i!N#1O&C zR4k4Dpz+bSAN*%bJN;I+Bp4cj@?9D;MULjMytJaP5#gb3Zj89ky3G?;MD+HE+w_SI z5gC1F3w(R=_WzuiAiTY{;wcLIu3v!Pu^o0OOcvvr`n9&y?WtL;m6wUvcAB@4MQuy; zKu$$4MtVR$UeCgL#C%G6 zbFbZHli)O{nd*D$+-M?>xRoJ;?M`mzR?#m7-1>51EoEW$cb!jQx7NJ9_DN@gA}%Va zG=;q8LvrH@ecz`y9Mb{bzj~W3-#gTe&CqpfInmD)!A-oZ-JYe5&D|FRp-=PZ?+gEz z;iK-E)Th)bdKy)N64~4=FDf z)7AJpy`N0D7=u+T_M6K;bn<4+*9C50%>GES4l3RhsAF;I{|;J;aS}0^d-a$61CA{M z_t~y;+)}#SG<9K(ck?@+Wy2<)_8bjmDU(7^Q=rIM!tT?%&GbE<$!d*u+jN)YRfR*Q z5I$w#i!8!~6aT6OL;vCnna=wAJI|@~x5m`zpA$l(;~UP-?sJNzYIMiP_2(Z{nvG54 z9o>=6R}&6iKTB=T3AkJ+8$sA0W9jvO(OJLhI0IY}xtAW{*uPIdk4Mr{&wWylftJ=k zfF}UIg<3s9K#ku-dC~S;)eC z>^6*P7B_y{P%n>E%$tyq&~J$ypu7^E9bS?PRU>>5P7l8O#I;6C^Cr~hZY5Kcnj}&K zW6VfW1ahKjAAK~S&)krS_vM}g?g+>C1A6TiB^1K zYCFOz`koedPZLR20~rfMcP?=)lT|l{tsERV`kwTxZnd zAtFJ2xssp?K<@CZvQ5&T{mEI_Aji!6PHZ7Eqok7bF}VSvVECiGcAMC z*}YQ&HloMRp^ydRIws3={lc(DIXm{5N5Gz8I7f0_VzHPXeEO&CQphKL|0B_n-;hYa zBSRE90#CtxlvD}86e4`N%03d7Uk`{rot*?c-=Ey|8>@-=Xi1;O%}^O(54%wu}wd=qHa&?$BS&iimW=fhgQTgQxik% zrxyYeUTW4urX#p))a^fv7>n@uGOK(RK z+Qql(VJ-u2r~UpYJ-^*0tB}E&iO9kiE8+dzH^D&BT};qH0b| zD+)5<=DVslY~J~9ZmE*0oi#=5LKOYoQChZ;OJW(^JfKjrUZQ2rCh7MoN$9m2m<_#^ zuQ6_EZ@Q&61%f=yf@kora}dxNcXiUE3yiE#Ksn<4FMnxzu(5QW4>KE`C#E8J%~lwq zZX_zOM&N;-v8c@hqwVP2Xo3~nJ#lZPcVmT4Y{lXoGa{ln4Tk=0s45@p)X4N}PQcdW zIEm@O|Mu>dD~=W?VWVYkG%@QvX_kKFoec4>CE2SpHFHR4webSIJz&U3`S(?yu0F%v zNdqM*#z>kTMWjVIZ>RQqb&ujBfc!^`UNAc_a{3k90H1;YuY-DlPm*GIQ>&^rCI$_$ zG$WXXB|AXsv5`N#w2D?)|d4U39x1 zzqZX4DC79cl;tHz+AZs{^$CMxKP@~3+f}};AF`EI67nYTC8t~5aV%M?X*pzaSnwMc zeVi5r2q#8|S1oZhGDSI#$V&e-p7ooJLD!_LcCJFwBVOeeZBD&<^ld(WiH1omh%H>L zL&kIcasPKv3_-spFG!gQZ)RxYd}x064&E$SzBg@yEHScY=ez!8qL8eT$o`Dd931|G z(kk8{x6iu1bOBI1J$1)a5(v`Mdi(~l2B8?maQnulo{`b7LyKJxAnKYuDhGDVk5 z7ri`s(NJ((q-S$PzMuvehlf$1sE!Xm@yy{5WLU$!Ld~bN6qJ}te)wSelQ=fU)rxDC z%IhQ8Fs0GKSZ#xF2rBt zVOrl8ZQ#?k11wy`k>g(B0(j|KXOi>p@qcX_R*AbfhcA~vQ|-cr3tYb!XertaA`~^E zh1OJr54qy;mmsN4#+&!=XNlWSlveFsHvZj53|nDvI;l|<=D>_~Ht-!B`#b%o(CmE> zBw9;L-(l8h>FKRa4S^2u zccfb}r2!7WxMb5ksiN2*i+YQ)UxEM8_Med10;oh#!Btgccl#iMvF(|tC{fB<A*(%|M$Jh+2VKMtb*m#j{v zKy5O%k<+I5YMKs>45AdH52vA}8XdV)1cU%rBI`x{=k=r*pCYK2rGE0gU1u|+|Nl!8 z>&o{93S@;bV;r5CsmK03K4xvN*JS3{ns*Y_v-$@lB`zBgzE_?Nd2PbB6AsDiY#~m2AgJixy4~ zfRgK$3}}=8p#zNu76M^RfTzRW>XUlqojelcKFM_>1P%B76HA!bQ!jF+CZqEIVIL8);e5C(R^}4$$LATFp%lbt{ZX zQ7-NO8ZKBa*|gUOA=Gw`*nM)AZ_b{y5~(G)aP3tJ^5g{3Q}}togRz$aRR$$?r(-Wf=efReu0Nk$pTVXJnBl)8|f})&*B?ax=S{MN7u)PkiDFj<a7!e# z3bmS8EHJ%q##Gl#*UXlqHf?V7Pe2LmQ*nbIB*W^NF>`M!-?fyxaQ#f@RQ*+~XTX5F z1d2370vGBR|N1>#{G}{{r9)^PFz&PFM$~8yqP$2O@QUg1$y+6od$~w4HeOE7%RB27 zBHaZPzn5=@=`H&@byald0$}s6b?oh`t8GGnbx~b3CLeVMXuk>cZQeh?zF< z9ZuDnVSVk^?}IdTvzo;IyLuPN*l5$y(JhIrhO_<0g`8!%BdU6!mh-gWI&uCWxN?I# zZFM>p)X%^)#@_gvi)=k{hG9Dqu`*w^MTs{H!R<1=Li zeU=GN_k1(yX`AJz$NRwS-UGvs;WDwWuPP0sD4qV>FX)jW!$QfN?)WRLG|&M#fW_JQ zHm#yID$*yCamCvg4QgQ!*Uo8Llyz_(b*I#~JFT3e)#di712ich6d6o+2GtIH+U1B# zPd&cqespm1S*|d=V(LViWfRSwKWe>Ms%hTM7&~(Or}0iYD3zML>}1Ni$l;Lq^90Z2 zLC4hdy{(FfUCsRT3a7}sr5xS-It|^vYxx49m>Z82*xXRiqn~NIV%~R64W2A<0%_GK zQkOsS+L68*Vud+l4@~2+CTK3$V&k;aEjn$_dW2fasBZ<1q;Ah)qe6=BEaUY&tms#k z*UjT2#58-zizy(@DUjC0HktmBoKzOxnm-&^npijl zc<$8mOi`tR3jzHWuVCMDgftIZMFJ2IMrUzLri z5zP<2#9k#hwzKzioS&h_*tR7fJoPPuKe4%jl}F?!r(ye%t-ZT=>(}QTyN)VHC4}^} zI$>iJ^sL$Tjx-++f4jjT4|C8|94v|6Rd|Jxd()B0bbE%t6tG7)NE?&Y;n+(8CeT99 zC1te~{NR5Xw%-QN(hq%`+P~$s4gR9wr;wLhaXEnQ8a7aGDfzo9UWe>;Y(^!-Hdh68p&J#4)sFCr|WiaUoD zHL=UN!h-*G+;!j8X=;{YawveTOJBXZnKw#GTeOI9R7f#q)!kam0Q&8%S!xM)%B zo)#1pK23o!KRFJqTdu8AUNogDi)*FRH;~f6MpAJ5E$fjmi7i=ci&17Nf34211>vqo z0$NdWogRX);EWn_UxR!b3kJKK( zIy36WEPSf}vmN{N{^;S-Ei#H$bWJ%XSVAnwc|7A�V>5r|+L^8Za!u@BZ4vRU!$s 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

D(_5YP!;5V@5^C$Iu=JdYmU(NHN(|_l^{6XkH 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 UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + image.draw(in: CGRect(origin: CGPoint.zero, size: size)) + let resizedImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return resizedImage + } + + // UIImage의 cornerRadius를 설정할 수 있도록 해주는 함수 + func setCornerRadius(with radius: CGFloat) -> UIImage { + let format = UIGraphicsImageRendererFormat() + format.scale = scale + let renderer = UIGraphicsImageRenderer(size: size, format: format) + return renderer.image { rendererContext in + let rect = CGRect(origin: .zero, size: size) + let path = UIBezierPath(roundedRect: rect, + byRoundingCorners: .allCorners, + cornerRadii: CGSize(width: radius, height: radius)) + path.close() + + let cgContext = rendererContext.cgContext + cgContext.saveGState() + path.addClip() + draw(in: rect) + cgContext.restoreGState() + } + } +} + + +@available(iOS 13.0.0, *) +struct MainViewControllerPreview: PreviewProvider { + static var previews: some View { + MainViewController().toPreview() + } +} diff --git a/Week 5/Shimmy/week5-mission/week5-mission/SceneDelegate.swift b/Week 5/Shimmy/week5-mission/week5-mission/SceneDelegate.swift index ccbe42a..fcfbc65 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission/SceneDelegate.swift +++ b/Week 5/Shimmy/week5-mission/week5-mission/SceneDelegate.swift @@ -13,11 +13,27 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { - // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. - // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. - // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } - } + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + + let window = UIWindow(windowScene: windowScene) + + // 처음 보일 main ViewController + let rootViewController = MainViewController() + + // NavigationController 설정 + let navigationController = UINavigationController(rootViewController: rootViewController) + + // 위에서 만든 viewController를 첫 화면으로 설정(navigationController로 설정 + window.rootViewController = navigationController + + // 화면에 보이게 설정 + window.makeKeyAndVisible() + + self.window = window + } func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. diff --git a/Week 5/Shimmy/week5-practice/week5-practice/WorkbookViewController.swift b/Week 5/Shimmy/week5-practice/week5-practice/WorkbookViewController.swift index fb20d78..0f0a1f9 100644 --- a/Week 5/Shimmy/week5-practice/week5-practice/WorkbookViewController.swift +++ b/Week 5/Shimmy/week5-practice/week5-practice/WorkbookViewController.swift @@ -148,3 +148,4 @@ struct WorkbookViewControllerPreview: PreviewProvider { WorkbookViewController().toPreview() } } + From 18f67555436bc90e1a854cec04bd8636551c1902 Mon Sep 17 00:00:00 2001 From: Seungbo Shim Date: Tue, 31 Oct 2023 04:23:19 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[5=EC=A3=BC=EC=B0=A8]=20Shimmy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../week5-mission.xcodeproj/project.pbxproj | 4 + .../week5-mission/MainViewController.swift | 6 +- .../week5-mission/MenuViewController.swift | 157 ++++++++++++++++++ 3 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift diff --git a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj index b8a6aa2..436398d 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj +++ b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 03887C522AE791A0008C0635 /* week5_missionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C512AE791A0008C0635 /* week5_missionUITests.swift */; }; 03887C542AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03887C532AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift */; }; 03EABFFB2AEF95AA00C80B72 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */; }; + 03EABFFD2AF02E2900C80B72 /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03EABFFC2AF02E2900C80B72 /* MenuViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +50,7 @@ 03887C512AE791A0008C0635 /* week5_missionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week5_missionUITests.swift; sourceTree = ""; }; 03887C532AE791A0008C0635 /* week5_missionUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week5_missionUITestsLaunchTests.swift; sourceTree = ""; }; 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + 03EABFFC2AF02E2900C80B72 /* MenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -106,6 +108,7 @@ 03887C3B2AE791A0008C0635 /* LaunchScreen.storyboard */, 03887C3E2AE791A0008C0635 /* Info.plist */, 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */, + 03EABFFC2AF02E2900C80B72 /* MenuViewController.swift */, ); path = "week5-mission"; sourceTree = ""; @@ -258,6 +261,7 @@ buildActionMask = 2147483647; files = ( 03887C352AE7919F008C0635 /* ViewController.swift in Sources */, + 03EABFFD2AF02E2900C80B72 /* MenuViewController.swift in Sources */, 03887C312AE7919F008C0635 /* AppDelegate.swift in Sources */, 03887C332AE7919F008C0635 /* SceneDelegate.swift in Sources */, 03EABFFB2AEF95AA00C80B72 /* MainViewController.swift in Sources */, diff --git a/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift b/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift index 2ce96fa..58812cd 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift +++ b/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift @@ -92,6 +92,7 @@ class MainViewController: UIViewController { // MARK: - Set up private func setupUI() { + self.view.backgroundColor = .white // self.view 는 전체 view(부모) // 전체 view에 각각의 view를 추가 // 추가한 순서대로 z 값이 정해짐 (나중에 넣은 black이 제일 위로 감) @@ -113,7 +114,7 @@ class MainViewController: UIViewController { private func setTitleLabelLayout() { let titleLabelConstraint = [ self.titleLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), - self.titleLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 100), + self.titleLabel.bottomAnchor.constraint(equalTo: self.subTitleLabel.topAnchor), ] NSLayoutConstraint.activate(titleLabelConstraint) } @@ -121,7 +122,8 @@ class MainViewController: UIViewController { private func setSubTitleLabelLayout() { let subTitleLabelConstraint = [ self.subTitleLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), - self.subTitleLabel.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor), + //self.subTitleLabel.topAnchor.constraint(equalTo: self.titleLabel.bottomAnchor), + self.subTitleLabel.bottomAnchor.constraint(equalTo: self.logoView.topAnchor, constant: -80) ] NSLayoutConstraint.activate(subTitleLabelConstraint) } diff --git a/Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift b/Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift new file mode 100644 index 0000000..be48e61 --- /dev/null +++ b/Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift @@ -0,0 +1,157 @@ +// +// MenuViewController.swift +// week5-mission +// +// Created by Seungbo Shim on 2023/10/31. +// + +import UIKit +import SwiftUI + +class UniversityCell: UICollectionViewCell { + static let reuseIdentifier = "UniversityCell" + + // MARK: - UI Components + private lazy var universityUIButton: UIButton = { + var config = UIButton.Configuration.filled() + config.title = "OO대" + config.baseForegroundColor = .black + config.baseBackgroundColor = .white + config.background.strokeWidth = 1.0 + config.background.strokeColor = .white + config.cornerStyle = .fixed + config.titleAlignment = .center + + if let image = UIImage(named: "INHA-UMC-5th") { + let resizeImage = image.resizeImage(image: image, toSize: CGSize(width: 150, height: 150)) + config.image = resizeImage?.setCornerRadius(with: 15) + config.imagePlacement = .top + config.imagePadding = 10 + } + + let button = UIButton(configuration: config, primaryAction: nil) + button.addTarget(self, action: #selector(ButtonTapped), for: .touchUpInside) + button.translatesAutoresizingMaskIntoConstraints = false + + return button + }() + + @objc func ButtonTapped() { + print("뿅") + } +} + +class MenuViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + // MARK: - Components + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.text = "GACI 지부" + label.textAlignment = .center + label.font = UIFont.boldSystemFont(ofSize: 32) + label.textColor = .black + label.translatesAutoresizingMaskIntoConstraints = false + label.numberOfLines = 0 + return label + }() + + + private lazy var logoView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "INHA-UMC-5th") + imageView.layer.cornerRadius = 30 + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() + + private lazy var uiCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .white + collectionView.dataSource = self + collectionView.delegate = self + collectionView.register(UniversityCell.self, forCellWithReuseIdentifier: UniversityCell.reuseIdentifier) + collectionView.translatesAutoresizingMaskIntoConstraints = false + + return collectionView + }() + + // MARK: - UICollectionViewDataSource 메서드 + // 특정 섹션에 속하는 아이템(셀)의 총 개수를 반환한다. + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 4 + } + + // 각 셀을 생성하고 구성하기 위해 호출 + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + // dequeueReusableCell()을 통해 재사용 가능한 셀을 얻는다. 셀이 없으면 재사용 큐에 셀을 생성한다. + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) + cell.backgroundColor = .red + return cell + } + + // MARK: - UICollectionViewDelegateFlowLayout + + // CollectionView에 들어갈 Item에 size에 대한 정보 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: 100, height: 100) + } + + // CollectionView에 들어갈 셀 사이의 minimum spacing에 대한 정보 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 10 + } + + // CollectionView에 들어갈 각 Item의 Inset(여백) 대한 정보 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + } + + // MARK: - Life cycle + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + setupUI() + } + + // MARK: - Set up + private func setupUI() { + self.view.backgroundColor = .white + // self.view 는 전체 view(부모) + // 전체 view에 각각의 view를 추가 + // 추가한 순서대로 z 값이 정해짐 (나중에 넣은 black이 제일 위로 감) + self.view.addSubview(titleLabel) + self.view.addSubview(uiCollectionView) + + // 각각 뷰에 오토레이아웃 함수 호출 + setTitleLabelLayout() + setUICollectionViewLayout() + + } + + private func setTitleLabelLayout() { + let titleLabelConstraint = [ + self.titleLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.titleLabel.bottomAnchor.constraint(equalTo: self.uiCollectionView.topAnchor), + ] + NSLayoutConstraint.activate(titleLabelConstraint) + } + + + + private func setUICollectionViewLayout() { + let uiCollectionViewContraint = [ + self.uiCollectionView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + self.uiCollectionView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), + ] + NSLayoutConstraint.activate(uiCollectionViewContraint) + } +} + + +@available(iOS 13.0.0, *) +struct MenuViewControllerPreview: PreviewProvider { + static var previews: some View { + MenuViewController().toPreview() + } +} From d4815c5432164410a7c6b19a913e00732590113c Mon Sep 17 00:00:00 2001 From: Seungbo Shim Date: Tue, 31 Oct 2023 14:44:43 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[5=EC=A3=BC=EC=B0=A8]=20Shimmy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../week5-mission.xcodeproj/project.pbxproj | 44 ++++++++++++++- .../ViewController}/MainViewController.swift | 9 ++- .../Menu/View/UniversityCell.swift | 10 ++++ .../ViewController}/MenuViewController.swift | 56 +++++++++++-------- 4 files changed, 93 insertions(+), 26 deletions(-) rename Week 5/Shimmy/week5-mission/week5-mission/{ => Main/ViewController}/MainViewController.swift (96%) create mode 100644 Week 5/Shimmy/week5-mission/week5-mission/Menu/View/UniversityCell.swift rename Week 5/Shimmy/week5-mission/week5-mission/{ => Menu/ViewController}/MenuViewController.swift (80%) diff --git a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj index 436398d..8a1c624 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj +++ b/Week 5/Shimmy/week5-mission/week5-mission.xcodeproj/project.pbxproj @@ -107,8 +107,8 @@ 03887C392AE791A0008C0635 /* Assets.xcassets */, 03887C3B2AE791A0008C0635 /* LaunchScreen.storyboard */, 03887C3E2AE791A0008C0635 /* Info.plist */, - 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */, - 03EABFFC2AF02E2900C80B72 /* MenuViewController.swift */, + 03FBD2BF2AF0C736002C459B /* Main */, + 03FBD2BB2AF0C6DC002C459B /* Menu */, ); path = "week5-mission"; sourceTree = ""; @@ -130,6 +130,46 @@ path = "week5-missionUITests"; sourceTree = ""; }; + 03FBD2BB2AF0C6DC002C459B /* Menu */ = { + isa = PBXGroup; + children = ( + 03FBD2BE2AF0C6F4002C459B /* ViewController */, + 03FBD2BC2AF0C6E4002C459B /* View */, + ); + path = Menu; + sourceTree = ""; + }; + 03FBD2BC2AF0C6E4002C459B /* View */ = { + isa = PBXGroup; + children = ( + ); + path = View; + sourceTree = ""; + }; + 03FBD2BE2AF0C6F4002C459B /* ViewController */ = { + isa = PBXGroup; + children = ( + 03EABFFC2AF02E2900C80B72 /* MenuViewController.swift */, + ); + path = ViewController; + sourceTree = ""; + }; + 03FBD2BF2AF0C736002C459B /* Main */ = { + isa = PBXGroup; + children = ( + 03FBD2C02AF0C740002C459B /* ViewController */, + ); + path = Main; + sourceTree = ""; + }; + 03FBD2C02AF0C740002C459B /* ViewController */ = { + isa = PBXGroup; + children = ( + 03EABFFA2AEF95AA00C80B72 /* MainViewController.swift */, + ); + path = ViewController; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ diff --git a/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift b/Week 5/Shimmy/week5-mission/week5-mission/Main/ViewController/MainViewController.swift similarity index 96% rename from Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift rename to Week 5/Shimmy/week5-mission/week5-mission/Main/ViewController/MainViewController.swift index 58812cd..53ac999 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission/MainViewController.swift +++ b/Week 5/Shimmy/week5-mission/week5-mission/Main/ViewController/MainViewController.swift @@ -34,7 +34,14 @@ class MainViewController: UIViewController { private lazy var logoView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "INHA-UMC-5th") - imageView.layer.cornerRadius = 30 + imageView.layer.cornerRadius = 100 + + imageView.clipsToBounds = true + // cornerRadius 적용시 추가 + + //imageView.layer.masksToBounds = false + // 이건 shadow 같이쓸때 추가 + // 이걸 뒤에 선언하니까 clipsToBounds가 묻힘 imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() diff --git a/Week 5/Shimmy/week5-mission/week5-mission/Menu/View/UniversityCell.swift b/Week 5/Shimmy/week5-mission/week5-mission/Menu/View/UniversityCell.swift new file mode 100644 index 0000000..bad224d --- /dev/null +++ b/Week 5/Shimmy/week5-mission/week5-mission/Menu/View/UniversityCell.swift @@ -0,0 +1,10 @@ +// +// UniversityCell.swift +// week5-mission +// +// Created by Seungbo Shim on 2023/10/31. +// + +import UIKit + + diff --git a/Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift b/Week 5/Shimmy/week5-mission/week5-mission/Menu/ViewController/MenuViewController.swift similarity index 80% rename from Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift rename to Week 5/Shimmy/week5-mission/week5-mission/Menu/ViewController/MenuViewController.swift index be48e61..c60f560 100644 --- a/Week 5/Shimmy/week5-mission/week5-mission/MenuViewController.swift +++ b/Week 5/Shimmy/week5-mission/week5-mission/Menu/ViewController/MenuViewController.swift @@ -8,6 +8,7 @@ import UIKit import SwiftUI +// MARK: - Cell class UniversityCell: UICollectionViewCell { static let reuseIdentifier = "UniversityCell" @@ -39,9 +40,21 @@ class UniversityCell: UICollectionViewCell { @objc func ButtonTapped() { print("뿅") } + + // MARK: - Configure + public func configure() { + + self.setupUI() + } + + // MARK: - UI Setup + private func setupUI() { + self.addSubview(self.universityUIButton) + } } -class MenuViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + +class MenuViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { // MARK: - Components private lazy var titleLabel: UILabel = { let label = UILabel() @@ -55,14 +68,6 @@ class MenuViewController: UIViewController, UICollectionViewDataSource, UICollec }() - private lazy var logoView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(named: "INHA-UMC-5th") - imageView.layer.cornerRadius = 30 - imageView.translatesAutoresizingMaskIntoConstraints = false - return imageView - }() - private lazy var uiCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical @@ -81,30 +86,32 @@ class MenuViewController: UIViewController, UICollectionViewDataSource, UICollec func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 4 } - + // 각 셀을 생성하고 구성하기 위해 호출 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - // dequeueReusableCell()을 통해 재사용 가능한 셀을 얻는다. 셀이 없으면 재사용 큐에 셀을 생성한다. - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) - cell.backgroundColor = .red + guard let cell = uiCollectionView.dequeueReusableCell(withReuseIdentifier: UniversityCell.reuseIdentifier, for: indexPath) as? UniversityCell else { + fatalError("Failed to dequeue UniversityCell in CalcController") + } + cell.configure() + return cell } - + // MARK: - UICollectionViewDelegateFlowLayout - + // CollectionView에 들어갈 Item에 size에 대한 정보 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - return CGSize(width: 100, height: 100) + return CGSize(width: self.view.bounds.width / 4 + 76, height: self.view.bounds.width / 2) } - + // CollectionView에 들어갈 셀 사이의 minimum spacing에 대한 정보 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { - return 10 + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 50 } - + // CollectionView에 들어갈 각 Item의 Inset(여백) 대한 정보 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { - return UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0 } // MARK: - Life cycle @@ -132,7 +139,7 @@ class MenuViewController: UIViewController, UICollectionViewDataSource, UICollec private func setTitleLabelLayout() { let titleLabelConstraint = [ self.titleLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), - self.titleLabel.bottomAnchor.constraint(equalTo: self.uiCollectionView.topAnchor), + self.titleLabel.bottomAnchor.constraint(equalTo: self.uiCollectionView.topAnchor, constant: -100), ] NSLayoutConstraint.activate(titleLabelConstraint) } @@ -143,6 +150,9 @@ class MenuViewController: UIViewController, UICollectionViewDataSource, UICollec let uiCollectionViewContraint = [ self.uiCollectionView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), self.uiCollectionView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor), + // 밑에 제약조건 안넣으니까 안떴음;; + self.uiCollectionView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9), + self.uiCollectionView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.6) ] NSLayoutConstraint.activate(uiCollectionViewContraint) } From da2844a9adccac1cb6bf64341e5f775035ff5f66 Mon Sep 17 00:00:00 2001 From: Seungbo Shim Date: Tue, 7 Nov 2023 03:54:01 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[6=EC=A3=BC=EC=B0=A8]=20Shimmy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../week6-mission.xcodeproj/project.pbxproj | 635 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/xcschememanagement.plist | 14 + .../week6-mission/CalcController.swift | 101 +++ .../CalcControllerViewModel.swift | 23 + .../week6-mission/week6-mission/Info.plist | 23 + .../Models/CalculatorButton.swift | 85 +++ .../Supporting/AppDelegate.swift | 36 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Supporting/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../Supporting/SceneDelegate.swift | 68 ++ .../week6-mission/Views/ButtonCell.swift | 63 ++ .../week6-mission/Views/CalcHeaderCell.swift | 12 + .../week6_missionTests.swift | 36 + .../week6_missionUITests.swift | 41 ++ .../week6_missionUITestsLaunchTests.swift | 32 + 19 files changed, 1239 insertions(+) create mode 100644 Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj create mode 100644 Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Info.plist create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/AppDelegate.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/Contents.json create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/Base.lproj/LaunchScreen.storyboard create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Supporting/SceneDelegate.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-missionTests/week6_missionTests.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITests.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITestsLaunchTests.swift diff --git a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj new file mode 100644 index 0000000..541d10d --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj @@ -0,0 +1,635 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 03474F362AF9487C00532F6F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F352AF9487C00532F6F /* AppDelegate.swift */; }; + 03474F382AF9487C00532F6F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F372AF9487C00532F6F /* SceneDelegate.swift */; }; + 03474F3A2AF9487C00532F6F /* CalcController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F392AF9487C00532F6F /* CalcController.swift */; }; + 03474F3F2AF9487D00532F6F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03474F3E2AF9487D00532F6F /* Assets.xcassets */; }; + 03474F422AF9487D00532F6F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 03474F402AF9487D00532F6F /* LaunchScreen.storyboard */; }; + 03474F4D2AF9487D00532F6F /* week6_missionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F4C2AF9487D00532F6F /* week6_missionTests.swift */; }; + 03474F572AF9487D00532F6F /* week6_missionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F562AF9487D00532F6F /* week6_missionUITests.swift */; }; + 03474F592AF9487D00532F6F /* week6_missionUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F582AF9487D00532F6F /* week6_missionUITestsLaunchTests.swift */; }; + 03474F692AF9506100532F6F /* CalcControllerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F682AF9506100532F6F /* CalcControllerViewModel.swift */; }; + 03474F6B2AF9509100532F6F /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6A2AF9509100532F6F /* ButtonCell.swift */; }; + 03474F6D2AF950CA00532F6F /* CalcHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6C2AF950CA00532F6F /* CalcHeaderCell.swift */; }; + 03474F6F2AF950F900532F6F /* CalculatorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6E2AF950F900532F6F /* CalculatorButton.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 03474F492AF9487D00532F6F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 03474F2A2AF9487C00532F6F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03474F312AF9487C00532F6F; + remoteInfo = "week6-mission"; + }; + 03474F532AF9487D00532F6F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 03474F2A2AF9487C00532F6F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03474F312AF9487C00532F6F; + remoteInfo = "week6-mission"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 03474F322AF9487C00532F6F /* week6-mission.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "week6-mission.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03474F352AF9487C00532F6F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 03474F372AF9487C00532F6F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 03474F392AF9487C00532F6F /* CalcController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalcController.swift; sourceTree = ""; }; + 03474F3E2AF9487D00532F6F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 03474F412AF9487D00532F6F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 03474F432AF9487D00532F6F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03474F482AF9487D00532F6F /* week6-missionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "week6-missionTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03474F4C2AF9487D00532F6F /* week6_missionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week6_missionTests.swift; sourceTree = ""; }; + 03474F522AF9487D00532F6F /* week6-missionUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "week6-missionUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03474F562AF9487D00532F6F /* week6_missionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week6_missionUITests.swift; sourceTree = ""; }; + 03474F582AF9487D00532F6F /* week6_missionUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = week6_missionUITestsLaunchTests.swift; sourceTree = ""; }; + 03474F682AF9506100532F6F /* CalcControllerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalcControllerViewModel.swift; sourceTree = ""; }; + 03474F6A2AF9509100532F6F /* ButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = ""; }; + 03474F6C2AF950CA00532F6F /* CalcHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalcHeaderCell.swift; sourceTree = ""; }; + 03474F6E2AF950F900532F6F /* CalculatorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorButton.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 03474F2F2AF9487C00532F6F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F452AF9487D00532F6F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F4F2AF9487D00532F6F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03474F292AF9487C00532F6F = { + isa = PBXGroup; + children = ( + 03474F342AF9487C00532F6F /* week6-mission */, + 03474F4B2AF9487D00532F6F /* week6-missionTests */, + 03474F552AF9487D00532F6F /* week6-missionUITests */, + 03474F332AF9487C00532F6F /* Products */, + ); + sourceTree = ""; + }; + 03474F332AF9487C00532F6F /* Products */ = { + isa = PBXGroup; + children = ( + 03474F322AF9487C00532F6F /* week6-mission.app */, + 03474F482AF9487D00532F6F /* week6-missionTests.xctest */, + 03474F522AF9487D00532F6F /* week6-missionUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 03474F342AF9487C00532F6F /* week6-mission */ = { + isa = PBXGroup; + children = ( + 03474F392AF9487C00532F6F /* CalcController.swift */, + 03474F682AF9506100532F6F /* CalcControllerViewModel.swift */, + 03474F672AF94FCE00532F6F /* Views */, + 03474F662AF94FC500532F6F /* Models */, + 03474F652AF94F9400532F6F /* Supporting */, + 03474F432AF9487D00532F6F /* Info.plist */, + ); + path = "week6-mission"; + sourceTree = ""; + }; + 03474F4B2AF9487D00532F6F /* week6-missionTests */ = { + isa = PBXGroup; + children = ( + 03474F4C2AF9487D00532F6F /* week6_missionTests.swift */, + ); + path = "week6-missionTests"; + sourceTree = ""; + }; + 03474F552AF9487D00532F6F /* week6-missionUITests */ = { + isa = PBXGroup; + children = ( + 03474F562AF9487D00532F6F /* week6_missionUITests.swift */, + 03474F582AF9487D00532F6F /* week6_missionUITestsLaunchTests.swift */, + ); + path = "week6-missionUITests"; + sourceTree = ""; + }; + 03474F652AF94F9400532F6F /* Supporting */ = { + isa = PBXGroup; + children = ( + 03474F352AF9487C00532F6F /* AppDelegate.swift */, + 03474F372AF9487C00532F6F /* SceneDelegate.swift */, + 03474F3E2AF9487D00532F6F /* Assets.xcassets */, + 03474F402AF9487D00532F6F /* LaunchScreen.storyboard */, + ); + path = Supporting; + sourceTree = ""; + }; + 03474F662AF94FC500532F6F /* Models */ = { + isa = PBXGroup; + children = ( + 03474F6E2AF950F900532F6F /* CalculatorButton.swift */, + ); + path = Models; + sourceTree = ""; + }; + 03474F672AF94FCE00532F6F /* Views */ = { + isa = PBXGroup; + children = ( + 03474F6A2AF9509100532F6F /* ButtonCell.swift */, + 03474F6C2AF950CA00532F6F /* CalcHeaderCell.swift */, + ); + path = Views; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 03474F312AF9487C00532F6F /* week6-mission */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03474F5C2AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-mission" */; + buildPhases = ( + 03474F2E2AF9487C00532F6F /* Sources */, + 03474F2F2AF9487C00532F6F /* Frameworks */, + 03474F302AF9487C00532F6F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "week6-mission"; + productName = "week6-mission"; + productReference = 03474F322AF9487C00532F6F /* week6-mission.app */; + productType = "com.apple.product-type.application"; + }; + 03474F472AF9487D00532F6F /* week6-missionTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03474F5F2AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-missionTests" */; + buildPhases = ( + 03474F442AF9487D00532F6F /* Sources */, + 03474F452AF9487D00532F6F /* Frameworks */, + 03474F462AF9487D00532F6F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03474F4A2AF9487D00532F6F /* PBXTargetDependency */, + ); + name = "week6-missionTests"; + productName = "week6-missionTests"; + productReference = 03474F482AF9487D00532F6F /* week6-missionTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 03474F512AF9487D00532F6F /* week6-missionUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03474F622AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-missionUITests" */; + buildPhases = ( + 03474F4E2AF9487D00532F6F /* Sources */, + 03474F4F2AF9487D00532F6F /* Frameworks */, + 03474F502AF9487D00532F6F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03474F542AF9487D00532F6F /* PBXTargetDependency */, + ); + name = "week6-missionUITests"; + productName = "week6-missionUITests"; + productReference = 03474F522AF9487D00532F6F /* week6-missionUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 03474F2A2AF9487C00532F6F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 03474F312AF9487C00532F6F = { + CreatedOnToolsVersion = 14.3.1; + }; + 03474F472AF9487D00532F6F = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 03474F312AF9487C00532F6F; + }; + 03474F512AF9487D00532F6F = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 03474F312AF9487C00532F6F; + }; + }; + }; + buildConfigurationList = 03474F2D2AF9487C00532F6F /* Build configuration list for PBXProject "week6-mission" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 03474F292AF9487C00532F6F; + productRefGroup = 03474F332AF9487C00532F6F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 03474F312AF9487C00532F6F /* week6-mission */, + 03474F472AF9487D00532F6F /* week6-missionTests */, + 03474F512AF9487D00532F6F /* week6-missionUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 03474F302AF9487C00532F6F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03474F422AF9487D00532F6F /* LaunchScreen.storyboard in Resources */, + 03474F3F2AF9487D00532F6F /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F462AF9487D00532F6F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F502AF9487D00532F6F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 03474F2E2AF9487C00532F6F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03474F3A2AF9487C00532F6F /* CalcController.swift in Sources */, + 03474F6F2AF950F900532F6F /* CalculatorButton.swift in Sources */, + 03474F362AF9487C00532F6F /* AppDelegate.swift in Sources */, + 03474F382AF9487C00532F6F /* SceneDelegate.swift in Sources */, + 03474F6D2AF950CA00532F6F /* CalcHeaderCell.swift in Sources */, + 03474F6B2AF9509100532F6F /* ButtonCell.swift in Sources */, + 03474F692AF9506100532F6F /* CalcControllerViewModel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F442AF9487D00532F6F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03474F4D2AF9487D00532F6F /* week6_missionTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03474F4E2AF9487D00532F6F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03474F592AF9487D00532F6F /* week6_missionUITestsLaunchTests.swift in Sources */, + 03474F572AF9487D00532F6F /* week6_missionUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 03474F4A2AF9487D00532F6F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03474F312AF9487C00532F6F /* week6-mission */; + targetProxy = 03474F492AF9487D00532F6F /* PBXContainerItemProxy */; + }; + 03474F542AF9487D00532F6F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03474F312AF9487C00532F6F /* week6-mission */; + targetProxy = 03474F532AF9487D00532F6F /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 03474F402AF9487D00532F6F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 03474F412AF9487D00532F6F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 03474F5A2AF9487D00532F6F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 03474F5B2AF9487D00532F6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 03474F5D2AF9487D00532F6F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "week6-mission/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-mission"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 03474F5E2AF9487D00532F6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "week6-mission/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-mission"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 03474F602AF9487D00532F6F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-missionTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/week6-mission.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/week6-mission"; + }; + name = Debug; + }; + 03474F612AF9487D00532F6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-missionTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/week6-mission.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/week6-mission"; + }; + name = Release; + }; + 03474F632AF9487D00532F6F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-missionUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "week6-mission"; + }; + name = Debug; + }; + 03474F642AF9487D00532F6F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.week6-missionUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "week6-mission"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03474F2D2AF9487C00532F6F /* Build configuration list for PBXProject "week6-mission" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03474F5A2AF9487D00532F6F /* Debug */, + 03474F5B2AF9487D00532F6F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03474F5C2AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-mission" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03474F5D2AF9487D00532F6F /* Debug */, + 03474F5E2AF9487D00532F6F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03474F5F2AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-missionTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03474F602AF9487D00532F6F /* Debug */, + 03474F612AF9487D00532F6F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03474F622AF9487D00532F6F /* Build configuration list for PBXNativeTarget "week6-missionUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03474F632AF9487D00532F6F /* Debug */, + 03474F642AF9487D00532F6F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 03474F2A2AF9487C00532F6F /* Project object */; +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..5b0ec06 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + week6-mission.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift b/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift new file mode 100644 index 0000000..ce1f32f --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift @@ -0,0 +1,101 @@ +// +// ViewController.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +class CalcController: UIViewController { + + let viewModel: CalcControllerViewModel + + // MARK: - UI Components + private let collectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .black + collectionView.register(CalcHeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CalcHeaderCell.identifier) + collectionView.register(ButtonCell.self, forCellWithReuseIdentifier: ButtonCell.identifier) + return collectionView + }() + + + // MARK: - Lifecycle + init(viewModel: CalcControllerViewModel = CalcControllerViewModel()) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = .systemPurple + self.setupUI() + + self.collectionView.delegate = self + self.collectionView.dataSource = self + } + + // MARK: = UI Setup + private func setupUI() { + self.view.addSubview(self.collectionView) + self.collectionView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + self.collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + self.collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + self.collectionView.topAnchor.constraint(equalTo: self.view.topAnchor), + self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), + + ]) + } +} + +// MARK: - CollectionView Methods +extension CalcController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + + // MARK: - Normal Cells (Buttons) + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.viewModel.calcButtonCells.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ButtonCell.identifier, for: indexPath) as? ButtonCell else { + fatalError("Failed to dequeue ButtonCell in CalcController.") + } + let calcButton = self.viewModel.calcButtonCells[indexPath.row] + + cell.configure(with: calcButton) + + return cell + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + + let calcButton = self.viewModel.calcButtonCells[indexPath.row] + + // 0 버튼의 사이즈만 길게 + switch calcButton { + case let .number(int) where int == 0: + return CGSize( + width: (view.frame.self.width/5)*2 + ((view.frame.self.width/5)/3), + height: view.frame.size.width/5 + ) + default: + return CGSize(width: view.frame.size.width/5, height: view.frame.size.width/5) + } + } + + // 버튼의 배치?? + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return (self.view.frame.width/5)/4 + } +} + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift b/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift new file mode 100644 index 0000000..a0f2385 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift @@ -0,0 +1,23 @@ +// +// CalcControllerViewModel.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import Foundation + +class CalcControllerViewModel { + // MARK: - TableView DataSource Array + // 계산기 버튼 위치 + // Model(CalculatorButton)의 정보(text, color)를 View(ButtonCell)에 가져오고, ViewModel에서 이 위치에 따라 배치 + let calcButtonCells: [CalculatorButton] = [ + .allClear, .plusMinus, .percentage, .divide, + .number(7), .number(8), .number(9), .multiply, + .number(4), .number(5), .number(6), .subtract, + .number(3), .number(2), .number(1), .add, + .number(0), .decimal, .equals + ] + + private(set) lazy var calcHeaderLabel: String = "42" +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Info.plist b/Week 6/Shimmy/week6-mission/week6-mission/Info.plist new file mode 100644 index 0000000..0eb786d --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Info.plist @@ -0,0 +1,23 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift new file mode 100644 index 0000000..891cb76 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift @@ -0,0 +1,85 @@ +// +// CalculaterButton.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +enum CalculatorButton { + case allClear + case plusMinus + case percentage + case divide + case multiply + case subtract + case add + case equals + case number(Int) + case decimal + + init(calcButton: CalculatorButton) { + switch calcButton { + + case .allClear, .plusMinus, .percentage, .divide, .multiply, .subtract, .add, .equals, .decimal: + self = calcButton + case .number(let int): + if int.description.count == 1 { + self = calcButton + } else { + fatalError("CalculatorButton.number Int was not 1 digit during init") + } + } + } +} + +extension CalculatorButton { + // 버튼 글자 + var title: String { + switch self { + case .allClear: + return "AC" + case .plusMinus: + return "+/-" + case .percentage: + return "%" + case .divide: + return "/" + case .multiply: + return "x" + case .subtract: + return "-" + case .add: + return "+" + case .equals: + return "=" + case .number(let int): + return int.description + case .decimal: + return "." + } + } + + // 원래 버튼 색 + var color: UIColor { + switch self { + case .allClear, .plusMinus, .percentage: + return .lightGray + case .divide, .multiply, .subtract, .add, .equals: + return .systemOrange + case .number, .decimal: + return .darkGray + } + } + + // 누르면 바뀌는 색 + var selectedColor: UIColor? { + switch self { + case .allClear, .plusMinus, .percentage, .equals, .number, .decimal: + return nil + case .divide, .multiply, .subtract, .add: + return .white + } + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/AppDelegate.swift b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/AppDelegate.swift new file mode 100644 index 0000000..5abb53f --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AccentColor.colorset/Contents.json b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AppIcon.appiconset/Contents.json b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/Contents.json b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Base.lproj/LaunchScreen.storyboard b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Supporting/SceneDelegate.swift b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/SceneDelegate.swift new file mode 100644 index 0000000..111912d --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Supporting/SceneDelegate.swift @@ -0,0 +1,68 @@ +// +// SceneDelegate.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + + let window = UIWindow(windowScene: windowScene) + + // 처음 보일 main ViewController + let rootViewController = CalcController() + + // NavigationController 설정 + let navigationController = UINavigationController(rootViewController: rootViewController) + + // 위에서 만든 viewController를 첫 화면으로 설정(navigationController로 설정 + window.rootViewController = navigationController + + // 화면에 보이게 설정 + window.makeKeyAndVisible() + + self.window = window + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift b/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift new file mode 100644 index 0000000..2c1adec --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift @@ -0,0 +1,63 @@ +// +// ButtonCell.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +class ButtonCell: UICollectionViewCell { + static let identifier = "ButtonCell" + + // MARK: - Variables + private(set) var calculatorButton: CalculatorButton! + + // MARK: - UI Components + private let titleLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.font = .systemFont(ofSize: 40, weight: .regular) + label.text = "Error" + return label + }() + + // MARK: - Configure + public func configure(with calculatorButton: CalculatorButton) { + // calculatorButton 모델에서 불러옴 + self.calculatorButton = calculatorButton + + // calculatorButton의 title을 titleLabel의 text로 + self.titleLabel.text = calculatorButton.title + // color를 backgroundColor로 + self.backgroundColor = calculatorButton.color + self.titleLabel.textColor = .white + + self.setupUI() + } + + private func setupUI() { + self.addSubview(titleLabel) + titleLabel.translatesAutoresizingMaskIntoConstraints = false + + switch self.calculatorButton { + case let .number(int) where int == 0: + self.layer.cornerRadius = 36 + + default: + self.layer.cornerRadius = self.frame.size.width/2 + } + + + // 버튼 모양 둥글게 cornerRadius 조절 + //self.layer.cornerRadius = self.frame.size.width/2 + + // titleLable 위치를 각 버튼의 가운데로 설정 + NSLayoutConstraint.activate([ + self.titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor), + self.titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor), + self.titleLabel.heightAnchor.constraint(equalTo: self.heightAnchor), + self.titleLabel.widthAnchor.constraint(equalTo: self.widthAnchor), + ]) + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift b/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift new file mode 100644 index 0000000..729100c --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift @@ -0,0 +1,12 @@ +// +// CalcHeaderCell.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/07. +// + +import UIKit + +class CalcHeaderCell: UICollectionReusableView { + static let identifier = "CalcHeaderCell" +} diff --git a/Week 6/Shimmy/week6-mission/week6-missionTests/week6_missionTests.swift b/Week 6/Shimmy/week6-mission/week6-missionTests/week6_missionTests.swift new file mode 100644 index 0000000..2bd298a --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-missionTests/week6_missionTests.swift @@ -0,0 +1,36 @@ +// +// week6_missionTests.swift +// week6-missionTests +// +// Created by Seungbo Shim on 2023/11/07. +// + +import XCTest +@testable import week6_mission + +final class week6_missionTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITests.swift b/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITests.swift new file mode 100644 index 0000000..7da66d8 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITests.swift @@ -0,0 +1,41 @@ +// +// week6_missionUITests.swift +// week6-missionUITests +// +// Created by Seungbo Shim on 2023/11/07. +// + +import XCTest + +final class week6_missionUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITestsLaunchTests.swift b/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITestsLaunchTests.swift new file mode 100644 index 0000000..f53ca83 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-missionUITests/week6_missionUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// week6_missionUITestsLaunchTests.swift +// week6-missionUITests +// +// Created by Seungbo Shim on 2023/11/07. +// + +import XCTest + +final class week6_missionUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} From bb8aad9c3544a401433f3b41830e5baa086872cc Mon Sep 17 00:00:00 2001 From: Seungbo Shim Date: Tue, 21 Nov 2023 15:08:46 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[7=EC=A3=BC=EC=B0=A8]=20Shimmy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../week6-mission.xcodeproj/project.pbxproj | 8 + .../week6-mission/CalcController.swift | 74 +- .../CalcControllerViewModel.swift | 238 ++++++- .../Models/CalculatorButton.swift | 7 + .../Models/CalculatorOperation.swift | 28 + .../week6-mission/String+Extention.swift | 26 + .../week6-mission/Views/ButtonCell.swift | 14 +- .../week6-mission/Views/CalcHeaderCell.swift | 46 +- .../naver-movie.xcodeproj/project.pbxproj | 644 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/xcschememanagement.plist | 14 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../naver-movie/Assets.xcassets/Contents.json | 6 + .../Extensions/Image+Extension.swift | 23 + .../naver-movie/Model/MovieBox.swift | 50 ++ .../Preview Assets.xcassets/Contents.json | 6 + .../naver-movie/View/ContentView.swift | 35 + .../naver-movie/View/MovieItem.swift | 38 ++ .../naver-movie/View/MovieList.swift | 26 + .../naver-movie/View/SearchView.swift | 33 + .../naver-movie/ViewModel/MovieFinder.swift | 102 +++ .../naver-movie/naver_movieApp.swift | 17 + .../naver-movieTests/naver_movieTests.swift | 36 + .../naver_movieUITests.swift | 41 ++ .../naver_movieUITestsLaunchTests.swift | 32 + 27 files changed, 1572 insertions(+), 11 deletions(-) create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorOperation.swift create mode 100644 Week 6/Shimmy/week6-mission/week6-mission/String+Extention.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.pbxproj create mode 100644 Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/Contents.json create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Extensions/Image+Extension.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Model/MovieBox.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/View/ContentView.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/View/MovieItem.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/View/MovieList.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/View/SearchView.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/ViewModel/MovieFinder.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movie/naver_movieApp.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movieTests/naver_movieTests.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITests.swift create mode 100644 Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITestsLaunchTests.swift diff --git a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj index 541d10d..c7f644b 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj +++ b/Week 6/Shimmy/week6-mission/week6-mission.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ 03474F6B2AF9509100532F6F /* ButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6A2AF9509100532F6F /* ButtonCell.swift */; }; 03474F6D2AF950CA00532F6F /* CalcHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6C2AF950CA00532F6F /* CalcHeaderCell.swift */; }; 03474F6F2AF950F900532F6F /* CalculatorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03474F6E2AF950F900532F6F /* CalculatorButton.swift */; }; + 03F9FC972B02BC9A00FA3DCA /* CalculatorOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F9FC962B02BC9A00FA3DCA /* CalculatorOperation.swift */; }; + 03F9FC992B02C04F00FA3DCA /* String+Extention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F9FC982B02C04F00FA3DCA /* String+Extention.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,6 +57,8 @@ 03474F6A2AF9509100532F6F /* ButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonCell.swift; sourceTree = ""; }; 03474F6C2AF950CA00532F6F /* CalcHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalcHeaderCell.swift; sourceTree = ""; }; 03474F6E2AF950F900532F6F /* CalculatorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorButton.swift; sourceTree = ""; }; + 03F9FC962B02BC9A00FA3DCA /* CalculatorOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorOperation.swift; sourceTree = ""; }; + 03F9FC982B02C04F00FA3DCA /* String+Extention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extention.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -107,6 +111,7 @@ children = ( 03474F392AF9487C00532F6F /* CalcController.swift */, 03474F682AF9506100532F6F /* CalcControllerViewModel.swift */, + 03F9FC982B02C04F00FA3DCA /* String+Extention.swift */, 03474F672AF94FCE00532F6F /* Views */, 03474F662AF94FC500532F6F /* Models */, 03474F652AF94F9400532F6F /* Supporting */, @@ -147,6 +152,7 @@ isa = PBXGroup; children = ( 03474F6E2AF950F900532F6F /* CalculatorButton.swift */, + 03F9FC962B02BC9A00FA3DCA /* CalculatorOperation.swift */, ); path = Models; sourceTree = ""; @@ -294,9 +300,11 @@ 03474F6F2AF950F900532F6F /* CalculatorButton.swift in Sources */, 03474F362AF9487C00532F6F /* AppDelegate.swift in Sources */, 03474F382AF9487C00532F6F /* SceneDelegate.swift in Sources */, + 03F9FC992B02C04F00FA3DCA /* String+Extention.swift in Sources */, 03474F6D2AF950CA00532F6F /* CalcHeaderCell.swift in Sources */, 03474F6B2AF9509100532F6F /* ButtonCell.swift in Sources */, 03474F692AF9506100532F6F /* CalcControllerViewModel.swift in Sources */, + 03F9FC972B02BC9A00FA3DCA /* CalculatorOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift b/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift index ce1f32f..65370d6 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift +++ b/Week 6/Shimmy/week6-mission/week6-mission/CalcController.swift @@ -18,8 +18,12 @@ class CalcController: UIViewController { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .black - collectionView.register(CalcHeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CalcHeaderCell.identifier) + + // 등록할 뷰의 종류 지정 + collectionView.register(CalcHeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: CalcHeaderCell.reuseIdentifier) collectionView.register(ButtonCell.self, forCellWithReuseIdentifier: ButtonCell.identifier) + + collectionView.translatesAutoresizingMaskIntoConstraints = false return collectionView }() @@ -41,31 +45,79 @@ class CalcController: UIViewController { self.collectionView.delegate = self self.collectionView.dataSource = self + + self.viewModel.updateViews = { + [weak self] in + DispatchQueue.main.async { + [weak self] in + self?.collectionView.reloadData() + } + } } // MARK: = UI Setup private func setupUI() { self.view.addSubview(self.collectionView) - self.collectionView.translatesAutoresizingMaskIntoConstraints = false + - NSLayoutConstraint.activate([ + setCollectionViewLayout() + } + + private func setCollectionViewLayout() { + let collectionViewConstraint = [ self.collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), self.collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), self.collectionView.topAnchor.constraint(equalTo: self.view.topAnchor), self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), - - ]) + ] + NSLayoutConstraint.activate(collectionViewConstraint) } } // MARK: - CollectionView Methods extension CalcController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + // MARK: - Section Header Cell + func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: CalcHeaderCell.reuseIdentifier, for: indexPath) as? CalcHeaderCell else { + fatalError("Failed to dequeue CalcHeaderCell in CalcController") + } + header.configure(currentCalcText: self.viewModel.calcHeaderLabel) + + return header + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + + + // Cell Spacing + let totalCellHeight = view.frame.size.width + let totalVerticalCellSpacing = CGFloat(10*4) + + // Screen height + let window = view.window?.windowScene?.keyWindow + let topPadding = window?.safeAreaInsets.top ?? 0 + let bottomPadding = window?.safeAreaInsets.bottom ?? 0 + + // (상단, 하단의 safeArea를 포함하는 padding은 제외한)사용 가능한 뷰의 height + let avaliableScreenHeight = view.frame.size.height - topPadding - bottomPadding + + // Calculate Header Height + let headerHeight = (avaliableScreenHeight - totalCellHeight) - totalVerticalCellSpacing + + return CGSize(width: view.frame.size.width, height: headerHeight) + } + // MARK: - Normal Cells (Buttons) func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.viewModel.calcButtonCells.count } + // 각 셀의 생성, 구성 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ButtonCell.identifier, for: indexPath) as? ButtonCell else { fatalError("Failed to dequeue ButtonCell in CalcController.") @@ -74,6 +126,13 @@ extension CalcController: UICollectionViewDelegate, UICollectionViewDataSource, cell.configure(with: calcButton) + // 연산자의 색상 변경? + if let operation = self.viewModel.operation, self.viewModel.secondNumber == nil { + if operation.title == calcButton.title { + cell.setOperationSelected() + } + } + return cell } @@ -97,5 +156,10 @@ extension CalcController: UICollectionViewDelegate, UICollectionViewDataSource, func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return (self.view.frame.width/5)/4 } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let buttonCell = self.viewModel.calcButtonCells[indexPath.row] + self.viewModel.didSelectButton(with: buttonCell) + } } diff --git a/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift b/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift index a0f2385..1d96a4c 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift +++ b/Week 6/Shimmy/week6-mission/week6-mission/CalcControllerViewModel.swift @@ -7,7 +7,14 @@ import Foundation +enum CurrentNumber { + case firstNumber + case secondNumber +} + class CalcControllerViewModel { + var updateViews: (()->Void)? + // MARK: - TableView DataSource Array // 계산기 버튼 위치 // Model(CalculatorButton)의 정보(text, color)를 View(ButtonCell)에 가져오고, ViewModel에서 이 위치에 따라 배치 @@ -19,5 +26,234 @@ class CalcControllerViewModel { .number(0), .decimal, .equals ] - private(set) lazy var calcHeaderLabel: String = "42" + // private(set) : 해당 변수의 setter를 private으로 지정 + private(set) lazy var calcHeaderLabel: String = self.firstNumber ?? "0" + private(set) var currentNumber: CurrentNumber = .firstNumber + + private(set) var firstNumber: String? = nil { + didSet { + self.calcHeaderLabel = self.firstNumber?.description ?? "0" + } + } + private(set) var secondNumber: String? = nil { + didSet { + self.calcHeaderLabel = self.secondNumber?.description ?? "0" + } + } + private(set) var operation: CalculatorOperation? = nil + private(set) var firstNumberIsDecimal: Bool = false + private(set) var secondNumberIsDecimal: Bool = false + + var eitherNumberIsDecimal: Bool { + return firstNumberIsDecimal || secondNumberIsDecimal + } + + // MARK: - Memory Variables + // "=" 누르면 이전 숫자가 반복되어 연산 + private(set) var prevNumber: String? = nil + private(set) var prevOperation: CalculatorOperation? = nil + + // MARK: - Businiss Logic + public func didSelectButton(with calcButton: CalculatorButton) { + switch calcButton { + case .allClear: self.didSelectAllclear() + case .plusMinus: self.didSelectPlusMinus() + case .percentage: self.didSelectPercentage() + case .divide: self.didSelectOperation(with: .divide) + case .multiply: self.didSelectOperation(with: .multiply) + case .subtract: self.didSelectOperation(with: .subtract) + case .add: self.didSelectOperation(with: .add) + case .equals: self.didSelectedEqualsButton() + case .number(let number): self.didSelectNumber(with: number) + case .decimal: self.didSelectDecimal() + } + + self.updateViews?() + } + + // MARK: - All Clear + private func didSelectAllclear() { + self.calcHeaderLabel = "0" + self.currentNumber = .firstNumber + self.firstNumber = nil + self.secondNumber = nil + self.operation = nil + self.firstNumberIsDecimal = false + self.secondNumberIsDecimal = false + self.prevNumber = nil + self.prevOperation = nil + } + + // MARK: - Selecting Numbers + private func didSelectNumber(with number: Int) { + + if self.currentNumber == .firstNumber { + // if let -> 상수로 옵셔널 바인딩, if let -> 변수로 옵셔널 바인딩 + if var firstNumber = self.firstNumber { + // 기존에 입력되어있던 firstNumber에 새로 입력한 (파라미터의)number를 뒤에 이어서 붙여주는 동작 + firstNumber.append(number.description) + self.firstNumber = firstNumber + self.prevNumber = firstNumber + } else { + // 기존에 아무 것도 입력되어있지 않다면, number를 String으로 변환하여 firstNumber에 넣어준다. + self.firstNumber = number.description + self.prevNumber = number.description + } + } else { + if var secondNumber = self.secondNumber { + // 기존에 입력되어있던 secondNumber에 새로 입력한 (파라미터의)number를 뒤에 이어서 붙여주는 동작 + secondNumber.append(number.description) + self.secondNumber = secondNumber + self.secondNumber = secondNumber + } else { + // 기존에 아무 것도 입력되어있지 않다면, number를 String으로 변환하여 secondNumber에 넣어준다. + self.secondNumber = number.description + self.prevNumber = number.description + } + } + } + + // MARK: - Equals & ArithmeticOperations + + private func didSelectedEqualsButton() { + + if let operation = self.operation, + let firstNumber = self.firstNumber?.toDouble, + // secondNumber가 존재하는 경우 + let secondNumber = self.secondNumber?.toDouble { + + // firstNumber와 secondNumber 다음에 정상적으로 Equals가 눌러지는 경우 + let result = self.getOperationResult(operation, firstNumber, secondNumber) + // firstNumber과 secondNumber가 둘 다 Decimal(소수)라면 그대로 출력하고, 그렇지 않으면 Int로 변환하여 출력한다. + let resultString = self.eitherNumberIsDecimal ? result.description : result.toInt?.description + + self.secondNumber = nil + self.prevOperation = operation + self.operation = nil + self.firstNumber = resultString + self.currentNumber = .firstNumber + // secondNumberrk 존재하지 않는 경우 -> firstNumber와 prevOperation을 가지고 계산한다. + } else if let prevOperation = self.prevOperation, + let firstNumber = self.firstNumber?.toDouble, + // 이전에 입력된 숫자가 없는 경우 + let prevNumber = self.prevNumber?.toDouble { + + // firstNumber와 operation을 기반한 연산으로 result가 업데이트된다. + let result = self.getOperationResult(prevOperation, firstNumber, prevNumber) + let resultString = self.eitherNumberIsDecimal ? result.description : result.toInt?.description + self.firstNumber = resultString + } + } + + private func didSelectOperation(with operation: CalculatorOperation) { + // firstNumber와 operation만 입력된 상태일 때 + if self.currentNumber == .firstNumber { + self.operation = operation + self.currentNumber = .secondNumber + // firstNumber, operation, secondNumber까지 입력된 상태일 때 -> 새로운 operation이 들어오면 이전의 결과값을 출력해준다. + } else if self.currentNumber == .secondNumber { + if let prevOperation = self.operation, + let firstNumber = self.firstNumber?.toDouble, + let secondNumber = self.secondNumber?.toDouble { + // 이후 추가로 연산자가 들어올 경우 이전 값을 출력해주고 새로운 연산자를 받아야 하는데, 출력 값은 이전에 입력받았던 연산자를 사용해야하므로, opearion이 아닌 prevOperation을 사용한다. + let result = self.getOperationResult(prevOperation, firstNumber, secondNumber) + let resultString = self.eitherNumberIsDecimal ? result.description : result.toInt?.description + self.secondNumber = nil + self.firstNumber = resultString + self.currentNumber = .secondNumber + self.operation = operation + } else { + // secondNumber가 없으면 operation만 새로 갱신해준다. + self.operation = operation + } + } + } + + // MARK: - Helper + private func getOperationResult(_ operation: CalculatorOperation, _ firstNumber: Double?, _ secondNumber: Double?) -> Double { + guard let firstNumber = firstNumber, + let secondNumber = secondNumber else { return 0 } + switch operation { + case .divide: + return (firstNumber / secondNumber) + case .multiply: + return (firstNumber * secondNumber) + case .subtract: + return (firstNumber - secondNumber) + case .add: + return (firstNumber + secondNumber) + + } + } + + // MARK: - Action Buttons + private func didSelectPlusMinus() { + // firstNumber만 입력된 경우 + if self.currentNumber == .firstNumber, var number = self.firstNumber { + if number .contains("-") { + number.removeFirst() + } else { + number.insert("-", at: number.startIndex) + } + + self.firstNumber = number + self.prevNumber = number + } else if self.currentNumber == .secondNumber, var number = self.secondNumber { + // secondNumber까지 입력된 경우 + if number .contains("-") { + number.removeFirst() + } else { + number.insert("-", at: number.startIndex) + } + + self.secondNumber = number + self.prevNumber = number + } + } + + private func didSelectPercentage() { + + if self.currentNumber == .firstNumber, var number = self.firstNumber?.toDouble { + number /= 100 + if number.isInteger { + self.firstNumber = number.toInt?.description + } else { + self.firstNumber = number.description + self.firstNumberIsDecimal = true + } + } else if self.currentNumber == .secondNumber, var number = self.secondNumber?.toDouble { + + number /= 100 + if number.isInteger { + self.secondNumber = number.toInt?.description + } else { + self.secondNumber = number.description + self.secondNumberIsDecimal = true + } + } + } + + private func didSelectDecimal() { + + if self.currentNumber == .firstNumber { + self.firstNumberIsDecimal = true + + if let firstNumber = self.firstNumber, !firstNumber.contains(".") { + self.firstNumber = firstNumber.appending(".") + + } else if self.firstNumber == nil { + self.firstNumber = "0." + } + } else if self.currentNumber == .secondNumber { + self.secondNumberIsDecimal = true + + if let secondNumber = self.secondNumber, !secondNumber.contains(".") { + self.secondNumber = secondNumber.appending(".") + + } else if self.secondNumber == nil { + self.secondNumber = "0." + } + } + } + } diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift index 891cb76..f843ffe 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift +++ b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorButton.swift @@ -61,6 +61,13 @@ extension CalculatorButton { } } + enum Color: String { + case lightGray + case systemOrange + case darkGray + case white + } + // 원래 버튼 색 var color: UIColor { switch self { diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorOperation.swift b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorOperation.swift new file mode 100644 index 0000000..c498ae6 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/Models/CalculatorOperation.swift @@ -0,0 +1,28 @@ +// +// CalculatorOperation.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/14. +// + +import Foundation + +enum CalculatorOperation { + case divide + case multiply + case subtract + case add + + var title: String { + switch self { + case .divide: + return "/" + case .multiply: + return "x" + case .subtract: + return "-" + case .add: + return "+" + } + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/String+Extention.swift b/Week 6/Shimmy/week6-mission/week6-mission/String+Extention.swift new file mode 100644 index 0000000..316edf2 --- /dev/null +++ b/Week 6/Shimmy/week6-mission/week6-mission/String+Extention.swift @@ -0,0 +1,26 @@ +// +// String+Extention.swift +// week6-mission +// +// Created by Seungbo Shim on 2023/11/14. +// + +import Foundation + +extension String { + var toDouble: Double? { + return Double(self) + } +} + +extension Double { + var toInt: Int? { + return Int(self) + } +} + +extension FloatingPoint { + var isInteger: Bool { + return rounded() == self + } +} diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift b/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift index 2c1adec..dabd273 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift +++ b/Week 6/Shimmy/week6-mission/week6-mission/Views/ButtonCell.swift @@ -36,10 +36,16 @@ class ButtonCell: UICollectionViewCell { self.setupUI() } + public func setOperationSelected() { + self.titleLabel.textColor = .orange + self.backgroundColor = .white + } + private func setupUI() { self.addSubview(titleLabel) titleLabel.translatesAutoresizingMaskIntoConstraints = false + // 버튼 모양 둥글게 cornerRadius 조절 (0버튼만 다르게) switch self.calculatorButton { case let .number(int) where int == 0: self.layer.cornerRadius = 36 @@ -48,10 +54,6 @@ class ButtonCell: UICollectionViewCell { self.layer.cornerRadius = self.frame.size.width/2 } - - // 버튼 모양 둥글게 cornerRadius 조절 - //self.layer.cornerRadius = self.frame.size.width/2 - // titleLable 위치를 각 버튼의 가운데로 설정 NSLayoutConstraint.activate([ self.titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor), @@ -60,4 +62,8 @@ class ButtonCell: UICollectionViewCell { self.titleLabel.widthAnchor.constraint(equalTo: self.widthAnchor), ]) } + + override func prepareForReuse() { + + } } diff --git a/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift b/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift index 729100c..ad5eced 100644 --- a/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift +++ b/Week 6/Shimmy/week6-mission/week6-mission/Views/CalcHeaderCell.swift @@ -8,5 +8,49 @@ import UIKit class CalcHeaderCell: UICollectionReusableView { - static let identifier = "CalcHeaderCell" + static let reuseIdentifier = "CalcHeaderCell" + + // MARK: - UI Components + private let label: UILabel = { + let label = UILabel() + label.textColor = .white + label.textAlignment = .right + label.font = .systemFont(ofSize: 72, weight: .regular) + label.text = "Error" + label.translatesAutoresizingMaskIntoConstraints = false + + return label + }() + + // MARK: - LifeCylcle + override init(frame: CGRect) { + super.init(frame: frame) + self.setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // 현재 입력된 result를 보여주는 함수 + public func configure(currentCalcText: String) { + self.label.text = currentCalcText + } + + // MARK: - UI Setup + private func setupUI() { + self.backgroundColor = .black + self.addSubview(label) + + setupLabelLayout() + } + + private func setupLabelLayout() { + let labelConstraint = [ + self.label.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor), + self.label.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor), + self.label.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), + ] + NSLayoutConstraint.activate(labelConstraint) + } } diff --git a/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.pbxproj b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.pbxproj new file mode 100644 index 0000000..799de3f --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.pbxproj @@ -0,0 +1,644 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 03163EC32B0BEC1D00A7236D /* naver_movieApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EC22B0BEC1D00A7236D /* naver_movieApp.swift */; }; + 03163EC52B0BEC1D00A7236D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EC42B0BEC1D00A7236D /* ContentView.swift */; }; + 03163EC72B0BEC1E00A7236D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03163EC62B0BEC1E00A7236D /* Assets.xcassets */; }; + 03163ECA2B0BEC1E00A7236D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 03163EC92B0BEC1E00A7236D /* Preview Assets.xcassets */; }; + 03163ED42B0BEC1E00A7236D /* naver_movieTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163ED32B0BEC1E00A7236D /* naver_movieTests.swift */; }; + 03163EDE2B0BEC1E00A7236D /* naver_movieUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EDD2B0BEC1E00A7236D /* naver_movieUITests.swift */; }; + 03163EE02B0BEC1E00A7236D /* naver_movieUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EDF2B0BEC1E00A7236D /* naver_movieUITestsLaunchTests.swift */; }; + 03163EEF2B0BECE400A7236D /* MovieFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EEE2B0BECE400A7236D /* MovieFinder.swift */; }; + 03163EF22B0BEFCE00A7236D /* MovieBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EF12B0BEFCE00A7236D /* MovieBox.swift */; }; + 03163EF52B0BF14C00A7236D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EF42B0BF14C00A7236D /* SearchView.swift */; }; + 03163EF72B0BF15400A7236D /* MovieList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EF62B0BF15400A7236D /* MovieList.swift */; }; + 03163EF92B0BF2A600A7236D /* MovieItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EF82B0BF2A600A7236D /* MovieItem.swift */; }; + 03163EFC2B0BF33600A7236D /* Image+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03163EFB2B0BF33600A7236D /* Image+Extension.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 03163ED02B0BEC1E00A7236D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 03163EB72B0BEC1D00A7236D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03163EBE2B0BEC1D00A7236D; + remoteInfo = "naver-movie"; + }; + 03163EDA2B0BEC1E00A7236D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 03163EB72B0BEC1D00A7236D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03163EBE2B0BEC1D00A7236D; + remoteInfo = "naver-movie"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 03163EBF2B0BEC1D00A7236D /* naver-movie.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "naver-movie.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03163EC22B0BEC1D00A7236D /* naver_movieApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = naver_movieApp.swift; sourceTree = ""; }; + 03163EC42B0BEC1D00A7236D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 03163EC62B0BEC1E00A7236D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 03163EC92B0BEC1E00A7236D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 03163ECF2B0BEC1E00A7236D /* naver-movieTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "naver-movieTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03163ED32B0BEC1E00A7236D /* naver_movieTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = naver_movieTests.swift; sourceTree = ""; }; + 03163ED92B0BEC1E00A7236D /* naver-movieUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "naver-movieUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 03163EDD2B0BEC1E00A7236D /* naver_movieUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = naver_movieUITests.swift; sourceTree = ""; }; + 03163EDF2B0BEC1E00A7236D /* naver_movieUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = naver_movieUITestsLaunchTests.swift; sourceTree = ""; }; + 03163EEE2B0BECE400A7236D /* MovieFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieFinder.swift; sourceTree = ""; }; + 03163EF12B0BEFCE00A7236D /* MovieBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieBox.swift; sourceTree = ""; }; + 03163EF42B0BF14C00A7236D /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = ""; }; + 03163EF62B0BF15400A7236D /* MovieList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieList.swift; sourceTree = ""; }; + 03163EF82B0BF2A600A7236D /* MovieItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItem.swift; sourceTree = ""; }; + 03163EFB2B0BF33600A7236D /* Image+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image+Extension.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 03163EBC2B0BEC1D00A7236D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ECC2B0BEC1E00A7236D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ED62B0BEC1E00A7236D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03163EB62B0BEC1D00A7236D = { + isa = PBXGroup; + children = ( + 03163EC12B0BEC1D00A7236D /* naver-movie */, + 03163ED22B0BEC1E00A7236D /* naver-movieTests */, + 03163EDC2B0BEC1E00A7236D /* naver-movieUITests */, + 03163EC02B0BEC1D00A7236D /* Products */, + ); + sourceTree = ""; + }; + 03163EC02B0BEC1D00A7236D /* Products */ = { + isa = PBXGroup; + children = ( + 03163EBF2B0BEC1D00A7236D /* naver-movie.app */, + 03163ECF2B0BEC1E00A7236D /* naver-movieTests.xctest */, + 03163ED92B0BEC1E00A7236D /* naver-movieUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 03163EC12B0BEC1D00A7236D /* naver-movie */ = { + isa = PBXGroup; + children = ( + 03163EFA2B0BF32100A7236D /* Extensions */, + 03163EF02B0BEFC400A7236D /* Model */, + 03163EED2B0BECD600A7236D /* ViewModel */, + 03163EEC2B0BECA000A7236D /* View */, + 03163EC22B0BEC1D00A7236D /* naver_movieApp.swift */, + 03163EC62B0BEC1E00A7236D /* Assets.xcassets */, + 03163EC82B0BEC1E00A7236D /* Preview Content */, + ); + path = "naver-movie"; + sourceTree = ""; + }; + 03163EC82B0BEC1E00A7236D /* Preview Content */ = { + isa = PBXGroup; + children = ( + 03163EC92B0BEC1E00A7236D /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 03163ED22B0BEC1E00A7236D /* naver-movieTests */ = { + isa = PBXGroup; + children = ( + 03163ED32B0BEC1E00A7236D /* naver_movieTests.swift */, + ); + path = "naver-movieTests"; + sourceTree = ""; + }; + 03163EDC2B0BEC1E00A7236D /* naver-movieUITests */ = { + isa = PBXGroup; + children = ( + 03163EDD2B0BEC1E00A7236D /* naver_movieUITests.swift */, + 03163EDF2B0BEC1E00A7236D /* naver_movieUITestsLaunchTests.swift */, + ); + path = "naver-movieUITests"; + sourceTree = ""; + }; + 03163EEC2B0BECA000A7236D /* View */ = { + isa = PBXGroup; + children = ( + 03163EC42B0BEC1D00A7236D /* ContentView.swift */, + 03163EF42B0BF14C00A7236D /* SearchView.swift */, + 03163EF62B0BF15400A7236D /* MovieList.swift */, + 03163EF82B0BF2A600A7236D /* MovieItem.swift */, + ); + path = View; + sourceTree = ""; + }; + 03163EED2B0BECD600A7236D /* ViewModel */ = { + isa = PBXGroup; + children = ( + 03163EEE2B0BECE400A7236D /* MovieFinder.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + 03163EF02B0BEFC400A7236D /* Model */ = { + isa = PBXGroup; + children = ( + 03163EF12B0BEFCE00A7236D /* MovieBox.swift */, + ); + path = Model; + sourceTree = ""; + }; + 03163EFA2B0BF32100A7236D /* Extensions */ = { + isa = PBXGroup; + children = ( + 03163EFB2B0BF33600A7236D /* Image+Extension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 03163EBE2B0BEC1D00A7236D /* naver-movie */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03163EE32B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movie" */; + buildPhases = ( + 03163EBB2B0BEC1D00A7236D /* Sources */, + 03163EBC2B0BEC1D00A7236D /* Frameworks */, + 03163EBD2B0BEC1D00A7236D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "naver-movie"; + productName = "naver-movie"; + productReference = 03163EBF2B0BEC1D00A7236D /* naver-movie.app */; + productType = "com.apple.product-type.application"; + }; + 03163ECE2B0BEC1E00A7236D /* naver-movieTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03163EE62B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movieTests" */; + buildPhases = ( + 03163ECB2B0BEC1E00A7236D /* Sources */, + 03163ECC2B0BEC1E00A7236D /* Frameworks */, + 03163ECD2B0BEC1E00A7236D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03163ED12B0BEC1E00A7236D /* PBXTargetDependency */, + ); + name = "naver-movieTests"; + productName = "naver-movieTests"; + productReference = 03163ECF2B0BEC1E00A7236D /* naver-movieTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 03163ED82B0BEC1E00A7236D /* naver-movieUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03163EE92B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movieUITests" */; + buildPhases = ( + 03163ED52B0BEC1E00A7236D /* Sources */, + 03163ED62B0BEC1E00A7236D /* Frameworks */, + 03163ED72B0BEC1E00A7236D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03163EDB2B0BEC1E00A7236D /* PBXTargetDependency */, + ); + name = "naver-movieUITests"; + productName = "naver-movieUITests"; + productReference = 03163ED92B0BEC1E00A7236D /* naver-movieUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 03163EB72B0BEC1D00A7236D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 03163EBE2B0BEC1D00A7236D = { + CreatedOnToolsVersion = 14.3.1; + }; + 03163ECE2B0BEC1E00A7236D = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 03163EBE2B0BEC1D00A7236D; + }; + 03163ED82B0BEC1E00A7236D = { + CreatedOnToolsVersion = 14.3.1; + TestTargetID = 03163EBE2B0BEC1D00A7236D; + }; + }; + }; + buildConfigurationList = 03163EBA2B0BEC1D00A7236D /* Build configuration list for PBXProject "naver-movie" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 03163EB62B0BEC1D00A7236D; + productRefGroup = 03163EC02B0BEC1D00A7236D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 03163EBE2B0BEC1D00A7236D /* naver-movie */, + 03163ECE2B0BEC1E00A7236D /* naver-movieTests */, + 03163ED82B0BEC1E00A7236D /* naver-movieUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 03163EBD2B0BEC1D00A7236D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03163ECA2B0BEC1E00A7236D /* Preview Assets.xcassets in Resources */, + 03163EC72B0BEC1E00A7236D /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ECD2B0BEC1E00A7236D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ED72B0BEC1E00A7236D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 03163EBB2B0BEC1D00A7236D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03163EF92B0BF2A600A7236D /* MovieItem.swift in Sources */, + 03163EF72B0BF15400A7236D /* MovieList.swift in Sources */, + 03163EF52B0BF14C00A7236D /* SearchView.swift in Sources */, + 03163EFC2B0BF33600A7236D /* Image+Extension.swift in Sources */, + 03163EC52B0BEC1D00A7236D /* ContentView.swift in Sources */, + 03163EEF2B0BECE400A7236D /* MovieFinder.swift in Sources */, + 03163EF22B0BEFCE00A7236D /* MovieBox.swift in Sources */, + 03163EC32B0BEC1D00A7236D /* naver_movieApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ECB2B0BEC1E00A7236D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03163ED42B0BEC1E00A7236D /* naver_movieTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 03163ED52B0BEC1E00A7236D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03163EE02B0BEC1E00A7236D /* naver_movieUITestsLaunchTests.swift in Sources */, + 03163EDE2B0BEC1E00A7236D /* naver_movieUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 03163ED12B0BEC1E00A7236D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03163EBE2B0BEC1D00A7236D /* naver-movie */; + targetProxy = 03163ED02B0BEC1E00A7236D /* PBXContainerItemProxy */; + }; + 03163EDB2B0BEC1E00A7236D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03163EBE2B0BEC1D00A7236D /* naver-movie */; + targetProxy = 03163EDA2B0BEC1E00A7236D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 03163EE12B0BEC1E00A7236D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 03163EE22B0BEC1E00A7236D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 03163EE42B0BEC1E00A7236D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"naver-movie/Preview Content\""; + DEVELOPMENT_TEAM = T5RP2CSNDU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movie"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 03163EE52B0BEC1E00A7236D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"naver-movie/Preview Content\""; + DEVELOPMENT_TEAM = T5RP2CSNDU; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movie"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 03163EE72B0BEC1E00A7236D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movieTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/naver-movie.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/naver-movie"; + }; + name = Debug; + }; + 03163EE82B0BEC1E00A7236D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movieTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/naver-movie.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/naver-movie"; + }; + name = Release; + }; + 03163EEA2B0BEC1E00A7236D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movieUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "naver-movie"; + }; + name = Debug; + }; + 03163EEB2B0BEC1E00A7236D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T5RP2CSNDU; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shimmy.naver-movieUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "naver-movie"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03163EBA2B0BEC1D00A7236D /* Build configuration list for PBXProject "naver-movie" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03163EE12B0BEC1E00A7236D /* Debug */, + 03163EE22B0BEC1E00A7236D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03163EE32B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movie" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03163EE42B0BEC1E00A7236D /* Debug */, + 03163EE52B0BEC1E00A7236D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03163EE62B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movieTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03163EE72B0BEC1E00A7236D /* Debug */, + 03163EE82B0BEC1E00A7236D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 03163EE92B0BEC1E00A7236D /* Build configuration list for PBXNativeTarget "naver-movieUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03163EEA2B0BEC1E00A7236D /* Debug */, + 03163EEB2B0BEC1E00A7236D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 03163EB72B0BEC1D00A7236D /* Project object */; +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..608c223 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie.xcodeproj/xcuserdata/seungboshim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + naver-movie.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AccentColor.colorset/Contents.json b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AppIcon.appiconset/Contents.json b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/Contents.json b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Extensions/Image+Extension.swift b/Week 7/Shimmy/naver-movie/naver-movie/Extensions/Image+Extension.swift new file mode 100644 index 0000000..855f9f2 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Extensions/Image+Extension.swift @@ -0,0 +1,23 @@ +// +// Image+Extension.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +extension Image { + func ImageModifier() -> some View { + self + .resizable() + .scaledToFit() + } + + func IconModifier() -> some View { + self + .ImageModifier() + .frame(maxWidth: 200) + .foregroundColor(.blue.opacity(0.5)) + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Model/MovieBox.swift b/Week 7/Shimmy/naver-movie/naver-movie/Model/MovieBox.swift new file mode 100644 index 0000000..8509ded --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Model/MovieBox.swift @@ -0,0 +1,50 @@ +// +// MovieBox.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import Foundation + +struct MovieBox { + var searchKeyword = "" + var movies: [MovieInfo] = [] +} + +extension MovieBox { + struct MovieInfo: Codable, Identifiable { + let attributedTitle: String? + let link: String + let image: String + let subtitle: String + //let pubDate: Date + let director: String + let actor: String + let userRating: Double + let id: Int + + init(_ movieInfo: MovieFinder.Response.MovieInfo, id: Int) { + attributedTitle = movieInfo.title + link = movieInfo.link + image = movieInfo.image + subtitle = movieInfo.subtitle + //pubDate = movieInfo.pubDate + director = movieInfo.director + actor = movieInfo.actor + userRating = movieInfo.userRating + self.id = id + print(self) + } + } +} + +extension AttributedString { + init?(htmlString: String) { + let option: [NSAttributedString.DocumentReadingOptionKey: NSAttributedString.DocumentType] = [.documentType: .html] + guard let htmlData = htmlString.data(using: .utf16), + let nsStr = try? NSAttributedString(data: htmlData, options: option, documentAttributes: nil) + else { return nil } + self.init(nsStr.string) + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/Preview Content/Preview Assets.xcassets/Contents.json b/Week 7/Shimmy/naver-movie/naver-movie/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/View/ContentView.swift b/Week 7/Shimmy/naver-movie/naver-movie/View/ContentView.swift new file mode 100644 index 0000000..c0da6d1 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/View/ContentView.swift @@ -0,0 +1,35 @@ +// +// ContentView.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +struct ContentView: View { + @ObservedObject var viewModel: MovieFinder + + var body: some View { + NavigationView { + ZStack { + ScrollView { + SearchView(viewModel: viewModel) + MovieList(viewModel: viewModel) + } + .foregroundColor(.black) + .navigationTitle("네이버 영화 검색") + if viewModel.fetchingStatus == .fetching { + ProgressView() + .scaleEffect(1.5) + } + } + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(viewModel: MovieFinder()) + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/View/MovieItem.swift b/Week 7/Shimmy/naver-movie/naver-movie/View/MovieItem.swift new file mode 100644 index 0000000..011509f --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/View/MovieItem.swift @@ -0,0 +1,38 @@ +// +// MovieItem.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +struct MovieItem: View { + var movieInfo: MovieBox.MovieInfo + var body: some View { + VStack(alignment: .center) { + Group { + AsyncImage(url: URL(string: movieInfo.image)) { image in + image + .ImageModifier() + } placeholder: { + Image(systemName: "movieclapper") + .IconModifier() + } + .padding(20) + Text(movieInfo.attributedTitle!) + .padding(EdgeInsets(top: 0, leading: 10, bottom: 50, trailing: 10)) + } + } + } +} + +struct MovieItem_Previews: PreviewProvider { + static var movieInfo = MovieBox.MovieInfo(MovieFinder.Response.MovieInfo( + title: "그대들은 어떻게 살 것인가", link: "https://search.naver.com/search.naver?where=nexearch&sm=tab_etc&pkid=68&os=32034616&qvt=0&query=%EA%B7%B8%EB%8C%80%EB%93%A4%EC%9D%80%20%EC%96%B4%EB%96%BB%EA%B2%8C%20%EC%82%B4%20%EA%B2%83%EC%9D%B8%EA%B0%80", image: "https://search.pstatic.net/common?type=o&size=176x264&quality=85&direct=true&src=https%3A%2F%2Fs.pstatic.net%2Fmovie.phinf%2F20231026_9%2F16983044100188P1Ff_JPEG%2Fmovie_image.jpg%3Ftype%3Dw640_2", subtitle: "The Boy and the Heron", director: "미야자키 하야오", actor: "아이묭", userRating: 6.91 + ), id: 1) + + static var previews: some View { + MovieItem(movieInfo: movieInfo) + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/View/MovieList.swift b/Week 7/Shimmy/naver-movie/naver-movie/View/MovieList.swift new file mode 100644 index 0000000..cf8ade2 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/View/MovieList.swift @@ -0,0 +1,26 @@ +// +// MovieList.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +struct MovieList: View { + @ObservedObject var viewModel: MovieFinder + var colums: [GridItem] = Array(repeating: .init(.flexible()), count: 2) + + var body: some View { + LazyVGrid(columns: colums) { + ForEach(viewModel.model.movies) { + movieInfo in + NavigationLink { + MovieItem(movieInfo: movieInfo) + } label: { + MovieItem(movieInfo: movieInfo) + } + } + } + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/View/SearchView.swift b/Week 7/Shimmy/naver-movie/naver-movie/View/SearchView.swift new file mode 100644 index 0000000..521e6fb --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/View/SearchView.swift @@ -0,0 +1,33 @@ +// +// SearchView.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +struct SearchView: View { + @ObservedObject var viewModel: MovieFinder; + var body: some View { + ZStack { + Color.gray.opacity(0.1) + .padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)) + HStack { + TextField(text: $viewModel.model.searchKeyword, label: { Text("검색어를 입력하세요.") }) + Button(action: { viewModel.fetchMovieList()}) { + Text("검색") + } + .buttonStyle(.bordered) + } + .padding() + } + } +} + +struct SearchView_Previews: PreviewProvider { + static var previews: some View { + SearchView(viewModel: MovieFinder()) + } +} + diff --git a/Week 7/Shimmy/naver-movie/naver-movie/ViewModel/MovieFinder.swift b/Week 7/Shimmy/naver-movie/naver-movie/ViewModel/MovieFinder.swift new file mode 100644 index 0000000..07148f4 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/ViewModel/MovieFinder.swift @@ -0,0 +1,102 @@ +// +// MovieFinder.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +fileprivate enum NaverOpenAPI { + static let clientID = "VqtHIaKV56Yq0bbtzKXe" + static let clientSecret = "3CDDCRzxdH" + + static let scheme = "https" + static let host = "openapi.naver.com" + + enum Path: String { + case movie = "/v1/search/movie.json" + } +} + +class MovieFinder: ObservableObject { + @Published var model = MovieBox() + @Published var fetchingStatus = FetchStatus.idle + + func fetchMovieList() { + fetchingStatus = .fetching + + // urlComponents + var urlComponents = URLComponents() + urlComponents.scheme = NaverOpenAPI.scheme + urlComponents.host = NaverOpenAPI.host + urlComponents.path = NaverOpenAPI.Path.movie.rawValue + urlComponents.queryItems = [URLQueryItem(name: "query", value: model.searchKeyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed))] + + // 검색 쿼리를 UTF-8로 인코딩하여 설정 +// if let encodedQuery = model.searchKeyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { +// urlComponents.queryItems = [URLQueryItem(name: "query", value: encodedQuery)] +// } + + // urlComponents -> url + guard let url = urlComponents.url else { return } + + // request (header) + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = "GET" // method + urlRequest.addValue(NaverOpenAPI.clientID, forHTTPHeaderField: "X-Naver-Client-Id") // value, key + urlRequest.addValue(NaverOpenAPI.clientSecret, forHTTPHeaderField: "X-Naver-Client-Secret") + + // 비동기로 수행할 작업: task + let task = URLSession.shared.dataTask(with: urlRequest) {data, response, error in + // data, response, error 받아와서 처리할 부분 + guard error == nil, + let httpURLResponse = response as?HTTPURLResponse, + (200 ... 299).contains(httpURLResponse.statusCode), + let data = data, + let parsedData = try? JSONDecoder().decode(Response.self, from: data) + // 받아온 data(101011.....)를 우리가 볼수있게 parsing + else { return } + + // 비동기 함수를 main thread 에서 실행 + // mainthread에서 값이 바뀌어야 multithread 에서 갱신가능 + DispatchQueue.main.async { [weak self] in + // 서로 참조할 때, 다른 한 쪽에서 참조를 끊으면 자동으로 할당 해제 + // weak self 에서는 self? 사용 + // 받아온 JSON을 image로 parsing하고 ImageBox.ImageInfo에 넣어줌 + self?.model.movies = parsedData.items.indices.map { + MovieBox.MovieInfo(parsedData.items[$0], id: parsedData.start + $0) + } + // fetchingStatus를 idle로 + self?.fetchingStatus = .idle + } + } + task.resume() // mainthread로 넘길래 + } +} + +extension MovieFinder { + struct Response: Codable { + let lastBuildDate: String + let total: Int + let start: Int + let display: Int + let items: [MovieInfo] + + struct MovieInfo: Codable { + let title: String + let link: String + let image: String + let subtitle: String + //let pubDate: Date + let director: String + let actor: String + let userRating: Double + } + } + + enum FetchStatus { + case idle + case fetching + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movie/naver_movieApp.swift b/Week 7/Shimmy/naver-movie/naver-movie/naver_movieApp.swift new file mode 100644 index 0000000..9a12f6b --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movie/naver_movieApp.swift @@ -0,0 +1,17 @@ +// +// naver_movieApp.swift +// naver-movie +// +// Created by Seungbo Shim on 2023/11/21. +// + +import SwiftUI + +@main +struct naver_movieApp: App { + var body: some Scene { + WindowGroup { + ContentView(viewModel: MovieFinder()) + } + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movieTests/naver_movieTests.swift b/Week 7/Shimmy/naver-movie/naver-movieTests/naver_movieTests.swift new file mode 100644 index 0000000..0f37284 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movieTests/naver_movieTests.swift @@ -0,0 +1,36 @@ +// +// naver_movieTests.swift +// naver-movieTests +// +// Created by Seungbo Shim on 2023/11/21. +// + +import XCTest +@testable import naver_movie + +final class naver_movieTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITests.swift b/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITests.swift new file mode 100644 index 0000000..2f2d4b7 --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITests.swift @@ -0,0 +1,41 @@ +// +// naver_movieUITests.swift +// naver-movieUITests +// +// Created by Seungbo Shim on 2023/11/21. +// + +import XCTest + +final class naver_movieUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITestsLaunchTests.swift b/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITestsLaunchTests.swift new file mode 100644 index 0000000..061c07c --- /dev/null +++ b/Week 7/Shimmy/naver-movie/naver-movieUITests/naver_movieUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// naver_movieUITestsLaunchTests.swift +// naver-movieUITests +// +// Created by Seungbo Shim on 2023/11/21. +// + +import XCTest + +final class naver_movieUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +}