까칠코더 2023. 12. 30. 01:11
반응형

세그먼트 컨트롤을 사용하기 위해서는 기본 제공되는 Picker를 사용해서 .pickerStyle(SegmentedPickerStyle())을 적용해주면 됩니다. 
(Picker를 이용한 Segment 사용방법 : https://kka7.tistory.com/221)

기본 디자인을 사용하면 좋은데 실제 프로젝트에서는 대부분 전혀 다른 디자인을 요구함으로써 직접 구현해야 할때가 있습니다. 

다음 코드는 왼쪽과 오른쪽이 둥근 세그먼트 뷰에 대한 샘플이고,적용한 화면은 다음과 같습니다.

struct SegmentView<ID: Identifiable, Content: View>: View {
    let segments: [ID]
    @Binding var selected: ID
    @ViewBuilder var content: (ID) -> Content
    private let normalBorderColor = Color.gray
    private let selectedBorderColor = Color.red
    private let cornerRadius = CGFloat(20)
    private let borderWidth = CGFloat(2)
    
    var body: some View {
        HStack(spacing: 0) {
            let maxCount = segments.count
            ForEach(Array(segments.enumerated()), id: \.offset) { index, segment in
                let isSelected = selected.id == segment.id
                let dividerColor: Color = {
                    if (!isSelected && index != maxCount - 1 && segments[safe: index - 1]?.id != segment.id) || index == 0 {
                        return normalBorderColor
                    } else {
                        return Color.clear
                    }
                }()
                let rectCorners: UIRectCorner = {
                    if index == 0 {
                        [.topLeft, .bottomLeft]
                    } else if index == maxCount - 1 {
                        [.topRight, .bottomRight]
                    } else {
                        []
                    }
                }()
                
                Button {
                    selected = segment
                } label: {
                    content(segment)
                }
                .overlay(
                    ZStack(alignment: .trailing) {
                        Rectangle()
                            .frame(width: borderWidth)
                            .foregroundColor(dividerColor)
                            .offset(x: borderWidth / 2)
                        RoundedCorner(radius: cornerRadius, corners: rectCorners)
                            .stroke(isSelected ? selectedBorderColor : Color.clear, lineWidth: borderWidth)
                    }
                )
            }
        }
        .background(
            RoundedRectangle(cornerRadius: cornerRadius)
                .stroke(normalBorderColor, lineWidth: borderWidth)
        )
    }
}

struct RoundedCorner: Shape {
    let radius: CGFloat
    let corners: UIRectCorner
    
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius * 2, height: radius * 2))
        return Path(path.cgPath)
    }
}

extension Array {
    subscript(safe index: Int) -> Element? {
        return indices ~= index ? self[index] : nil
    }
}

사용한 예제 

struct TestSegmentView: View {
    enum Segment: Int, CustomStringConvertible, CaseIterable, Identifiable {
        case zero, one, two, three, four
        var description: String { "\(self.rawValue)" }
        var id: Int { rawValue }
    }
    
    @State var selected: Segment = .zero
    
    var body: some View {
        VStack {
            SegmentView(segments: Segment.allCases, selected: $selected) { segment in
                Text(segment.description)
                    .foregroundColor(selected == segment ? Color.red : Color.gray)
                    .frame(width: 60,  height: 40)
            }
        }
    }
}
반응형