Lecture 1: Getting started with SwiftUI


iOS 관련 유명한 강의인 스탠포드 대학의 cs193p Developing Applicaitons for iOS 강의다.

해당 강의는 누구나 스탠포드 유튜브 채널에서 시청이 가능하다.

기존의 2017버전 강의에서는 실시간 강의영상이었다면 이번에는 코로나 팬데믹으로 인한 온라인 강의 영상이므로 교수님이 작성하는 코드나 제공되는 시청자료들을 더 좋은 환경에서 볼 수 있다.

 

기존 2017버전에서는 UIKit 프레임워크를 이용한 MVC 패턴의 앱개발이 주 내용이었다면, 2021 버전에서는 이제 현업에서 적극적으로 사용되는 MVVM 패턴과 이에 맞게 애플에서 개발한 swiftUI 프레임워크를 이용한 앱개발이 주 내용이 될 것이다.

 

지난번 2017강의를 들으면서 내용들을 전부 옮겨 적으려 해서 학습에도, 기록에도 힘이 들었기에 이번에는 강의 내용을 모두 옮겨적는다기보단 내가 보았을 때 흥미로운 내용 위주로, 하나의 강의 내에 여러 흐름으로 나누어서 작성하는 것을 통해 빠르게 학습하고 빠르게 기록하는 것에 초점을 두고 글을 적으려고 한다.


이번 첫강의에서는 swiftUI로 작성된 프로젝트를 전반적으로 훑어보면서 swiftUI 프레임워크의 구성을 간략하게 알아보았다.

 

swiftUI project File Hierarchy

가장 먼저 swiftUI로 프로젝트를 생성하게 되면 다음과 같은 파일이 자동으로 생성된다.

(프로젝트명)App.swift 파일과 ContetnView.swift 파일이 이번 swiftUI 프레임워크의 주요 파일이다.

 

import SwiftUI

@main
struct MemoriesApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}​

 

App.swift 파일은 다음과 같이 작성이 되어있으며, 주의깊게 봐야할 부분이 @main 어노테이션이다.

해당 어노테이션은 UIKit으로 생성한 프로젝트에서 AppDelegate 파일에 있던 어노테이션으로 앱의 진입지점(entry point)를 나타낸다. 즉 swiftUI의 진입지점(entry point)은 App.swift 파일이며 해당 App 객체의 생성으로 앱의 실행으로 이어진다는 얘기이다. 

 

다음으로 주목해야 할 부분은 App 객체의 body 부분에 할당된 ContentView() 객체이다.

ContentView() 객체를 생성하는 것을 볼 수 있고, 해당 객체는 ContentView.swift 파일에 선언되있는 것을 확인할 수 있다.

 

 

ContentView.swift 파일을 살펴보게면 xcode에서 3개의 영역를 제공한다.

  1. 코드를 작성하는 textEditor 영역
  2. 앱의 UI가 어떻게 보이게 될지 미리 보여주는 preView 영역
  3. 사용자에게 보여질 UI를 구성하는 각 개별의 View를 설정 할 수 있는 inspector 영역

흥미로운 점은 이 3개의 영역이 실시간으로 연동이 된다는 점이다. 강의에서는 sync 되어있다는 표현을 사용했는데 즉, 사용자가 마주하는 Text, Image와 같은 UI뷰의 설정을 3개의 영역 모두에서 접근이 가능하며 해당 수정사항이 실시간으로 일어난다는 것.

 

 

기존의 UIKit 프레임워크의 XML 형태와 달리 xcode가 코드로 짜여진 UI를 실시간으로 처리하는 것처럼 와닿았다. 

 

해당 부분이 반가운 점은 UIKit을 사용할 때에는 기존의 UI를 IBAction, IBOutlet을 통해 연결했는데, 가끔씩 라인을 이동한다던지, 이름을 변경하게 되었을 때 싱크가 끊어지는 경우가 종종 생긴다는 점이었다.

 

애플도 해당 문제점을 정확히 파악하고 있었을 것이고 확실한 것은 아니지만 SwiftUI를 통해 해당 문제점을 해결하기 위래 노력을 많이 한 것으로 느껴졌다.

 

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello!")
                .padding(.all)
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

ContentView.swift의 내용을 살펴보면 View 구조체와 PreViewProvider 구조체가 보인다.

PreViewProvider의 경우 이름에서 알 수 있듯이 UI의 실시간 반영을 위한 preView를 위한 코드부분이다. 

 

결국 개발자가 UI를 구성하는 코드를 작성하는 곳은 ContentView라는 이름의 View 구조체인 것이다.

 

