added ios module
This commit is contained in:
143
ios/OralTrainer/Views/RootView.swift
Normal file
143
ios/OralTrainer/Views/RootView.swift
Normal file
@@ -0,0 +1,143 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct RootView: View {
|
||||
@StateObject private var controller = PlayerController()
|
||||
@StateObject private var catalogVM = CatalogViewModel()
|
||||
@State private var activeModule: Module = .train
|
||||
@State private var continuousPlayback = false
|
||||
@State private var showImporter = false
|
||||
@State private var showFullscreenPlayer = false
|
||||
|
||||
enum Module {
|
||||
case train
|
||||
case test
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
VStack(spacing: 0) {
|
||||
header
|
||||
moduleTabs
|
||||
if activeModule == .train {
|
||||
TrainingView(
|
||||
controller: controller,
|
||||
catalogVM: catalogVM,
|
||||
continuousPlayback: $continuousPlayback,
|
||||
onFullscreen: enterFullscreen
|
||||
)
|
||||
} else {
|
||||
TestView(controller: controller, onFullscreen: enterFullscreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
.onAppear {
|
||||
catalogVM.controller = controller
|
||||
controller.setContinuousPlayback(continuousPlayback)
|
||||
}
|
||||
.onChange(of: activeModule) { module in
|
||||
if module == .train {
|
||||
controller.setContinuousPlayback(continuousPlayback)
|
||||
} else {
|
||||
controller.setContinuousPlayback(true)
|
||||
}
|
||||
}
|
||||
.fileImporter(
|
||||
isPresented: $showImporter,
|
||||
allowedContentTypes: [.movie],
|
||||
allowsMultipleSelection: false
|
||||
) { result in
|
||||
if case .success(let urls) = result, let url = urls.first {
|
||||
Task {
|
||||
await catalogVM.importVideo(at: url)
|
||||
}
|
||||
}
|
||||
}
|
||||
.fullScreenCover(isPresented: $showFullscreenPlayer) {
|
||||
FullscreenPlayerView(controller: controller) {
|
||||
exitFullscreen()
|
||||
}
|
||||
.onAppear(perform: enterFullscreen)
|
||||
}
|
||||
}
|
||||
|
||||
private var header: some View {
|
||||
HStack(spacing: 0) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("跟读虫")
|
||||
.font(.system(size: 32, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
Text("外语跟读训练神器")
|
||||
.font(.system(size: 15))
|
||||
.foregroundColor(Theme.textMuted)
|
||||
}
|
||||
Spacer()
|
||||
headerButton("导入") {
|
||||
showImporter = true
|
||||
}
|
||||
headerButton("刷新") {
|
||||
Task {
|
||||
await catalogVM.load()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 18)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
|
||||
private func headerButton(_ label: String, action: @escaping () -> Void) -> some View {
|
||||
Button(action: action) {
|
||||
Text(label)
|
||||
.font(.system(size: 14, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 14)
|
||||
.frame(height: 40)
|
||||
.background(Theme.buttonBlue)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.leading, 8)
|
||||
}
|
||||
|
||||
private var moduleTabs: some View {
|
||||
HStack(spacing: 0) {
|
||||
moduleTab("训练", module: .train)
|
||||
moduleTab("测试", module: .test)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
private func moduleTab(_ label: String, module: Module) -> some View {
|
||||
let selected = activeModule == module
|
||||
return Button {
|
||||
activeModule = module
|
||||
} label: {
|
||||
Text(label)
|
||||
.font(.system(size: 15, weight: .bold))
|
||||
.foregroundColor(selected ? .white : Theme.textMuted)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 38)
|
||||
.background(selected ? Theme.buttonBlue : Theme.surface)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(2)
|
||||
}
|
||||
|
||||
private func enterFullscreen() {
|
||||
AppDelegate.orientationLock = UIDevice.current.userInterfaceIdiom == .pad ? .all : .landscape
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
showFullscreenPlayer = true
|
||||
}
|
||||
|
||||
private func exitFullscreen() {
|
||||
AppDelegate.orientationLock = UIDevice.current.userInterfaceIdiom == .pad ? .all : .portrait
|
||||
UIViewController.attemptRotationToDeviceOrientation()
|
||||
showFullscreenPlayer = false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user