[SwiftUI] pre/next buttons๊ฐ€ ์žˆ๋Š” ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋” ๊ตฌํ˜„ํ•˜๊ธฐ

2024. 5. 16. 16:10ยท๐ŸŽ Dev/๊ตฌํ˜„

 

 

Topic:

 

- pre/next ๋ฒ„ํŠผ์œผ๋กœ ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋” ์ž‘๋™

- ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋”

- tapGesture๋กœ ๋ฒ„ํŠผ ์ˆจ๊ธฐ๊ธฐ ๋ฐ ๋“œ๋Ÿฌ๋‚ด๊ธฐ

- ์ธ๋ฑ์Šค ๋„˜๋ฒ„์— ๋”ฐ๋ผ ์ฒซ๋ฒˆ์งธ์™€ ๋งˆ์ง€๋ง‰ ์ธ๋ฑ์Šค ์ผ ๋•Œ next ๋˜๋Š” pre ๋ฒ„ํŠผ ์ˆจ๊ธฐ๊ธฐ

 

 

์ ‘๊ทผ:

 

- ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋“œ์— ๋“ค์–ด์˜ฌ ์ด๋ฏธ์ง€ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด์„œ tabView์™€ forEach๋ฅผ ํ™œ์šฉํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

// ํ…Œ์ŠคํŠธ์šฉ ์ด๋ฏธ์ง€๋ฐฐ์—ด
    private var images: [String] = [
        "image1",
        "image2",
        "image3",
        "image4"
    ]

 

ํ…Œ์ŠคํŠธ์šฉ์œผ๋กœ ๋„ฃ์–ด๋‘” ์ด๋ฏธ์ง€๋กœ ํ™•์ธํ•  ์˜ˆ์ •์ด๋ผ, ์ด๋ฏธ์ง€ ๋ฐฐ์—ด์€ UIKit๊ณผ ๋˜‘๊ฐ™์ด ๋งŒ๋“ค์—ˆ๋‹ค.

 

 

 

 

 

 

- tabView๋ฅผ ์จ์„œ ์Šฌ๋ผ์ด๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๋ฐ, ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜์— ๋”ฐ๋ผ ๋ฒ„ํŠผ์„ ๋‚˜ํƒ€๋‚ด์•ผํ•œ๋‹ค๊ฑฐ๋‚˜, ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ด์•ผํ•œ๋‹ค๊ฑฐ๋‚˜ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ธ๋ฑ์Šค๋ฅผ ์ผ๋ฐ˜ ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹Œ @State๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

- ๊ทธ๋ฆฌ๊ณ  ์ด๋ฏธ์ง€ ๋ฐ›์•„์˜ค๋Š” ์ˆœ์„œ ๋“ฑ์„ ํ™•์‹คํžˆ ํ•˜๊ณ  ์‹ถ์–ด์„œ tag๋กœ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ›์•˜๋‹ค.

struct MainGuideView: View {
        
    // ํ…Œ์ŠคํŠธ์šฉ ์ด๋ฏธ์ง€๋ฐฐ์—ด
    private var images: [String] = [
        "image1",
        "image2",
        "image3",
        "image4"
    ]
    
    // ์ด๋ฏธ์ง€์Šฌ๋ผ์ด๋“œ ์ธ๋ฑ์Šค
    @State private var index: Int = 0
    