참고 영상

https://www.youtube.com/watch?v=bqu6BquVi2M&ab_channel=Stanford 

'iOS > Stanford' 카테고리의 다른 글

[Lecture 2] Learning more about SwiftUI  (0) 2023.07.05
[Lecture 1] Getting Started with SwiftUI - 2  (0) 2023.06.22
day06_multiTouch  (0) 2022.03.26
day05_view  (0) 2021.12.30
day04_swift_part2  (0) 2021.10.25

https://www.acmicpc.net/problem/16926

 

16926번: 배열 돌리기 1

크기가 N×M인 배열이 있을 때, 배열을 돌려보려고 한다. 배열은 다음과 같이 반시계 방향으로 돌려야 한다. A[1][1] ← A[1][2] ← A[1][3] ← A[1][4] ← A[1][5] ↓ ↑ A[2][1] A[2][2] ← A[2][3] ← A[2][4] A[2][5]

www.acmicpc.net

배열에 관련된 구현문제다.

 

풀이

일반적인 풀이 방법은 문제에 주어진 대로 2차원 배열을 탐색하여 반시계방향으로 요소를 옮겨주면 된다.

각 그룹이 R번 회전해야 하므로 이 방법으로 구현하게 된다면 O(N*M*R)에 해결할 수 있다.

하지만 swift의 경우 다른 언어에 비해 느려서 해당 방법으로는 시간초과를 받는다.

 

R번 회전한다는 것은 결국 원점으로 돌아올 수 있다는 것이고 이는 곧 회전 수를 줄일 수 있는 요인이라 생각했다. 문제는 회전해야 하는 그룹의 크기가 전부 달라서 이를 어떻게 해결할까 고민하였다.

 

https://www.acmicpc.net/board/view/86800

 

글 읽기 - [Swift] 시간초과의 해결방법이 있을까요?

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

질문 탭을 보니 2차원 배열을 1차원으로 접근해 보라는 힌트를 발견. 해당 힌트를 통해 문제를 해결할 수 있었다.

입력예제 1을 기준으로 설명하면 다음과 같다.

 

회전시켜야 할 그룹을 파악한 후 1차원 배열로 생성.

 

(R % 각 배열의 크기) 부분에서 분리하여 순서를 바꾼 배열을 만든다.

 

이어서 다시 회전시켜야 할 그룹의 자리에 1차원 배열의 요소를 담아주면 O(2*N*M)만에 결과를 얻을 수 있다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[23743] 방탈출  (0) 2023.10.01
[1833] 고속도로 설계하기  (0) 2023.09.30
[3015] 오아시스 재결합  (0) 2023.06.12
[14890] 경사로  (0) 2023.06.03
[14719] 빗물  (0) 2023.06.02

https://www.acmicpc.net/problem/3015

 

3015번: 오아시스 재결합

첫째 줄에 줄에서 기다리고 있는 사람의 수 N이 주어진다. (1 ≤ N ≤ 500,000) 둘째 줄부터 N개의 줄에는 각 사람의 키가 나노미터 단위로 주어진다. 모든 사람의 키는 231 나노미터 보다 작다. 사람

www.acmicpc.net

스택을 활용한 문제다. 

 

풀이

입력되는 모든 키를 비교하게 되면 당연하게도 시간초과가 일어난다. 풀이를 찾아보니 스택을 사용한다면 O(N) 만에 탐색이 가능했다.

우선 주어지는 현재 키를 순서대로 스택에 담아낼 것이다. 

(키 높이, 인원)를 담는 비열을 생성하여 현재 주어진 키와 스택의 top과 비교하여 쌍을 계산하면 된다. 

 

스택이 비어있다면 당연하게도( 현재 키 높이: 인원(1) )을 담고 다음 정보를 입력받는다.

 

크게 두 가지 경우로 나뉜다.

1. top의 키높이가 현재 키높이와 같은 경우  -> top의 인원수만큼 쌍으로 추가

 

2. top의 키높이보다 현재 키높이가 큰 경우 -> 인접한 경우이므로 쌍으로 추가

 

위 두 가지 경우는 top의 키높이가 현재 키높이보다 클 때까지 혹은 스택이 빈상태가 될 때까지 pop 연산을 수행한다.

 

pop 연산을 수행한 이후에 스택이 비어있지 않다면 top에 해당하는 키와 현재 키와는 인접한 경우이므로 쌍으로 추가할 수 있다.

 

