Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PackageDescription

let package = Package(
name: "Montage",
defaultLocalization: "ko",
platforms: [.iOS(.v16)],
products: [
.library(name: "Montage", targets: ["Montage"])
Expand Down
5 changes: 4 additions & 1 deletion Sources/Montage/1 Components/2 Actions/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ public struct Button: View {
private var loading = false
private var fillHorizontal = false
private var fillVertical = false

/// 버튼을 비활성화 상태로 설정합니다.
///
/// 비활성화된 버튼은 시각적으로 흐리게 표시되며 사용자 상호작용에 반응하지 않습니다.
Expand Down Expand Up @@ -366,6 +365,10 @@ public struct Button: View {
}
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: handler))
.allowsHitTesting(!disable && !loading)
.accessibilityElement(children: .ignore)
.accessibilityLabel(text ?? leadingIcon?.rawValue ?? "")
.accessibilityAddTraits(.isButton)
.accessibilityValue(loading ? String(localized: "로딩 중", bundle: .module) : "")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/Montage/1 Components/2 Actions/Chip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ public struct Chip: View {
)
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: handler))
.disabled(disable)
.accessibilityElement(children: .ignore)
.accessibilityLabel(text)
.accessibilityAddTraits(.isButton)
.accessibilityValue(active ? String(localized: "선택됨", bundle: .module) : "")
}

// MARK: - Modifiers
Expand Down
4 changes: 3 additions & 1 deletion Sources/Montage/1 Components/2 Actions/IconButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public struct IconButton: View {
private var iconColor: SwiftUI.Color?
private var backgroundColor: SwiftUI.Color?
private var borderColor: SwiftUI.Color?

/// 버튼의 비활성화 여부를 설정합니다.
/// - Parameter value: 비활성화 여부, true이면 버튼이 비활성화됩니다.
/// - Returns: 수정된 IconButton 인스턴스
Expand Down Expand Up @@ -223,6 +222,9 @@ public struct IconButton: View {
)
.allowsHitTesting(disable == false)
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: handler))
.accessibilityElement(children: .ignore)
.accessibilityLabel("\(icon.rawValue) \(String(localized: "아이콘", bundle: .module))")
.accessibilityAddTraits(.isButton)
Comment thread
knine79 marked this conversation as resolved.
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ struct Control: View {
onSelect?($0 ? .checked : .unchecked)
})
)
.labelsHidden()
.disabled(disable)
.tint(backgroundColor)
.frame(width: switchSize.width, height: switchSize.height)
Expand Down Expand Up @@ -166,6 +167,10 @@ struct Control: View {
}
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel(label)
.accessibilityValue(accessibilityStateDescription)
.accessibilityAddTraits(.isButton)
}
}

Expand Down Expand Up @@ -407,6 +412,14 @@ extension Control {
)
}

fileprivate var accessibilityStateDescription: String {
switch state {
case .checked: String(localized: "선택됨", bundle: .module)
case .unchecked: String(localized: "선택 안 됨", bundle: .module)
case .indeterminate: String(localized: "부분 선택됨", bundle: .module)
}
}

fileprivate var spacing: CGFloat {
switch variant {
case .checkbox, .radio:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public struct FilterButton: View {
)
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: handler))
.disabled(disable)
.accessibilityElement(children: .ignore)
.accessibilityLabel(text)
.accessibilityAddTraits(.isButton)
.accessibilityValue(active ? (activeLabel ?? String(localized: "선택됨", bundle: .module)) : "")
}

