IOS(Swift)

[IOS/Swift] ์ฝ˜ํ…์ธ  ๊ฒ€์ƒ‰ ์•ฑ ๊ตฌํ˜„ - 2. ์ฑ„ํŒ…๋ฐฉ UI ๊ตฌํ˜„

Tempo 2022. 3. 5. 13:03

์ „ํŽธ์— ์ด์–ด์„œ ์ฑ„ํŒ…๋ฐฉ UI๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ๋‹ค.

์ฑ„ํŒ…๋ฐฉ์€ ํด๋Ÿฝํ•˜์šฐ์Šค ์•ฑ์˜ UI๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ฒฐ๊ณผ๋ฌผ ํ™”๋ฉด

 

๊ฒฐ๊ณผ๋ฌผ ์˜ˆ์‹œ

์ฑ„ํŒ…๋ฐฉ ์ƒ์„ธ ํ™”๋ฉด ๊ตฌํ˜„

์ฑ„ํŒ…๋ฐฉ์—๋Š” ๊ฐ ์œ ์ €๋ณ„ ํ”„๋กœํ•„ ์‚ฌ์ง„์ด ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœํ•„ ํ™”๋ฉด๋„ ๊ตฌํ˜„ํ•ด๋ณด์ž

ํ”„๋กœํ•„ ํ™”๋ฉด ๊ตฌํ˜„

ํ”„๋กœํ•„ ํ™”๋ฉด์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„์€ ์ถ”ํ›„ Amazon S3 ๋ฅผ ํ™œ์šฉํ•ด ๊ด€๋ฆฌํ•  ์˜ˆ์ •์ด๋‹ค. (REST API๋กœ ๊ด€๋ จ URL ์„ ๋ฐ›์„ ์˜ˆ์ •). ๊ทธ๋ž˜์„œ ์ด๋ฏธ์ง€ URL ๋กœ ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” UI ๊ฐ€ ๋ณ„๋„๋กœ ํ•„์š”ํ•˜๋‹ค.

Swift ์—์„œ๋Š” AsyncImage๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ด๋ฏธ์ง€์˜ url๋กœ(html ์—์„œ image ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌ) ์‚ฌ์ง„์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

import SwiftUI

struct ProfileItem: View {
    var talkRoomTitle: String
    var imageUrl: String
    
    
    var body: some View {
        VStack(alignment: .leading){
            Circle() // Circle element ์œ„์— image๋ฅผ ์˜ค๋ฒ„๋ ˆ์ดํ•˜์—ฌ ํ”„๋กœํ•„ ์‚ฌ์ง„์„ ๋งŒ๋“ฌ
                .overlay(
                    AsyncImage(
                        url: URL(string: imageUrl),
                        content: { image in
                            image.resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(width: 100, height: 100)
                                .clipShape(Circle())
                        },
                        placeholder: {
                            ProgressView()
                        }
                    )
                )
                .frame(width: 100, height: 100)
            Text(talkRoomTitle) // ์ฑ„ํŒ…๋ฐฉ์— ๋“ค์–ด๊ฐ€๋Š” ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„
                .frame(width: 100)
                .lineLimit(2)
                .font(.subheadline)
        }
            
        
    }
}

struct ProfileItem_Previews: PreviewProvider {
    static var previews: some View {
        ProfileItem(
            talkRoomTitle: "John",
            imageUrl: "<https://cdn.pixabay.com/photo/2015/03/03/18/58/woman-657753_960_720.jpg>"
        )
    }
}

์ฑ„ํŒ…๋ฐฉ ์ƒ์„ธํ™”๋ฉด(With ์œ ์ €)

๊ฐ ์ฑ„ํŒ…๋ฐฉ์— ์œ ์ €๊ฐ€ ๋“ค์–ด์˜จ ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

import SwiftUI

struct TalkRoomDetail: View {
    @Environment(\\.presentationMode) var presentationMode
    
    var roomTitle: String
    var mbtiType: Array<String>
    