키높이가 같은 경우를 거쳤다면 (현재 키높이 : 추가했던 인원수+1) 형태로 스택에 추가.

그렇지 않았다면 (현재 키높이 : 인원수 1) 형태로 스택에 추가하면 된다.

 

즉 스택이 top으로 갈수록 내림차순을 유지하면 서로 바라볼 수 있는 쌍을 알아낼 수 있다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[1833] 고속도로 설계하기  (0) 2023.09.30
[16926] 배열 돌리기 1  (0) 2023.06.15
[14890] 경사로  (0) 2023.06.03
[14719] 빗물  (0) 2023.06.02
[5719] 거의 최단 경로  (0) 2023.04.30

https://www.acmicpc.net/problem/14890

 

14890번: 경사로

첫째 줄에 N (2 ≤ N ≤ 100)과 L (1 ≤ L ≤ N)이 주어진다. 둘째 줄부터 N개의 줄에 지도가 주어진다. 각 칸의 높이는 10보다 작거나 같은 자연수이다.

www.acmicpc.net

구현문제다. 입력되는 경사로의 정보를 어떻게 탐색할지 고민했다.

 

풀이

입력되는 경사로의 정보를 ( 높이(숫자), 길이(연속되는 갯수) ) 형태의 튜플을 담는 배열로 정리하여 탐색을 수행한다.

다음과 같은 조건을 확인하여 갯수를 추가하였다.

  • 현재 위치와 다음 위치와의 높이 차이가 1 이어야 한다.
  • 다음 경사로의 높이가 더 높은 경우라면 현재 위치의 길이를 확인한다.
  • 현재 경사로의 높이가 더 높은 경우라면 다음 위치의 길이를 확인한다.
  • 만약 경사로를 놓아야 하는 위치에 처음 경사로가 설치되는 경우라면 해당 위치의 길이가 L 이상인지 확인한다.
  • 만약 경사로를 놓아야 하는 위치에 경사로가 이미 설치된 경우라면 해당 위치의 길이가 2*L 이상인지 확인한다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[16926] 배열 돌리기 1  (0) 2023.06.15
[3015] 오아시스 재결합  (0) 2023.06.12
[14719] 빗물  (0) 2023.06.02
[5719] 거의 최단 경로  (0) 2023.04.30
[1711] 직각삼각형  (0) 2023.04.19

https://www.acmicpc.net/problem/14719

 

14719번: 빗물

첫 번째 줄에는 2차원 세계의 세로 길이 H과 2차원 세계의 가로 길이 W가 주어진다. (1 ≤ H, W ≤ 500) 두 번째 줄에는 블록이 쌓인 높이를 의미하는 0이상 H이하의 정수가 2차원 세계의 맨 왼쪽 위치

www.acmicpc.net

구현 문제다. 특정 알고리즘보단 문제에 어떻게 접근해야 할지 고민해 보게 되는 유형이다.

 

풀이

입력된 벽의 높이를 좌에서 우로 탐색한다. (탐색값 중 가장 높은 높이 - 현재높이) 값을 임시 변수에 누적. 가장 높은 높이가 갱신되면 여태 구한 임시 변수의 값을 최종 정답에 반영. 새로 바뀐 최대 높이를 기준으로 다시 빗물을 계산한다. 

 

탐색이 끝나고 임시 값을 최종 정답에 반영하지 못했다면, W-1번부터 마지막으로 갱신된 가장 높은 높이의 벽까지 역탐색하며 빗물을 계산하면 된다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[3015] 오아시스 재결합  (0) 2023.06.12
[14890] 경사로  (0) 2023.06.03
[5719] 거의 최단 경로  (0) 2023.04.30
[1711] 직각삼각형  (0) 2023.04.19
[16169] 수행시간  (0) 2023.04.11

https://www.acmicpc.net/problem/5719

 

5719번: 거의 최단 경로

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 장소의 수 N (2 ≤ N ≤ 500)과 도로의 수 M (1 ≤ M ≤ 104)가 주어진다. 장소는 0부터 N-1번까지 번호가 매겨져 있

www.acmicpc.net

다익스트라 알고리즘 문제다. 런타임 에러를 만나 고생했다.

 

풀이

접근방법에 대해 간략하게 이야기하면 다음 흐름과 같다.

  1. 다익스트라 알고리즘을 통해 시작노드 S부터 각 노드까지의 최단거리를 구해놓은 뒤,
  2. 도착노드 D부터 시작노드 S까지 그래프 탐색을 통해 해당 경로가 최단경로임이 확인되면 해당 경로를 삭제
    (여기서 1에서 구한 최단경로 비용이 사용된다.)
  3. 최단 경로가 삭제된 상태의 그래프를 다익스트라 알고리즘을 수행하여 거의 최단 경로를 구해내면 된다.