// MARK: - Modifiers
Expand All @@ -137,7 +141,6 @@ public struct FilterButton: View {
private var customIconColor: SwiftUI.Color?
private var fillHorizontal = false
private var fillVertical = false

/// 버튼의 활성화 상태와 레이블을 설정합니다.
///
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ public struct SegmentedControl: View {
.padding(buttonInsets)
.frame(width: max(0, buttonWidth))
.frame(maxHeight: .infinity)
.accessibilityRemoveTraits(selectedIndex == index ? [] : .isSelected)
.accessibilityAddTraits(selectedIndex == index ? .isSelected : [])
.background {
Group {
switch variant {
Expand Down Expand Up @@ -193,7 +195,6 @@ public struct SegmentedControl: View {
// MARK: - Modifiers
private var variant: Variant = .solid
private var size: Size = .large

/// 세그먼트 컨트롤의 시각적 스타일을 설정합니다.
///
/// - Parameter variant: 적용할 스타일
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ public struct Select: View {
private var shadowBackgroundColor: SwiftUI.Color = .init(uiColor: UIColor.systemBackground)
private var leadingContent: LeadingContent?
private var menuResize: BottomSheet.Resize = .hug

/// negative 상태 여부를 조정합니다.
/// - Parameter negative: 부정적 상태 여부, 생략하면 기본값으로 `true` 적용
/// - Returns: 수정된 Select 인스턴스
Expand Down Expand Up @@ -427,6 +426,10 @@ public struct Select: View {
}
}
.allowsHitTesting(disable == false)
.accessibilityElement(children: .ignore)
.accessibilityLabel(heading.isEmpty ? placeholder : heading)
.accessibilityValue(selectedItems.map(\.text).joined(separator: ", "))
.accessibilityAddTraits(.isButton)
.onTapGesture {
menuPresented.wrappedValue.toggle()
}
Expand Down
47 changes: 45 additions & 2 deletions Sources/Montage/1 Components/3 Selection And Input/Slider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,18 @@ public struct Slider: View {
}
}
.allowsHitTesting(!disable)
.modifier(SliderAccessibilityModifier(
label: String(localized: "슬라이더", bundle: .module),
value: headingLabel,
disable: disable,
onAdjust: handleAccessibilityAdjust
))
}

// MARK: - Modifiers
private var heading = false
private var label = false
private var disable = false

/// 슬라이더 상단에 제목을 표시할지 여부를 설정합니다.
///
/// - Parameter heading: 제목 표시 여부, 생략하면 기본값으로 `true` 적용
Expand Down Expand Up @@ -270,7 +275,30 @@ public struct Slider: View {
}

// MARK: - private


private func handleAccessibilityAdjust(_ direction: AccessibilityAdjustmentDirection) {
guard !disable else { return }
let step = 0.05
switch direction {
case .increment:
if isRangeSlider, focusedThumb == 1 {
thumbRatio1 = min(thumbRatio2, thumbRatio1 + step)
} else {
thumbRatio2 = min(1.0, thumbRatio2 + step)
}
case .decrement:
if isRangeSlider, focusedThumb != 1 {
thumbRatio2 = max(thumbRatio1, thumbRatio2 - step)
} else if isRangeSlider {
thumbRatio1 = max(0.0, thumbRatio1 - step)
} else {
thumbRatio2 = max(0.0, thumbRatio2 - step)
}
@unknown default:
break
}
}
Comment thread
knine79 marked this conversation as resolved.

private func updateValues() {
lowValue = (valueRange.upperBound - valueRange.lowerBound) * lowThumbRatio + valueRange.lowerBound
highValue = (valueRange.upperBound - valueRange.lowerBound) * highThumbRatio + valueRange.lowerBound
Expand Down Expand Up @@ -445,3 +473,18 @@ public struct Slider: View {
}
}
}

private struct SliderAccessibilityModifier: ViewModifier {
let label: String
let value: String
let disable: Bool
let onAdjust: (AccessibilityAdjustmentDirection) -> Void

func body(content: Content) -> some View {
content
.accessibilityElement(children: .ignore)
.accessibilityLabel(label)
.accessibilityValue(value)
.accessibilityAdjustableAction(onAdjust)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ public struct TextArea: View {
private var trailingResourceSpacing: CGFloat = 4
private var characterCounterLimit: Int?
private var characterCounterOverflow: Bool = false

/// 텍스트 영역의 크기 조절 방식을 설정합니다.
///
/// - Parameter resize: 크기 조절 방식
Expand Down Expand Up @@ -430,6 +429,8 @@ public struct TextArea: View {
.onTapGesture {
focus.wrappedValue = true
}
.accessibilityLabel(heading ?? "")
.accessibilityValue(negative ? (description ?? String(localized: "오류", bundle: .module)) : "")
}

private var editorStrokeColor: SwiftUI.Color {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ public struct TextField: View {
private var trailingContent: () -> AnyView = { AnyView(EmptyView()) }
private var suggestions: Binding<[String]> = .constant([])
private var customBackgroundColor: SwiftUI.Color?

/// 텍스트 필드의 상태를 설정합니다.
///
/// - Parameter status: 텍스트 필드의 상태
Expand Down Expand Up @@ -330,6 +329,8 @@ private extension TextField {
.focused($textFieldFocusState)
.frame(minHeight: 24)
.padding(.horizontal, 4)
.accessibilityLabel(heading ?? "")
.accessibilityValue(accessibilityStatusDescription)

if !text.isEmpty, textFieldFocusState {
IconButton(
Expand Down Expand Up @@ -464,6 +465,17 @@ private extension TextField {
)
}

var accessibilityStatusDescription: String {
switch status {
case .negative(let description):
description.isEmpty ? String(localized: "오류", bundle: .module) : description
case .positive(let description):
description.isEmpty ? "" : description
case .normal(let description):
description
}
}

var captionTextColor: SwiftUI.Color {
switch status {
case .negative:
Expand Down
14 changes: 12 additions & 2 deletions Sources/Montage/1 Components/4 Contents/Avatar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ public struct Avatar: View {
/// 학원 프로필 (둥근 모서리 사각형)
case academy

var accessibilityDescription: String {
switch self {
case .person: String(localized: "프로필 이미지", bundle: .module)
case .company: String(localized: "회사 로고", bundle: .module)
case .academy: String(localized: "학원 로고", bundle: .module)
}
}

fileprivate var placeholderImageName: String {
switch self {
case .person:
Expand Down Expand Up @@ -175,16 +183,18 @@ public struct Avatar: View {
}
}
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: onTap))
.accessibilityElement(children: .ignore)
.accessibilityLabel(variant.accessibilityDescription)
.if(onTap != nil) { $0.accessibilityAddTraits(.isButton) }
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private var pushBadge = false
private var pushBadgeSizeOverride: PushBadge.Size?
private var customCornerRadius: CGFloat?
private var contentMode: ContentMode = .fit
private var borderColor: SwiftUI.Color = .semantic(.lineAlternative)
private var borderWidth: CGFloat = 1
private var interactionDisabled = false

/// 푸시 알림 표시 뱃지를 아바타에 추가합니다.
///
/// 푸시 뱃지는 사용자(.person) 아바타에만 적용 가능합니다.
Expand Down
5 changes: 5 additions & 0 deletions Sources/Montage/1 Components/4 Contents/AvatarGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ public struct AvatarGroup: View {

trailingContent()
}
.accessibilityElement(children: .ignore)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
.accessibilityLabel(
"\(variant.accessibilityDescription) \(imageSources.count)"
)
}

// MARK: - Modifiers
Expand Down Expand Up @@ -169,6 +173,7 @@ public struct AvatarGroup: View {
zelf.trailingContent = { AnyView(trailingContent()) }
return zelf
}

}

/// `AvatarGroup`의 내부 구현을 위한 확장입니다.
Expand Down
1 change: 1 addition & 0 deletions Sources/Montage/1 Components/4 Contents/Card.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public struct Card: View {
.frame(maxWidth: thumbnailWidth, alignment: .leading)
}
}
.accessibilityElement(children: .combine)
}

private var horizontalPadding: CGFloat = 2
Expand Down
1 change: 1 addition & 0 deletions Sources/Montage/1 Components/4 Contents/ListCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,6 @@ public struct ListCard: View {

trailingContent()
}
.accessibilityElement(children: .combine)
}
}
2 changes: 2 additions & 0 deletions Sources/Montage/1 Components/4 Contents/ListCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public struct ListCell: View {
.allowsHitTesting(disable == false)
.opacity(disable ? 0.43 : 1)
.modifier(PressActionDetectingModifier(isPressed: $isPressed, action: onTap))
.accessibilityElement(children: .combine)
.if(onTap != nil) { $0.accessibilityAddTraits(.isButton) }
}

// MARK: - Modifiers
Expand Down
1 change: 1 addition & 0 deletions Sources/Montage/1 Components/5 Loading/Loading.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public struct Loading: View {
$0
}
}
.accessibilityLabel(String(localized: "로딩 중", bundle: .module))
}

struct LoadingViewModifier: ViewModifier {
Expand Down
1 change: 1 addition & 0 deletions Sources/Montage/1 Components/5 Loading/Skeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public enum Skeleton {
}
.foregroundStyle(color)
.opacity(opacity)
.accessibilityLabel(String(localized: "로딩 중", bundle: .module))
}

// MARK: - Modifiers
Expand Down
3 changes: 3 additions & 0 deletions Sources/Montage/1 Components/6 Navigations/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ public struct Tab: View {
}
}
.contentShape(Rectangle())
.accessibilityAddTraits(.isButton)
.accessibilityAddTraits(index == selectedIndex ? .isSelected : [])
.accessibilityRemoveTraits(index == selectedIndex ? [] : .isSelected)
.onTapGesture {
guard !itemDisabled(index) else { return }
withAnimation(animation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ extension TopNavigation {
IconButton(icon: .chevronLeft) {
action()
}
.accessibilityLabel(String(localized: "뒤로 가기", bundle: .module))
.frame(width: 24, height: 24)
case let .icon(i, action):
IconButton(icon: i) {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Montage/1 Components/7 Feedback/Toast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ public struct Toast: View, KeyboardReadable {
BackgroundView()
)
.clipShape(RoundedRectangle(cornerRadius: 12))
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isStaticText)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ struct TooltipView: View {
.frame(maxWidth: 280)
.fixedSize(horizontal: true, vertical: false)
.offset(x: offsetHorizontal, y: offsetVertical)
.accessibilityLabel(message)
}

var arrowPadding: EdgeInsets {
Expand Down
Loading
Loading