๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ“ฑ Mobile/iOS

iOS - UIKit vs SwiftUI ์— ๋Œ€ํ•ด์„œ (2)

UIkit์— ๋Œ€ํ•ด์„œ  →  2023.07.16 - [iOS] - UIKit vs SwiftUI ์— ๋Œ€ํ•ด์„œ - (1)

 

SwiftUI ๋ž€?

์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค(UI) ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, iOS, macOS, watchOS, tvOS ์•ฑ ๊ฐœ๋ฐœ์— ์‚ฌ์šฉ๋˜๋Š” ์ตœ์‹  UI ํˆดํ‚ท์ด๋‹ค.

 

Swift ์–ธ์–ด์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋ฉฐ, ์„ ์–ธ์  ๊ตฌ๋ฌธ์„ ํ™œ์šฉํ•˜์—ฌ UI๋ฅผ ๋น ๋ฅด๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ค.
 

UIKit์€ ViewController์™€ Storyboard๋ฅผ ์ค‘์‹ฌ์œผ๋กœ UI๋ฅผ ๊ฐœ๋ฐœํ•œ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•ด์„œ UI ์š”์†Œ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ฐฐ์น˜ํ•˜๊ณ  ์ฝ”๋“œ๋กœ ์ด๋ฅผ ์กฐ์ž‘ํ•œ๋‹ค. 

 

 

SwiftUI์˜ ์ฃผ์š” ํŠน์ง•๊ณผ ๊ธฐ๋Šฅ (์˜ˆ์‹œ ์ฝ”๋“œ)

 

 

์„ ์–ธ์  ๊ตฌ๋ฌธ

SwiftUI๋Š” ์„ ์–ธ์ ์ธ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ UI๋ฅผ ์„ค๊ณ„ํ•œ๋‹ค.

์ฝ”๋“œ๋กœ UI ์š”์†Œ๋“ค์„ ์„ค๋ช…ํ•˜๊ณ  ๊ตฌ์„ฑํ•˜๋Š” ๋Œ€์‹ , SwiftUI๋Š” ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์„ ์–ธํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

์ด๋กœ ์ธํ•ด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฝ๊ณ  ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. 

 

import SwiftUI

struct MyView: View {
    // body ํ”„๋กœํผํ‹ฐ๋Š” View๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ณ„์‚ฐ๋œ ํ”„๋กœํผํ‹ฐ๋กœ, ๋ทฐ์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜
    var body: some View {
        // VStack์€ ์„ธ๋กœ๋กœ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
        VStack {
            Text("Hello, SwiftUI!")		//ํ…์ŠคํŠธ ํ‘œ์‹œ
            Button("Tap Me!") {		//์‚ฌ์šฉ์ž์˜ ํƒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฒ„ํŠผ
                print("Button tapped!")		//๋ฒ„ํŠผ์„ ํƒญํ–ˆ์„ ๋•Œ ์‹คํ–‰๋˜๋Š” ํด๋กœ์ €
            }
        }
    }
}

// ๋ทฐ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ
import PlaygroundSupport
// Playground์—์„œ MyView์˜ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
PlaygroundPage.current.setLiveView(MyView())

 

 

 

์ž๋™ํ™”๋œ ๋ ˆ์ด์•„์›ƒ

SwiftUI๋Š” UI ์š”์†Œ๋“ค์˜ ๋ ˆ์ด์•„์›ƒ์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

๋ทฐ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ œ์•ฝ ์กฐ๊ฑด์„ ์ง์ ‘ ์ž‘์„ฑํ•  ํ•„์š” ์—†์ด, ์ž๋™์œผ๋กœ ์ตœ์ ์˜ ๋ ˆ์ด์•„์›ƒ์„ ๊ตฌ์„ฑํ•œ๋‹ค.

 

import SwiftUI

struct MyView: View {
    var body: some View {
        // HStack์€ ๊ฐ€๋กœ๋กœ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
        HStack(spacing: 20) {
            Image("icon")	// ์ด๋ฏธ์ง€ ํ‘œ์‹œ
                .resizable()
                .frame(width: 50, height: 50)
            // VStack์€ ์„ธ๋กœ๋กœ ๋ทฐ๋ฅผ ๋ฐฐ์น˜ํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
            VStack {
                Text("Title")		// ํ…์ŠคํŠธ ํ‘œ์‹œ
                    .font(.headline) // ๊ธ€์ž ํฌ๊ธฐ ์ง€์ •
                Text("Description")
                    .font(.subheadline) // ๊ธ€์ž ํฌ๊ธฐ ์ง€์ •
            }
        }
    }
}

 

 

 

ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์ง€์›

iOS, macOS, watchOS, tvOS ์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์—์„œ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜์—ฌ ์•ฑ์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค. 

์ข…๋ฅ˜๊ฐ€ ๋‹ค์–‘ํ•œ Apple ๊ธฐ๊ธฐ๋“ค์— ๋Œ€์‘ํ•˜๋Š” ์•ฑ์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. 

 