2번 과정이 이번 문제의 핵심이라고 생각한다. 우선 그래프가 방향이 정해져 있는 지향그래프이기에, 그래프 탐색을 위해 간선정보를 입력받을 때, 역탐색을 위한 rev배열을 생성하여 따로 저장해 놓는다. 배열의 경우 [[(node:Int, cost:Int)]]() 형태의 튜플 배열로 저장하여 다음 노드의 번호와 비용을 저장.

 

D -> S 역탐색을 수행할 때, 해당 경로가 최단경로임을 확인하는 방법은 다음과 같다. 여기서 1번 과정에서 구한 최단비용이 사용된다.

(D에서 해당 노드까지의 탐색비용 + S에서 해당 노드까지의 최단 비용) == (S에서 D까지의 최단비용)

 

여기서 최단경로임이 확인되면, 해당 경로를 삭제한 뒤 해당노드에서 다음 탐색을 이어가면 된다. 경로 삭제를 표현하는 방법은 많겠지만, 본인의 경우 해당 노드의 인덱스를 -1로 수정하여 삭제된 경로임을 표시하였다.

 

이후 수정된 그래프를 기준으로 다익스트라 알고리즘을 수행하면 거의 최단 경로를 구할 수 있다.

 

주의할 점

8%에서 런타임 에러를 뿜어대길래 반례를 찾느라 헤맸다. 다익스트라 알고리즘을 수행할 때, INF를 생각 없이 Int.max로 설정하여 오버플로우가 발생했었다. 즉 1번에서 얻은 최단 경로값에 Int.max가 있었고, 이걸 2번 과정에서 최단경로인지 확인할 때, D에서 해당 노드의 탐색비용 + Int.max의 연산이 수행되니 오버플로우가 발생했던 것. 따라서 (N의 최댓값 500 * 비용의 최댓값 1,000) 여기에 조금 더 여유를 둬서 INF를 5,000,000으로 설정하여 다익스트라를 수행하였더니 통과하였다. 백준의 경우 스위프트는 어떤 오류던지 무조건 런타임 에러를 반환하기에 원인을 찾는데 조금 고생했다. 그래도 이렇게 힘들게 문제를 해결하면 성취감이 몰려온다. 이런 매력에 PS를 하는 게 아닌가 싶다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[14890] 경사로  (0) 2023.06.03
[14719] 빗물  (0) 2023.06.02
[1711] 직각삼각형  (0) 2023.04.19
[16169] 수행시간  (0) 2023.04.11
[24526] 전화 돌리기  (0) 2023.04.05

https://www.acmicpc.net/problem/1711

 

1711번: 직각삼각형

첫째 줄에 점의 개수 N(3 ≤ N ≤ 1,500)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 점의 x좌표와 y좌표가 빈 칸을 사이에 두고 주어진다. 좌표값은 절댓값이 1,000,000,000을 넘지 않는 정수이며, 주

www.acmicpc.net

브루트포스로 접근한 문제다.

 

풀이

들어오는 좌표 중 3개를 선택하는 조합 반복문을 생성. 피타고라스의 정리를 사용해 직각 삼각형인지 판별하면 된다.

단 해당 문제에서 흥미로웠던 점은 세 점 A, B, C의 조합에서 나온 3가지의 선분(AB, BC, AC) 중 가장 긴 선분을 찾아내는 부분에서 시간초과가 났다.

 

처음에는 단순히 3개의 선분의 길이를 배열에 담아낸 뒤 내림차순 정렬하여 (0번 원소 == 1번 원소 + 2번 원소) 조건문으로 판별하였으나, 시간초과가 발생. 

func isRightTriangle(a:(Int,Int),b:(Int,Int),c:(Int,Int)) -> Bool{
    let AB = ((a.0-b.0)*(a.0-b.0)) + ((a.1-b.1)*(a.1-b.1))
    let AC = ((a.0-c.0)*(a.0-c.0)) + ((a.1-c.1)*(a.1-c.1))
    let BC = ((b.0-c.0)*(b.0-c.0)) + ((b.1-c.1)*(b.1-c.1))
    
    let tmp = [AB,AC,BC].sorted(by: >)
    return tmp[0]==tmp[1]+tmp[2] ? true:false
}

 

