Topic:
- pre/next 버튼으로 이미지 슬라이더 작동
- 이미지 슬라이더
- tapGesture로 버튼 숨기기 및 드러내기
- 인덱스 넘버에 따라 첫번째와 마지막 인덱스 일 때 next 또는 pre 버튼 숨기기
접근:
- 이미지 슬라이드에 들어올 이미지 배열을 만들어서 tabView와 forEach를 활용해야겠다고 생각했다.
// 테스트용 이미지배열
private var images: [String] = [
"bottle_coffee",
"new_menu",
"main_event_bossam2",
"new_menu"
]
테스트용으로 넣어둔 이미지로 확인할 예정이라, 이미지 배열은 UIKit과 똑같이 만들었다.
- tabView를 써서 슬라이드를 만들었는데, 이미지 개수에 따라 버튼을 나타내야한다거나, 이미지 개수를 나타내야한다거나 했기 때문에 인덱스를 일반 변수가 아닌 @State로 작성했다.
- 그리고 이미지 받아오는 순서 등을 확실히 하고 싶어서 tag로 인덱스를 받았다.
struct MainGuideView: View {
// 테스트용 이미지배열
private var images: [String] = [
"bottle_coffee",
"new_menu",
"main_event_bossam2",
"new_menu"
]
// 이미지슬라이드 인덱스
@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] = [
"bottle_coffee",
"new_menu",
"main_event_bossam2",
"new_menu"
]
// 이미지슬라이드 인덱스
@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
'iOS > 구현' 카테고리의 다른 글
[SwiftUI] 스유로 섹션 접었다폈다 구현하기 (0) | 2024.06.21 |
---|---|
[SwiftUI] TabView page indicator 커스텀하기 (1) | 2024.04.25 |