import SwiftUI

struct MyView: View {
    var body: some View {
        // #if os() ์กฐ๊ฑด๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋žซํผ ๋ณ„๋กœ ๋‹ค๋ฅธ ๋ทฐ๋ฅผ ํ‘œ์‹œ
        #if os(iOS)
        Text("iOS")
        #elseif os(macOS)
        Text("macOS")
        #elseif os(watchOS)
        Text("watchOS")
        #else
        Text("tvOS")
        #endif
    }
}

 

 

 

์‹ค์‹œ๊ฐ„ ํ”„๋ฆฌ๋ทฐ

์‹ค์‹œ๊ฐ„์œผ๋กœ UI๋ฅผ ํ”„๋ฆฌ๋ทฐํ•˜๊ณ  ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ฆ‰์‹œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. 

์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ๋งˆ๋‹ค UI ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•ด์„œ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ƒ์‚ฐ์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค.

 

import SwiftUI

struct MyView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
    }
}

#if DEBUG
import SwiftUI
struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView()	    // MyView์˜ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ ์ฐฝ์— ํ‘œ์‹œ
    }
}
#endif

 

 

 

๋ชจ๋“ˆํ™” ๋ฐ ์žฌ์‚ฌ์šฉ์„ฑ

๋ชจ๋“ˆํ™”์™€ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ฐ•์กฐํ•œ๋‹ค. UI ์š”์†Œ๋“ค์„ ์ปค์Šคํ…€ ๋ทฐ๋กœ ์ •์˜ํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ์•ฑ์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•œ๋‹ค. 

 

import SwiftUI

struct CustomButton: View {
    var title: String // ๋ฒ„ํŠผ์— ํ‘œ์‹œ๋  ํ…์ŠคํŠธ
    var action: () -> Void // ๋ฒ„ํŠผ์ด ํƒญ๋˜์—ˆ์„ ๋•Œ ์‹คํ–‰๋  ํด๋กœ์ €
    
    var body: some View {
        Button(action: action) {
            // ๋ฒ„ํŠผ ๋‚ด๋ถ€์— ํ…์ŠคํŠธ๋ฅผ ๋ฐฐ์น˜
            Text(title)
                .foregroundColor(.white)
                .padding()
                .background(Color.blue)
                .cornerRadius(8)
        }
    }
}

 

 

 

Dark Mode ์ง€์›

Dark Mode๋ฅผ ์ž๋™์œผ๋กœ ์ง€์›ํ•˜์—ฌ ์•ฑ์ด ๋ผ์ดํŠธ ๋ชจ๋“œ์™€ ๋‹คํฌ ๋ชจ๋“œ ์‚ฌ์ด์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ „ํ™˜ํ•ด์ค€๋‹ค.

 

import UIKit

class FirstViewController: UIViewController {
    // FirstViewController์˜ ์ฝ”๋“œ
}

class SecondViewController: UIViewController {
    // SecondViewController์˜ ์ฝ”๋“œ
}

// UITabBarController๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํƒญ ํ™”๋ฉด ์ „ํ™˜ํ•˜๊ธฐ
let tabBarController = UITabBarController()
let firstVC = FirstViewController()
let secondVC = SecondViewController()
tabBarController.viewControllers = [firstVC, secondVC]

 

 

 

์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ์ƒํƒœ ๊ด€๋ฆฌ 

์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•˜๊ฑฐ๋‚˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

 

import SwiftUI

struct MyView: View {
    // ์ƒํƒœ ๋ณ€์ˆ˜ isAnimating์„ ์„ ์–ธ
    @State private var isAnimating = false
    
    var body: some View {
        Button("Animate") {
            // ๋ฒ„ํŠผ์„ ํƒญํ–ˆ์„ ๋•Œ, ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉ
            withAnimation {
                // isAnimating์„ ํ† ๊ธ€ํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ์ ์šฉ
                self.isAnimating.toggle()
            }
        }
        // ๋ฒ„ํŠผ์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ ˆ
        .scaleEffect(isAnimating ? 1.5 : 1.0)
    }
}

 

 

 

SwiftUI์˜ ์žฅ์ 

๊ธฐ์กด์˜ UI ํ”„๋ ˆ์ž„์›Œํฌ์— ๋น„ํ•ด ํ›จ์”ฌ ๊ฐ„ํŽธํ•˜๊ณ  ๋›ฐ์–ด๋‚œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋‹ค. ์ด๋กœ ์ธํ•ด iOS ์•ฑ ๊ฐœ๋ฐœ์„ ๋”์šฑ ์‰ฝ๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

ํŠนํžˆ ์‹ ์†ํ•œ ๊ฐœ๋ฐœ๊ณผ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ ์ง€์›์œผ๋กœ ์ธํ•ด SwiftUI๋Š” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ธ๊ธฐ๊ฐ€ ์žˆ๊ณ , ์•ž์œผ๋กœ ๋”์šฑ ๋ฐœ์ „ํ•  ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€๋œ๋‹ค.  

728x90