이때부터 조금 헤맸다. 시간복잡도를 계산해 보면 입력좌표는 최대 1500개가 입력. 1500C3 = 561,375,500 여기서 각 조합마다 정렬이 이루어지는데, Swift 배열의 정렬 함수 시간복잡도는 0(N)이므로 대략 16억 번의 수행이 이루어진다. 보통 알고리즘 문제를 풀 때 러프하게 1초당 1억 번의 연산으로 계산하게 되는데, 5초의 시간은 턱없이 부족한 시간인 것이다.

 

그러다 문득 배열을 생성하고 정렬하는 것보단 비트연산이 더욱 빠르겠다는 생각이 들어 아래와 같이 수정하였더니 통과하였다.

func isRightTriangle(a:(Int,Int),b:(Int,Int),c:(Int,Int)) -> Bool{
    let AB = ((a.0-b.0)*(a.0-b.0)) + ((a.1-b.1)*(a.1-b.1))
    let AC = ((a.0-c.0)*(a.0-c.0)) + ((a.1-c.1)*(a.1-c.1))
    let BC = ((b.0-c.0)*(b.0-c.0)) + ((b.1-c.1)*(b.1-c.1))

//  let tmp = [AB,AC,BC].sorted(by: >)
//  return tmp[0]==tmp[1]+tmp[2] ? true:false

    let res = (AB==AC+BC) || (AC == AB+BC) || (BC==AB+AC)
    return res ? true:false
}

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[14719] 빗물  (0) 2023.06.02
[5719] 거의 최단 경로  (0) 2023.04.30
[16169] 수행시간  (0) 2023.04.11
[24526] 전화 돌리기  (0) 2023.04.05
[14907] 프로젝트 스케줄링  (0) 2023.04.03

https://www.acmicpc.net/problem/16169

 

16169번: 수행 시간

첫 번째 줄에는 컴퓨터의 개수 n이 주어진다. (3 ≤ n ≤ 100) 두 번째 줄부터 n개의 줄에 걸쳐 1번부터 n번까지 각 컴퓨터의 계급과 동작 속도 t가 공백을 두고 주어진다. (1 ≤ t ≤ 100)

www.acmicpc.net

위상정렬로 접근한 문제다.

 

풀이

해당 문제의 컴퓨터는 자료를 전송할 다음 등급의 컴퓨터가 여러 대라면 동시에 자료를 전송하는 것으로 계산하면 된다. 즉 위상정렬을 통해 다음 등급의 컴퓨터를 탐색할 때, 간선의 비용이 최대 값인 경우만 찾아내면 된다.

 

입력되는 등급을 통해 위상정렬 순서를 구해 탐색을 수행한다. (다음 등급의 컴퓨터에게 자료를 전달하는 시간 + 다음 컴퓨터의 구동시간)의 최댓값을 저장할 ans 배열을 생성. 매 탐색마다 해당 배열을 최댓값으로 갱신하고 탐색이 종료된 후 ans 배열의 최댓값을 출력하면 정답이다.

 

예제를 기준으로 다음과 같이 수행된다.

가장 먼저 가장 낮은 등급의 컴퓨터인 1번과 7번 컴퓨터가 동작한다.

각자의 (동작시간 + 전송시간)의 값 중 가장 큰 값이 다음 등급인 6번 컴퓨터에게 전달되며,

해당 컴퓨터는 자신의 구동시간을 더해 ans배열에 갱신한다.

이후 탐색 큐에 6과 ans [6]의 값 36을 담아 다음 탐색을 수행한다.

2등급 컴퓨터인 6번 노드는 다음 등급의 컴퓨터인 2번과 3번 컴퓨터에 자료를 전송.

6번 컴퓨터의 대기시간 + 동작시간의 값 + 전송시간의 값이 전달된다.

이제 해당 값에 2번과 3번 컴퓨터의 구동시간을 더해 ans배열을 갱신한다.

각 노드로 진입하는 대기시간 + 구동시간 + 전송시간의 값 중 가장 큰 값을 입력받아 ans배열을 갱신해 나가면 된다.

구동시간에는 음수가 입력되지 않으므로 마지막 컴퓨터의 대기시간 + 구동시간 + 동작시간을 더해 출력하면 된다.

 

정답 코드

'Problem Solving > BOJ' 카테고리의 다른 글

[5719] 거의 최단 경로  (0) 2023.04.30
[1711] 직각삼각형  (0) 2023.04.19
[24526] 전화 돌리기  (0) 2023.04.05
[14907] 프로젝트 스케줄링  (0) 2023.04.03
[2637] 장난감 조립  (0) 2023.04.01

+ Recent posts