    let userList = ModelData().userInfos
    let columns = [
        GridItem(.adaptive(minimum: 100))
    ]
    
    
    var body: some View {
        ZStack(alignment: .leading){
            Color.white.ignoresSafeArea()
                .navigationBarBackButtonHidden(true)
                .navigationBarHidden(true)
            VStack(alignment: .leading){
                Button(action: {
                    self.showingAlert = true
                }){
                    Text("๋‚˜๊ฐ€๊ธฐ")
                }.alert(isPresented: $showingAlert){
                    Alert(title: Text("์ฑ„ํŒ…๋ฐฉ ๋‚˜๊ฐ€๊ธฐ"), message: Text("์ฑ„ํŒ…๋ฐฉ์„ ๋‚˜๊ฐ€์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"),
                          primaryButton: .destructive(Text("๋‚˜๊ฐ€๊ธฐ"), action: {
                        presentationMode.wrappedValue.dismiss()
                    })
                          , secondaryButton: .cancel(Text("์ทจ์†Œ")))
                }
                .padding()
                Text(roomTitle)
                    .font(.title)
                    .bold()
                    .padding(.leading)
                    .padding(.top)
                Text(mbtiType.joined(separator: " "))
                    .font(.headline)
                    .bold()
                    .padding(.leading)
                
                Divider()
                
                ScrollView{
                    LazyVGrid(columns: columns, spacing: 10) {
                        ForEach(userList, id:\\.self) {i in
                            ProfileItem(talkRoomTitle: i.userId, imageUrl: i.profilePic)}
                    }
                }.frame(width: .infinity, height: 400)
                Divider()
            }
        }
    }
    
}

struct TalkRoomDetail_Previews: PreviewProvider {
    static var previews: some View {
        TalkRoomDetail(
            roomTitle: "์ œ์ฃผ๋„ ์—ฌํ–‰์ฐ",
            mbtiType: ["#ISTJ","#ENFP"]
        )
    }
}

์ฑ„ํŒ…๋ฐฉ UI๋กœ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์ด์ „ UI์—์„œ๋Š” NavigationView๋ฅผ ์ด์šฉํ•˜์—ฌ ์ ‘๊ทผํ•œ๋‹ค. NavigationView๋ฅผ ์ด์šฉํ•ด ์ ‘๊ทผํ•˜๋ฉด ๋’ค๋กœ ๋Œ์•„๊ฐ€๋Š” ํ™”๋ฉด(Navigation UI)์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์ฝ”๋“œ ๋‚ด

@Environment(\\.presentationMode) **var** presentationMode ๋ฅผ ์ด์šฉํ•ด NavigationView๋กœ ์ ‘๊ทผํ•œ View ์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์ด ์—†๋„๋ก ๊ฐœ๋ฐœํ•œ๋‹ค.

๋˜ํ•œ ์ฑ„ํŒ…๋ฐฉ์˜ ์œ ์ €๋Š” ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋ณ„๋„ ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜์˜€๋‹ค. (REST API ๋˜๋Š” NoSQL(Firebase or Mongo DB)์—์„œ ๋ฐ›์•„์˜ค๋„๋ก)

Grid View ๊ตฌํ˜„

์œ ์ €๋ณ„ ํ”„๋กœํ•„์€ Grid View๋ฅผ ์ด์šฉํ•ด 3 * N ๊ฐœ์˜ ์œ ์ €๊ฐ€ ๋ณด์—ฌ์งˆ ์ˆ˜ ์žˆ๋„๋ก UI๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๋‹ค.

์—ฌ๊ธฐ์— ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

LazyVGrid(columns: columns, spacing: 10) {
    ForEach(userList, id:\\.self) {i in
        ProfileItem(talkRoomTitle: i.userId, imageUrl: i.profilePic)}
}

์ฑ„ํŒ…๋ฐฉ ๋‚˜๊ฐ€๊ธฐ ํ™”๋ฉด ๊ตฌํ˜„

Navigation ๊ธฐ๋Šฅ์—์„œ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ Hidden ์‹œ์ผฐ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ๋‚˜๊ฐ€๊ธฐ ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์—ˆ๋‹ค.

Button(action: {
        self.showingAlert = true
    }){
        Text("๋‚˜๊ฐ€๊ธฐ")
    }.alert(isPresented: $showingAlert){
        Alert(title: Text("์ฑ„ํŒ…๋ฐฉ ๋‚˜๊ฐ€๊ธฐ"), message: Text("์ฑ„ํŒ…๋ฐฉ์„ ๋‚˜๊ฐ€์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?"),
              primaryButton: .destructive(Text("๋‚˜๊ฐ€๊ธฐ"), action: {
            presentationMode.wrappedValue.dismiss()
        })
              , secondaryButton: .cancel(Text("์ทจ์†Œ")))
    }
    .padding()

๋‹ค์Œ์—๋Š” ํ‚ค์›Œ๋“œ ๊ฒ€์ƒ‰์„ ๊ตฌํ˜„ํ•˜๊ณ (REST API ์™€ ์—ฐ๊ฒฐ) ๊ฒ€์ƒ‰ ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

๋ฐ˜์‘ํ˜•