    var body: some View {
        
        ZStack {
            
            // MARK: - ์ด๋ฏธ์ง€ (๋ฐฐ๋„ˆ)
            
            VStack {
                TabView(selection: $index) {
                    ForEach(0..<images.count, id: \.self) { index in
                        
                        Image(images[index])
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(
                                width: UIScreen.main.bounds.width,
                                height: 500,
                                alignment: .center
                            )
                            .clipped()
                            .cornerRadius(16)
                            .tag(index)

                    }
                    
                }
                .tabViewStyle(
                    PageTabViewStyle(
                        indexDisplayMode: .never
                    )
                )
            } // Vstak
            

        
    } // * Body
} // * View

 

 

 

 

- ๊ธฐํš์•ˆ ๊ธฐ์ค€, ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€๋ฅผ ํƒญํ–ˆ์„ ๋•Œ ์ขŒ์šฐ ๋ฒ„ํŠผ์ด ๋‚˜์™€์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์•ก์…˜์„ ๊ฐ์ง€ํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ๋˜ @State์œผ๋กœ ์„ ์–ธํ–ˆ๋‹ค.

// ์‚ฌ์šฉ์ž ๋ฐฐ๋„ˆ ํƒญ์•ก์…˜ ๊ฐ์ง€
    @State private var isTapped: Bool = false

 

// ๋ฐฐ๋„ˆ ํƒญ ์‹œ ์ขŒ์šฐ๋ฒ„ํŠผ + ์ž์„ธํžˆ๋ณด๊ธฐ ๋ฒ„ํŠผ show
     .onTapGesture {
     isTapped.toggle()
     }

 

onTapGesture๊ฐ€ UIKit์˜ tapGesutre ๋ฐ›๋Š” ๊ฑฐ๋ž‘ ๋˜‘๊ฐ™์•„์„œ, ๊ฑฐ๊ธฐ์— ์‚ฌ์šฉ์ž ์•ก์…˜ ๊ฐ์ง€๋ฅผ ์œ„ํ•œ ๋ณ€์ˆ˜๋ฅผ ํ† ๊ธ€ ์ฒ˜๋ฆฌ ํ•ด์ฃผ์—ˆ๋‹ค.

 

 

 

 

 

- ์ธ๋ฑ์Šค๋ฅผ ํ™œ์šฉํ•ด์„œ ์ธ๋ฑ์Šค๊ฐ€ 0์ผ๋•Œ์™€ ๋งˆ์ง€๋ง‰์ผ ๋•Œ, ๊ฐ๊ฐ ์™ผ์ชฝ ๋˜๋Š” ์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ์„ ์ˆจ๊ฒจ์ฃผ์–ด์•ผ ํ•ด์„œ ๊ทธ๋ถ€๋ถ„์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌํ–ˆ๋‹ค.

- ๊ทธ๋ฆฌ๊ณ  pre/next ๋ฒ„ํŠผ์„ ํƒญํ•˜๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ์ธ๋ฑ์Šค์— ๋งž๊ฒŒ ๋ฐ”๋€Œ์–ด์•ผํ•ด์„œ action์€ ๊ทธ์— ๋Œ€ํ•œ ์ฝ”๋“œ.

 

  // MARK: - Buttons (pre/next)
            
