DADAHAE's Log
비싼 장난감 가지고 노는 중 (❁´▽`❁)*✲゚*
[TIL] 22-10-18: SwiftUI 기초(개요, Xcode, CustomView)
22-10-18 화요일
6주차

 

 

 

 

 

 

🦢 SwiftUI

들어가즈아ㅏㅏ

 

 

 

 

✔️ SwiftUI 개요

SwiftUI는 데이터 주도 방식이다.

MVVM이니 MVC니 하는 건 각 회사나 개인이 선택한 것이고, 애플에서 공식적으로 밝힌 SwiftUI의 디자인 패턴은 아니다. 애플은 SwiftUI가 MVVM 패턴을 채택하고 있다고 공식적으로 밝힌 적이 없다.

 

1️⃣ SwiftUI의 선언적 구문

명령형 구문과 선언형 구문

 

📌 명령형 구문

 

1.  강남역 2호선에서 CU 교대역점 앞 횡단보도까지 3개의 횡단보도를 지나 약 1.02Km 이동

2.  횡단보도를 이용해 베스킨라빈스 교대역 방면으로 횡단

3.  스타벅스 교대역점 앞 횡단보도까지 1개의 횡단보도를 지나 약 345m 이동

4.  횡단보도를 이용하여 롯데 하이마트 교대역점 방면으로 횡단

5.  교대역까지 약 44m 이동

  • ‘어떻게(How) 가는가’ 중심으로 작성된다.

 

📌 선언형 구문

1.  강남역 교대역까지 도보로 이동

 

중간에 있는 것들은 ‘알아서’ 해준다는 거다. 일일이 적어야 했던 부분을 미리 작성해놔서 간단하다. 필요하면 디테일하게 수정할 수도 있다.

  • ‘무엇을(What) 하는가’ 중심으로 작성된다.

 

Decorative syntax of SwiftUI

선언적 구문
  • Interface Builder와 같은 별도 레이아웃 설계 도구나, XIB, Storyboard에 의존x
    • 화면 구성하는 컴포넌트의 레이아웃 모양, 세부 사항 설계에 대한 시간 절약
  • SwiftUI는 단순, 직관적인 구문으로 화면 설계
    • 레이아웃이 실제 구축되는 방식의 복잡함을 고민x
    • 사용자 인터페이스가 어떤 모양이어야 하는지 코드를 사용해 선언하는 방식으로 레이아웃 생성

 

  1. 레이아웃에 포함될 컴포넌트 선언
  2. 컴포넌트에 포함될 레이아웃 매니저 종류 명시
    • HStack, VStack, Form, List
  3. 속성을 설정하기 위한 수정자(modifier) 사용
    • 버튼의 텍스트, 레이블의 전면 색상 또는 탭 제스처 이벤트의 호출 메서드

 

  • 이름을 설정하지 않아도 불러올 수 있다. 이름짓기의 고통 경감..!
  • 이렇게 선언하면 모든 복잡한 세부 사항은 SwiftUI가 자동으로 처리한다.
    • 레이아웃 위치, 강제사항(constraint), 렌더링 방법 등…
  • SwiftUI 선언은 계층적으로 구조화되어 있다. HTML 태그와 유사하게 된다.

 

  • 작고 재사용 가능한 하위 View를 사용자 정의로 만들어 복잡한 View 구성 가능
  • Xcode의 Preview canvas → 코딩 순간마다 실시간 변경되는 레이아웃 확인 및 테스트 가능

 

 

 

2️⃣ Data Driven of SwiftUI

책의 내용보다 좀 더 자세하게 알려주셨다.

 

UIKit과 MVC 모델

Model-View-Controller

 

📌 MVC

  • 사용자 인터페이스, 데이터, 논리 제어를 구현하는데 사용되는 소프트웨어 디자인 패턴
  • ‘소프트웨어의 비즈니스 로직 ↔ 화면’ 구분하는데 중점
  • 관심사 분리 → 업무의 분리, 향상된 관리
  • Model : 데이터와 비즈니스 로직 관리
  • View : 레이아웃 화면 처리
  • Controller : 명령을 모델과 뷰 부분으로 라우팅

 

IBOutlet, @~ 이런것들이 나온 것도 디자인 패턴을 맞춰주기 위해서 나온 것이다.

계산기를 만들 때 계산 로직을 분리하자고 한 것도 이 때문이다. 화면 설계와 로직을 나누기 위해서!

 

 

MVVM의 유행

Model-View-ViewModel
  • VideModel
    • View의 추상화 계층
    • ViewModel은 View를 알지 못하고 Notify만 한다.
  • MVVM이 MVC에 비해 가지는 장점
    • ViewModel은 아예 View를 참조할 필요가 없이 그냥 자신이 할 일만 하면 된다.

 

RxSwift

https://github.com/ReactiveX/RxSwift

  • UIKit 개발에 사용한 데이터 바인딩 라이브러리
  • UI 이벤트 중심으로 클로저를 활용해 코드 단순화
  • 바인딩된 데이터를 변경하면 관련된 화면 요소는 자동으로 갱신
  • 과도기적인 라이브러리, 좋긴 했다! 잘쓰였다. 그러나 스유가 나오면서 대체 가능해졌다.

 

 

 

Data driven

Data driven

데이터 주도는 앱 데이터사용자 인터페이스 및 로직 사이의 관계를 본 것이다.

  • UIKit
    • 앱 안의 현재 데이터를 살펴보려면 관련 코드를 직접 작성해야 했다.
    • 데이터가 변경되면 그 변경이 화면에 나타나도록 하는 코드 직접 작성
    • 필요하면 주기적으로 데이터 변경을 확인해 반영하도록 코드 작성
  • SwiftUI
    • 앱의 데이터 모델과 사용자 인터페이스 컴포넌트, 그리고 기능을 제공하는 로직을 Binding하는 여러 방법으로 복잡도를 낮춰줌

 

  • 데이터 모델은 앱이 다른 부분에서 구독(subscribe)할 수 있는 데이터 변수를 게시(publish)하게 된다.
    • 개념상으로 그렇다는 거다!
  • 이러한 방법을 통해 데이터가 변경되었다는 사실을 모든 구독자에게 바로 알릴 수 있다.
  • 만약 사용자 인터페이스 컴포넌트와 데이터 모델 간에 바인딩이 된다면, 추가적인 코드를 작성하지 않아도 모든 데이터의 변경 사항을 SwiftUI가 사용자 인터페이스에 자동으로 반영할 것이다.

 

 

 

 


 

✔️ Xcode로 SwiftUI 시작하기

Apple Developer Documentation

 

Apple Developer Documentation

 

developer.apple.com

SwiftUI 이전에는 Mac, iPhone 어플 개발을 따로 했었다.

  • AppKit - Mac
  • UIKit - iPhone

SwiftUI는 따로 개발하지 않고 한번에 macOS, iOS 개발을 할 수 있다.

 

 

1️⃣ Xcode 시작하기

 

1.  이제 Xcode를 켜보자.

  • multiplaform으로 해도 된다.
    • 단, 여기로 들어가면 선택의 여지가 없이 무조건 Swift다.
    • SwiftUI 전용이기 때문이다!
  • 우리는 iphone 앱을 만들거니까 지금은 그냥 iOS로 프로젝트를 생성하자.

 

2. SwiftUI 프로젝트 생성

 

3.  이것저것 만져보자.

  • 기본 세팅에서 몇가지를 바꿔보았다. 재밌다 스유! 리액트했던게 생각난다…

 

4. [실습] 내 맘대로 화면을 설계해보자.

 

  • 팀원이름 뜨게 화면 구성해보기
  • 다하고 나면 회고조 안에서 둘러보기

 

 

Dynamic Type

  • font size가 정해져 있다. 왜일까? 왜 이렇게 정해놨을까?

  • 숫자로 박아두면 크기 조절이 불가하기 때문이라는데... 무슨말 일까?애플의 가이드라인에 따르면 이런 상황을 피해달라고 한다.
  • 그러므로 특별한 이유가 없다면,폰트 크기를 절대값으로 주기 보다는 그 가이드를 따라가도록 하자.
  • 만약 사용자가 휴대폰 설정에서 글씨 크기를 바꾼다면 어떻게 될까. 상황에 맞게 커지거나 줄어들어야 하는데, 개발 단계에서 절대값 크기를 심어두면 유연하게 크기 조절이 되지 않는다.
  • padding: 양 옆에 안닿게 간격을 줄 수 있다.
더보기

주의  VStack에는 뷰가 10개까지만 들어갈 수 있다!

 

10개 넘어서는 스크롤이 되어야 하는 대상인데.. VStack에는 스크롤이 없다. 이런 경우에는 List로 바꿔야 한다!

https://stackoverflow.com/questions/58397964/are-there-maximum-limits-to-vstack

 

 

 

 

2️⃣ Preview Canvas

  • Preview Canvas
    • View를 여러개 깔 수 있다.
    • 다국어, 다크모드, 가로모드 등의 화면을 한번에 볼 수도 있다.
  • Preview를 위한 코드도 따로 있다!
  • Preview 코드안에서 Group으로 묶고 뷰를 뿌리면 여러개의 프리뷰를 볼 수 있다.
  • 디바이스를 각각 다른걸로 분리할 수도 있다.
  • Preview 구조체는 오직 프리뷰를 위한 코드이다. 실제 빌드에서는 없어도 된다.

 

🚬 초기 SwiftUI에서 쓰던 파일들

SwiftUI 2.0 까지 SceneDelegate, AppDelegate 파일이 필요했다. 그래서 책에 보면 장황하게 설명 중인거다. 그때는 이 파일을 모르면 SwiftUI를 사용할 수 없었기 때문에..

지금은 해당 파일들을 사용하지 않는다! 잊어도 좋다.

현재 SwiftUI는 앱이름App.swift 으로 앱을 실행한다.

import SwiftUI

@main
struct HelloSwiftUIApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
  • WindowGroup
    • 동시에 여러 개를 띄울 때 여기다 넣으면.. 된다..!
    • 지금은 이 상태로 둘 것이다. 아직 건드리지 않을 것이다!!

 

 


 

 

✔️ Custom View 생성하기

1️⃣ 뷰 추가하기

아래의 코드로 화면에 생성될 뷰를 짜보았다. 현재 VStack에 8개의 Text 뷰가 들어있다.

  • 뷰?
    • SwiftUI에서 뷰는, View 프로토콜을 따르는 구조체로 선언된다.
    • 해당 프로토콜을 따르기 위해서 구조체는 body 프로퍼티를 가져야 한다.
    • body안에 뷰가 선언된다.
struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello World")
                .font(.largeTitle)
                .fontWeight(.ultraLight)
                .padding()
            Text("Dadahae's Log")
                .font(.subheadline)
                .italic()
                .foregroundColor(.gray)
                .padding()
            
            Text("홍길동")
            Text("뽀로로")
            Text("루피")
            Text("탄지로")
            Text("펭수")
            Text("라바")
        }
    }
}

 

 

 

 

 

2️⃣ 하위 뷰 만들기

애플을 최대한 뷰를 작고 가볍게 하라고 권장한다.

이렇게 뷰를 분리하면 재사용하기 쉽고 관리가 용이해진다.

struct ContentView: View {
    var body: some View {
        VStack {
            TitleView()
            NamesView()
        }
    }
}

struct NamesView: View {
    var body: some View{
        VStack {
            Text("홍길동")
            Text("뽀로로")
            Text("루피")
            Text("탄지로")
            Text("펭수")
            Text("라바")
        }
    }
}

struct TitleView: View {
    var body: some View {
        Text("Hello World")
            .font(.largeTitle)
            .fontWeight(.ultraLight)
            .padding()
        Text("Dadahae's Log")
            .font(.subheadline)
            .italic()
            .foregroundColor(.gray)
            .padding()
    }
}

위와 같이 구성해도 첫 화면과 동일하게 나온다.

여기까지가 1차적인 구조화이다.

  • view들을 VStack에 넣고 VStack에 색이나 속성을 넣으면, 그 내부의 뷰에도 모두 적용된다.

명령형으로 변수에 클로저를 넣어서 뷰를 호출할 수도 있다.

근데 굳이 그렇게 하지 말자. 왜 그러냐구우우 알아보기 힘들고, 수정하기도 쉽지않다구! 하지마하지마

 

 

3️⃣ 이미지 넣기

Image

Image를 넣을 수 있다.

 

SF-Symbols, System Name

  • 근데 속성에 systemName이라는게 있는데.. 그게 뭘까?
  • SF Symbols 을 보자. 애플에서 직접 만든 앱이다.
    • 각각의 이미지에 맞는 역할이나 디자인 가이드가 다 나와있다.
    • SF Symbols
  • home brew로 SF Symbols을 설치할 수 있다.
brew install sf-symbols

 

  • sf-symbols 앱에 들어가면 사진의 이름이 있는데, 그게 systemName이다.
    • 애플에서 제공하는 이미지 이름을 systemName에 넣으면 바로 사용할 수 있다.
    • 그림 위에 우클릭을 하면 ‘이름 복사’가 있다. 이걸로 쉽게 사용할 수 있다!

 

 

 

써먹어보자!

struct EmojiView: View {
    var body: some View {
        HStack {
            Image(systemName: "circle.grid.cross.fill")
            Image(systemName: "house.fill")
            Image(systemName: "gamecontroller.fill")
            Image(systemName: "dpad.fill")
            Image(systemName: "logo.playstation")
            Image(systemName: "logo.xbox")
        }.foregroundColor(.gray)
    }
}

 

 

 

 

 

이미지 뷰 변경하기

이미지 크기를 조정할 수도 있다.

struct EmojiView: View {
    var body: some View {
        HStack {
            Image(systemName: "house.fill")
            Image(systemName: "gamecontroller.fill")
            Image(systemName: "dpad.fill")
            Image(systemName: "logo.playstation")
            Image(systemName: "logo.xbox")
                .resizable()
                .aspectRatio(contentMode: .fit)
        }.foregroundColor(.gray)
            
    }
}

화면에 꽉 차게 xbox 이미지가 커졌다. 쿠쿡.

 

 

 

4️⃣ 수정자 순서

순서에 따라 결과가 바뀐다!! 순서대로 수정자가 적용된다.

Text("안녕하세요")
	.padding()
	.border(Color.black)

Text("안녕하세요")
	.border(Color.black)
	.padding()

swiftUI에는 margin이 없다. padding을 밖에 두느냐, 안에 두느냐의 차이이다.

 

 

5️⃣ Custom Modifier

뷰에 자주 적용되는 수정자들을 커스텀 수정자로 묶어서 필요할 때마다 참조할 수 있다.

struct StandardName: ViewModifier { // protocol
	func body(content: Content) -> some View {
		// return
		content
			.foregroundColor(Color.purple)
			.font(.body)
	}
}

필요한 곳에서 modifier() 메서드를 통해 커스텀 수정자를 전달하여 적용한다.

Text("안녕하세요")
	.modifier(StandardName())

 

 

 

6️⃣ 아주 간단한 이벤트 처리하기

사용자가 화면을 조작할 때 발생하는 이벤트 처리를 할 때 사용된다.

 

Button

Button("한번 눌러보시겠습니까?") {
	print("짜잔-! 배고파요~")
}

후행 클로저다.

 

Button("한번 눌러보시겠습니까?" action: {
	print("짜잔-! 배고파요~")
})

사실 이거랑 같다.

 

 

Button 뷰 생성 방법 4가지

패턴이 4가지가 있다.

Button("Hello", action: {
    print("전기차를 충전해주세요")
})

Button("Hello") {
    print("전기차를 충전해주세요")
}

// 이미지가 있을 때 버튼으로 만들기
Button(action: {
    print("전기차를 충전해주세요")
}, label: {
    Image(systemName: "bolt.car")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .padding()
})

Button(action: {
    print("전기차를 충전해주세요")
}) {
    Image(systemName: "bolt.car")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .padding()
}

모두 버튼을 만드는 패턴이다! 기억하고 수정할 수 있어야 한다.

  • 클로저
  • 후행클로저
  Comments,     Trackbacks