diff --git a/Controllers/Controllers/Sources/Tester/TesterView.swift b/Controllers/Controllers/Sources/Tester/TesterView.swift index 5c65aab..8f106d5 100644 --- a/Controllers/Controllers/Sources/Tester/TesterView.swift +++ b/Controllers/Controllers/Sources/Tester/TesterView.swift @@ -46,12 +46,14 @@ struct TesterView: View { } Spacer() - .frame(height: 60) - ZStack { + .frame(height: 12) + // offset 은 레이아웃에 반영되지 않아 스틱이 카드 밖(iOS 26 탭바 아래)으로 나갔음. + // 음수 spacing 으로 겹치게 배치해 카드 높이에 포함시킨다. + VStack(spacing: -50) { // 중앙 영역 - HStack(spacing: 40) { + // D-패드·얼굴 버튼을 양 끝(L1/R1 아래)에 두고, 가운데 남는 폭에 스틱 + PS 버튼이 겹쳐 들어간다. + HStack { // D-패드 - Spacer() DPadView(xValue: model.dpadX, yValue: model.dpadY) Spacer() // 얼굴 버튼 @@ -63,19 +65,17 @@ struct TesterView: View { } ControllerButton(systemName: "x.circle", size: 60, isPressed: model.buttonA) } - Spacer() } - .offset(y: -20) - + .padding(.horizontal, 24) + // 하단 버튼 - HStack(spacing: 40) { + HStack(spacing: 24) { // 왼쪽 스틱 (옵션) ThumbstickView(direction: .left, xValue: model.leftStickX, yValue: model.leftStickY) ControllerButton(systemName: "playstation.logo", size: 60, isPressed: model.buttonHome) // 오른쪽 스틱 (옵션) ThumbstickView(direction: .right, xValue: model.rightStickX, yValue: model.rightStickY) } - .offset(y: 60) } } diff --git a/Controllers/ControllersWidget/ControllerLiveActivity.swift b/Controllers/ControllersWidget/ControllerLiveActivity.swift index 673a471..b3d065c 100644 --- a/Controllers/ControllersWidget/ControllerLiveActivity.swift +++ b/Controllers/ControllersWidget/ControllerLiveActivity.swift @@ -26,17 +26,13 @@ struct ControllerLiveActivity: Widget { } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { - Label { - Text(context.attributes.vendorName) - .font(.caption) - .lineLimit(1) - } icon: { - Image(systemName: "gamecontroller.fill") - } + // 이름은 bottom 진행 바 라벨에 한 번만 표시한다 (leading 은 폭이 좁아 잘림) + Image(systemName: "gamecontroller.fill") + .font(.title3) } DynamicIslandExpandedRegion(.trailing) { Label { - Text(LiveActivityFormat.percentText(context.state.batteryLevel)) + LiveActivityFormat.percentLabel(context.state) .font(.title3) .fontWeight(.semibold) } icon: { @@ -55,10 +51,11 @@ struct ControllerLiveActivity: Widget { } compactLeading: { Image(systemName: "gamecontroller.fill") } compactTrailing: { - Text(LiveActivityFormat.percentText(context.state.batteryLevel)) + LiveActivityFormat.percentLabel(context.state) .foregroundColor(LiveActivityFormat.batteryColor(context.state.batteryLevel)) } minimal: { - Image(systemName: LiveActivityFormat.batteryIconName(for: context.state)) + // 공간이 아이콘 하나뿐이라 충전 중에는 번개, 아니면 잔량 아이콘 + Image(systemName: context.state.batteryState == 1 ? "bolt.fill" : LiveActivityFormat.batteryIconName(for: context.state)) .foregroundColor(LiveActivityFormat.batteryColor(context.state.batteryLevel)) } } @@ -83,6 +80,7 @@ struct LockScreenLiveActivityView: View { .fontWeight(.semibold) .foregroundColor(.white) .lineLimit(1) + .minimumScaleFactor(0.7) ProgressView(value: Double(state.batteryLevel)) .tint(LiveActivityFormat.batteryColor(state.batteryLevel)) @@ -92,7 +90,7 @@ struct LockScreenLiveActivityView: View { HStack(spacing: 4) { Image(systemName: LiveActivityFormat.batteryIconName(for: state)) - Text(LiveActivityFormat.percentText(state.batteryLevel)) + LiveActivityFormat.percentLabel(state) .fontWeight(.semibold) } .font(.headline) @@ -109,12 +107,15 @@ enum LiveActivityFormat { "\(Int(level * 100))%" } - /// 배터리 잔량/상태에 맞는 SF Symbol 이름을 반환한다. + /// "82%" 텍스트. 충전 중(batteryState == 1)이면 번개 아이콘을 붙인다. + static func percentLabel(_ state: ControllerActivityAttributes.ContentState) -> Text { + let text = Text(percentText(state.batteryLevel)) + return state.batteryState == 1 ? text + Text(" \(Image(systemName: "bolt.fill"))") : text + } + + /// 배터리 잔량에 맞는 SF Symbol 이름을 반환한다. + /// (충전 중 표시는 percentLabel 의 번개로 한다. battery.100.bolt 는 잔량과 무관하게 가득 찬 모양이라 쓰지 않음) static func batteryIconName(for state: ControllerActivityAttributes.ContentState) -> String { - // batteryState == 1 (charging) - if state.batteryState == 1 { - return "battery.100.bolt" - } switch state.batteryLevel { case ..<0.1: return "battery.0" case ..<0.25: return "battery.25"