            /// ๋ฐฐ๋„ˆ ํƒญ ํ•˜๋ฉด ๋“ฑ์žฅ
            if isTapped{
                
                HStack {
                    
                    /// ์ธ๋ฑ์Šค ๋ฒˆํ˜ธ์— ๋”ฐ๋ผ ์ขŒ์šฐ ๋ฒ„ํŠผ ์ˆจ๊น€
                    /// pre button
                    if index > 0 {
                        Button(action: {
                            if index > 0 {
                                index -= 1
                            }
                        }) {
                            Image(.ic32ArrowBack2)
                                .frame(width: 32, height: 32)
                        }
                    }
                    
                    Spacer()
                    
                    /// next button
                    if index < images.count - 1 {
                        Button(action: {
                            if index < images.count - 1 {
                                index += 1
                            }
                        }) {
                            Image(.ic32ArrowBack2)
                                .rotationEffect(.degrees(-180))
                                .frame(width: 32, height: 32)
                        }
                    }
                    
                } // * HStack (pre/next btn)
                .padding(.horizontal, 26)

 

 

 

 

 

 

 

- ์œ„ ์ ‘๊ทผ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•œ ์ „์ฒด ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

(๋ณธ๋ฌธ์—” ๋”ฐ๋กœ ์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์€ ์ฝ”๋“œ๋„ ํฌํ•จ๋˜์–ด ์žˆ์Œ.)

 

struct MainCoffeGuideTableSwiftUIView: View {
        
    // ํ…Œ์ŠคํŠธ์šฉ ์ด๋ฏธ์ง€๋ฐฐ์—ด
    private var images: [String] = [
        "image1",
        "image2",
        "image3",
        "image4"
    ]
    
    // ์ด๋ฏธ์ง€์Šฌ๋ผ์ด๋“œ ์ธ๋ฑ์Šค
    @State private var index: Int = 0
    // ์‚ฌ์šฉ์ž ๋ฐฐ๋„ˆ ํƒญ์•ก์…˜ ๊ฐ์ง€
    @State private var isTapped: Bool = false
    
    var body: some View {
        
        ZStack {
            
            // BavkView
            background
            
            // MARK: - ์ด๋ฏธ์ง€ (๋ฐฐ๋„ˆ)
            
            VStack {
                TabView(selection: $index) {
                    ForEach(0..<images.count, id: \.self) { index in
                        
                        Image(images[index])
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(
                                width: UIScreen.main.bounds.width,
                                height: 500,
                                alignment: .center
                            ) // -- ์•„๋งˆ ์ˆ˜์ • ํ•„์š”
                            .clipped()
                            .cornerRadius(16)
                            .tag(index)
                        
                        // ๋ฐฐ๋„ˆ ํƒญ ์‹œ ์ขŒ์šฐ๋ฒ„ํŠผ + ์ž์„ธํžˆ๋ณด๊ธฐ ๋ฒ„ํŠผ show
                            .onTapGesture {
                                print("banner tapped!")
                                isTapped.toggle()
                            }
                        
                            .overlay (
                                
                                VStack {
                                    
                                    /// ๋ฐฐ๋„ˆ ๊ฐœ์ˆ˜ ํ‘œ์‹œ
                                    Text("\(index + 1)/ \(images.count)")
                                        .font(Font.Pretendard(type: .Regular, size: 10))
                                        .foregroundStyle(.uiColorWhite)
                                        .frame(width: 30, height: 16, alignment: .center)
                                        .background(Capsule().fill(Color.uiColorBlack).opacity(0.5))
                                        .frame(
                                            maxWidth: .infinity,
                                            maxHeight: .infinity,
                                            alignment: .topTrailing
                                        )
                                        .padding(.top, 24)
                                    
                                    /// ์ž์„ธํžˆ๋ณด๊ธฐ ๋ฒ„ํŠผ
                                    /// ๋ฐฐ๋„ˆ ํƒญ ํ•˜๋ฉด ๋“ฑ์žฅ
                                    if isTapped {
                                        Button(action: {
                                            print("์ž์„ธํžˆ๋ณด๊ธฐ ๋ฒ„ํŠผ")
                                            self.moveAction?(.timeSaleAction)
                                            
                                        }, label: {
                                            
                                            HStack {
                                                
                                                Text("์ž์„ธํžˆ ๋ณด๊ธฐ")
                                                    .font(Font.Pretendard(type: .SemiBold, size: 12))
                                                    .foregroundStyle(.uiColorBlack)
                                                
                                                Image(.ic32ArrowBlack)
                                                    .frame(width: 4, height: 8)
                                            }
                                            .padding(.leading, 12)
                                            .padding(.trailing, 16)
                                            .padding(.vertical, 8)
                                            
                                        }) // * Button
                                        .background(.uiColorWhite)
                                        .cornerRadius(10, corners: .allCorners)
                                        .frame(width: 97, height: 32)
                                        .frame(
                                            maxWidth: .infinity,
                                            maxHeight: .infinity,
                                            alignment: .bottomTrailing
                                        )
                                        .padding(.bottom, 40)
                                    }
                                    
                                } // * Vstack (indexNm, ์ž์„ธํžˆ๋ณด๊ธฐ)
                                    .padding(.trailing, 25)
                                
                            )
                    }
                    
                }
                .tabViewStyle(
                    PageTabViewStyle(
                        indexDisplayMode: .never
                    )
                )
            } // Vstak
            
            // MARK: - Buttons (pre/next)
            
            /// ๋ฐฐ๋„ˆ ํƒญ ํ•˜๋ฉด ๋“ฑ์žฅ
            if isTapped{
                
                HStack {
                    
                    /// ์ธ๋ฑ์Šค ๋ฒˆํ˜ธ์— ๋”ฐ๋ผ ์ขŒ์šฐ ๋ฒ„ํŠผ ์ˆจ๊น€
                    /// pre button
                    if index > 0 {
                        Button(action: {
                            if index > 0 {
                                index -= 1
                            }
                        }) {
                            Image(.ic32ArrowBack2)
                                .frame(width: 32, height: 32)
                        }
                    }
                    
                    Spacer()
                    
                    /// next button
                    if index < images.count - 1 {
                        Button(action: {
                            if index < images.count - 1 {
                                index += 1
                            }
                        }) {
                            Image(.ic32ArrowBack2)
                                .rotationEffect(.degrees(-180))
                                .frame(width: 32, height: 32)
                        }
                    }
                    
                } // * HStack (pre/next btn)
                .padding(.horizontal, 26)
            }
            
            
        }
        .frame(
            width: UIScreen.main.bounds.width,
            height: 516,
            alignment: .center
        )
        
    } // * Body
} // * View

 

 

 

 

 

 


 

 

 

 

 

 

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ๋ณด๋‹ˆ๊นŒ ๊ฐ€๋…์„ฑ์ด ๋„ˆ๋ฌด ์•ˆ ์ข‹์•„์„œ...

 

ํ›„์— ๋„คํŠธ์›Œํฌ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ๋ฅผ ์ผ๋ถ€๋ถ„ ์ˆ˜์ •ํ•˜๋ฉด์„œ ๊ตฌ์กฐ์ฒด๋ฅผ ๋” ์ชผ๊ฐœ๋Š” ์ž‘์—…์„ ํ•ด์„œ ๋ฉ”์ธ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ฒฐํ•ฉํ•˜๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ–ˆ๋‹ค.

 

import SwiftUI
import Combine

struct MainCoffeeGuideTableSwiftUIView: View {
        
    // ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ƒ‰์ƒ
    let background = Color(UIColor(hexCode: "eeeeee"))
        
    // ๋ฐฐ๋„ˆ๋ฆฌ์ŠคํŠธ ๋ฐฐ์—ด
    var bannerList: [BannerList] = []
    // ์ด๋ฏธ์ง€์Šฌ๋ผ์ด๋“œ ์ธ๋ฑ์Šค
    @State private var index: Int = 0
    // ์‚ฌ์šฉ์ž ๋ฐฐ๋„ˆ ํƒญ์•ก์…˜ ๊ฐ์ง€
    @State private var isTapped: Bool = false
    
    var body: some View {
        
        ZStack {
            
            // BackView
            background
            
            // MARK: - ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋” (๋ฐฐ๋„ˆ)
            
            MainCoffeeGuideImageSlider(
                bannerImages: getImage(),
                title: getTitle(),
                bannerList: bannerList
            )
            
            // MARK: - Buttons (pre/next)
            
            /// ๋ฐฐ๋„ˆ ํƒญ ํ•˜๋ฉด ๋“ฑ์žฅ
            if isTapped{
                
                MainCoffeeGuideBannerButtons(
                    index: $index,
                    total: getImage().count
                )
                
            }
            
            
        }
        .frame(
            maxWidth: UIScreen.main.bounds.width,
            minHeight: 516,
            maxHeight: 601,
            alignment: .center
        )
        
    } // * Body
    

} // * View


extension MainCoffeeGuideTableSwiftUIView {
    
    /// ์ด๋ฏธ์ง€๋ฐฐ์—ด
    func getImage() -> [String] {
        return bannerList.map { $0.bannerImg ?? "" }
    }
    
    /// ํ…์ŠคํŠธ๋ฐฐ์—ด
    func getTitle() -> [String] {
        return bannerList.map { $0.title ?? "" }
    }
    
    /// ๋งํฌ๋ฐฐ์—ด
    func getLink() -> [String] {
        return bannerList.map { $0.linkUrlL ?? "" }
    }
}

 

 

 

 

 

 

 

 

 

 

Referances

 

https://developer.apple.com/documentation/swiftui/fitting-images-into-available-space

 

Fitting images into available space | Apple Developer Documentation

Adjust the size and shape of images in your app’s user interface by applying view modifiers.

developer.apple.com

 

์ €์ž‘์žํ‘œ์‹œ (์ƒˆ์ฐฝ์—ด๋ฆผ)

'๐ŸŽ Dev > ๊ตฌํ˜„' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[SwiftUI] pagerView ๋งŒ๋“ค๊ธฐ (iOS ๋ฒ„์ „๋Œ€์‘)  (2) 2024.08.14
[SwiftUI] CustomPopUpView ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ํ•ด๊ฒฐํ•˜๊ธฐ  (0) 2024.06.27
[SwiftUI] ์Šค์œ ๋กœ ์„น์…˜ ์ ‘์—ˆ๋‹คํˆ๋‹ค ๊ตฌํ˜„ํ•˜๊ธฐ  (0) 2024.06.21
[SwiftUI] TabView page indicator ์ปค์Šคํ…€ํ•˜๊ธฐ  (1) 2024.04.25
[UIKit] Enum with Reusable VC  (0) 2023.10.10
'๐ŸŽ Dev/๊ตฌํ˜„' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [SwiftUI] CustomPopUpView ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ํ•ด๊ฒฐํ•˜๊ธฐ
  • [SwiftUI] ์Šค์œ ๋กœ ์„น์…˜ ์ ‘์—ˆ๋‹คํˆ๋‹ค ๊ตฌํ˜„ํ•˜๊ธฐ
  • [SwiftUI] TabView page indicator ์ปค์Šคํ…€ํ•˜๊ธฐ
  • [UIKit] Enum with Reusable VC
Callie_
Callie_
  • Callie_
    CalliOS
    Callie_
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ
      • ๐ŸŽ APPLE
      • ๐ŸŽ Dev
        • Swift
        • UIKit
        • SwiftUI
        • Issue
        • ๊ตฌํ˜„
      • ๐ŸŽ Design
        • HIG
      • โš™๏ธ CS
      • ๐Ÿ’ก ์•Œ๊ณ ๋ฆฌ์ฆ˜
        • ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค
        • ๋ฐฑ์ค€
      • ๐ŸŸ๏ธ ์ง๊ด€๋กœ๊ทธ (์ถœ์‹œ์•ฑ)
        • ์—…๋ฐ์ดํŠธ
      • ๐ŸŒฑ SeSAC iOS 3๊ธฐ
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ƒ๋ช…์ฃผ๊ธฐ
    Entry Point
    ios
    SeSAC
    ํ™”๋ฉด์ „ํ™˜
    SwiftUI
    TableViewCell
    IBOutlet
    DiffableDataSource
    DidEndOnExit
    layer.shadow
    .OverFullScreen
    apply
    TapGestureRecognizer
    cornerradius
    Infoํƒญ
    modalPresentStyle
    Snapshot
    CocoaTouchFramework
    diffable
    .fullScreen
    IBAction
    Enum
    CustomView
    ๋„คํŠธ์›Œํฌํ†ต์‹ 
    addTarget
    clipsToBound
    assets
    stroyboard
    Swift
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.0
Callie_
[SwiftUI] pre/next buttons๊ฐ€ ์žˆ๋Š” ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋” ๊ตฌํ˜„ํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”