Files
2026-01-14 00:16:06 +09:00

152 lines
6.6 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
private enum InfoTab: String, CaseIterable {
case preference = "Preference"
case publish = "Publish"
}
struct InfoGuideView: View {
@Binding var showingInfo: Bool
@State private var selectedTab: InfoTab = .preference
var body: some View {
NavigationView {
VStack(spacing: 0) {
Picker("", selection: $selectedTab) {
ForEach(InfoTab.allCases, id: \.self) { tab in
Text(tab.rawValue).tag(tab)
}
}
.pickerStyle(.segmented)
.padding()
.padding(.top, 8)
TabView(selection: $selectedTab) {
PreferenceGuideList()
.tag(InfoTab.preference)
PublishGuideList()
.tag(InfoTab.publish)
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.navigationTitle("Help")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Done") { showingInfo = false }
}
}
}
}
}
private struct PreferenceGuideList: View {
var body: some View {
List {
Section("Stream Settings") {
GuideRow(title: "URL", description: "RTMP server address (e.g., rtmp://your-server.com/live)")
GuideRow(title: "Stream Name", description: "Unique stream key provided by your streaming platform")
}
Section("Audio Settings") {
GuideRow(title: "Format", description: "AAC: Universal compatibility\nOpus: Better quality at low bitrates")
}
Section("Video Settings") {
GuideRow(title: "HDR Video", description: "Captures wider color/brightness range. Requires HDR-capable camera.")
GuideRow(title: "Low Latency", description: "Reduces stream delay to ~2-3 seconds. May slightly reduce quality.")
GuideRow(title: "BitRate Mode", description: "Average: Consistent file size\nConstant: Stable quality\nVariable: Best quality")
}
Section("Capture Settings") {
GuideRow(title: "Preview Type", description: "Metal: Fast GPU-based preview.\nSystem PiP: Enables background streaming. When you switch apps, receive a phone call, or go to home screen, your stream continues in a floating window instead of dying.")
GuideRow(title: "Audio Capture", description: "AudioEngine: Most stable\nAudioSource: Direct capture\nStereo: For external mics")
GuideRow(title: "GPU Rendering", description: "Uses GPU for video effects. Disable if experiencing issues.")
}
Section("Debug") {
GuideRow(title: "Memory Release Test", description: "Opens PublishView in a sheet to verify memory is properly released when dismissed to help detect memory leaks.")
}
}
}
}
private struct PublishGuideList: View {
var body: some View {
List {
Section("Stream Settings") {
GuideRowWithIcon(icon: "15", isText: true, title: "FPS",
description: "Frames per second. 15 saves battery, 30 is standard, 60 is ultra-smooth.")
GuideRowWithIcon(icon: "slider.horizontal.3", title: "Bitrate (kbps)",
description: "Video quality. Higher = better but more data. 1500-2500 recommended.")
GuideRowWithIcon(icon: "rectangle.badge.checkmark", title: "720p",
description: "Video resolution (1280×720). Good balance of quality and performance.")
}
Section("Controls") {
GuideRowWithIcon(icon: "record.circle", title: "Record",
description: "Save a local copy to Photos. Only available while streaming.")
GuideRowWithIcon(icon: "mic.fill", title: "Mute",
description: "Mute/unmute microphone. Red when muted.")
GuideRowWithIcon(icon: "arrow.triangle.2.circlepath.camera", title: "Flip Camera",
description: "Switch between front and back cameras.")
GuideRowWithIcon(icon: "flashlight.on.fill", title: "Torch",
description: "Toggle flashlight. Only works with back camera.")
GuideRowWithIcon(icon: "rectangle.on.rectangle", title: "Dual Camera",
description: "Overlay the other camera in your stream. Viewers see both cameras.")
}
Section("Live Stats") {
GuideRowWithIcon(icon: "arrow.up", title: "Upload Speed",
description: "Current upload rate in KB/s. The graph shows last 60 seconds.")
GuideRowWithIcon(icon: "thermometer.medium", title: "Temperature",
description: "Device thermal state. Lower FPS/bitrate if too hot.")
}
}
}
}
private struct GuideRow: View {
let title: String
let description: String
var body: some View {
VStack(alignment: .leading, spacing: 4) {
Text(title).font(.headline)
Text(description)
.font(.caption)
.foregroundColor(.secondary)
}
.padding(.vertical, 2)
}
}
private struct GuideRowWithIcon: View {
let icon: String
var isText: Bool = false
let title: String
let description: String
var body: some View {
HStack(alignment: .top, spacing: 12) {
if isText {
Text(icon)
.font(.system(size: 14, weight: .bold))
.foregroundColor(.cyan)
.frame(width: 28, height: 28)
.background(Color.cyan.opacity(0.2))
.cornerRadius(6)
} else {
Image(systemName: icon)
.font(.system(size: 16))
.foregroundColor(.cyan)
.frame(width: 28, height: 28)
.background(Color.cyan.opacity(0.2))
.cornerRadius(6)
}
VStack(alignment: .leading, spacing: 2) {
Text(title)
.font(.subheadline.weight(.medium))
Text(description)
.font(.caption)
.foregroundColor(.secondary)
}
}
.padding(.vertical, 4)
}
}