Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Draft
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
12 changes: 12 additions & 0 deletions 12 TASK_BREAKDOWN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Matching Game Home Tasks

1. **建立 SwiftUI 首頁 (進行中/完成)**
- `MatchingGameHomeView` 內含 TabView 與自訂圓角分頁列
- `GameSetupTab` 負責標題、棋盤尺寸選擇與開始按鈕
- `ResultsTab` 目前提供靜態範例資料,後續可串接實際統計

2. **樣式與互動待辦**
- 如需動畫切換或真實資料流,可在 `RoundedTabBar` 與 `ResultsTab` 補上實際邏輯
- `BoardSizePicker` 可串接遊戲邏輯,觸發重新配置牌面

> 完成任務後,記得回到此檔案檢閱整體結構,以利後續調整。
16 changes: 16 additions & 0 deletions 16 ios/ContentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SwiftUI

/// App entry point view that simply hosts the custom matching game home.
struct ContentView: View {
var body: some View {
MatchingGameHomeView()
}
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
229 changes: 229 additions & 0 deletions 229 ios/MatchingGameHome.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import SwiftUI

/// Entry view that reproduces the Matching Game home UI with a rounded tab bar.
struct MatchingGameHomeView: View {
@State private var selectedTab: HomeTab = .game
@State private var boardSize: BoardSize = .twoByThree

var body: some View {
ZStack(alignment: .bottom) {
TabView(selection: $selectedTab) {
GameSetupTab(boardSize: $boardSize)
.tag(HomeTab.game)

ResultsTab()
.tag(HomeTab.results)
}
.tabViewStyle(.page(indexDisplayMode: .never))

RoundedTabBar(selection: $selectedTab)
.padding(.bottom, 32)
}
.background(Color(.systemGroupedBackground))
.ignoresSafeArea(.all, edges: .bottom)
}
}

// MARK: - Tabs

private struct GameSetupTab: View {
@Binding var boardSize: BoardSize

var body: some View {
VStack(alignment: .leading, spacing: 32) {
Spacer().frame(height: 24)

Text("Matching Game")
.font(.system(size: 34, weight: .bold))
.padding(.top, 16)

BoardSizePicker(current: $boardSize)

Button {
// Hook up to real start-game action.
} label: {
Text("Start Game")
.font(.headline)
.foregroundStyle(Color(.systemBlue))
.padding(.horizontal, 32)
.padding(.vertical, 12)
.background(
Capsule()
.fill(Color(.systemBlue).opacity(0.08))
)
}

Spacer()
}
.padding(.horizontal, 28)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.background(Color(.systemBackground))
}
}

private struct ResultsTab: View {
let mockResults = [
ResultEntry(title: "Today", score: "08 turns", subtitle: "2x3 • 45s"),
ResultEntry(title: "Best streak", score: "06 turns", subtitle: "3x4 • 39s"),
ResultEntry(title: "Average", score: "11 turns", subtitle: "All boards")
]

var body: some View {
VStack(alignment: .leading, spacing: 24) {
Spacer().frame(height: 24)

Text("Results")
.font(.system(size: 34, weight: .bold))
.padding(.top, 16)

ForEach(mockResults) { result in
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(result.title)
.font(.headline)
Spacer()
Text(result.score)
.font(.title3.weight(.semibold))
}
Text(result.subtitle)
.font(.subheadline)
.foregroundStyle(.secondary)
}
.padding(18)
.frame(maxWidth: .infinity)
.background(
RoundedRectangle(cornerRadius: 20)
.fill(Color(.secondarySystemBackground))
)
}

Spacer()
}
.padding(.horizontal, 28)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.background(Color(.systemBackground))
}
}

// MARK: - Components

private struct BoardSizePicker: View {
@Binding var current: BoardSize

var body: some View {
HStack(spacing: 8) {
ForEach(BoardSize.allCases) { size in
Text(size.title)
.font(.headline)
.foregroundStyle(current == size ? Color(.systemBackground) : .primary)
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.background(
Capsule()
.fill(current == size ? Color(.systemBlue) : Color.clear)
)
.clipShape(Capsule())
.onTapGesture {
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
current = size
}
}
}
}
.padding(6)
.background(
Capsule()
.fill(Color(.secondarySystemBackground))
.shadow(color: Color.black.opacity(0.05), radius: 8, y: 4)
)
}
}

private struct RoundedTabBar: View {
@Binding var selection: HomeTab

var body: some View {
HStack(spacing: 0) {
ForEach(HomeTab.allCases) { tab in
Button {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
selection = tab
}
} label: {
VStack(spacing: 6) {
Image(systemName: tab.icon)
.font(.system(size: 18, weight: .semibold))
Text(tab.title)
.font(.subheadline.weight(.semibold))
}
.foregroundStyle(selection == tab ? Color(.systemBlue) : Color(.tertiaryLabel))
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
.background(
Group {
if selection == tab {
Capsule()
.fill(Color(.systemBlue).opacity(0.12))
}
}
)
}
}
}
.padding(.horizontal, 8)
.padding(.vertical, 6)
.background(
Capsule()
.fill(Color(.systemBackground))
.shadow(color: Color.black.opacity(0.15), radius: 16, y: 8)
)
.padding(.horizontal, 32)
}
}

// MARK: - Models & Helpers

private enum HomeTab: CaseIterable, Identifiable {
case game
case results

var id: String { title }

var title: String {
switch self {
case .game: return "Game"
case .results: return "Results"
}
}

var icon: String {
switch self {
case .game: return "gamecontroller.fill"
case .results: return "chart.bar.fill"
}
}
}

private enum BoardSize: String, CaseIterable, Identifiable {
case twoByThree = "2x3"
case threeByFour = "3x4"

var id: String { rawValue }

var title: String { rawValue }
}

private struct ResultEntry: Identifiable {
let id = UUID()
let title: String
let score: String
let subtitle: String
}

#if DEBUG
struct MatchingGameHomeView_Previews: PreviewProvider {
static var previews: some View {
MatchingGameHomeView()
}
}
#endif
Morty Proxy This is a proxified and sanitized view of the page, visit original site.