Init project with windsurf

This commit is contained in:
2025-03-01 20:39:34 +11:00
parent 7aac0e6859
commit 19fb51407c
9 changed files with 917 additions and 6 deletions
+117
View File
@@ -0,0 +1,117 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
## Obj-C/Swift specific
*.hmap
## App packaging
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm
.build/
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build/
# Accio dependency management
Dependencies/
.accio/
# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
# macOS specific files
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
+37
View File
@@ -0,0 +1,37 @@
# Image Flow
A beautiful iOS app that combines your photos with stylish clock displays for a personalized fullscreen slideshow experience.
## Features
- Select multiple photos from your photo library
- Choose from different clock styles (Digital, Analog, Minimal)
- Select transition animations between photos (Fade, Slide, Zoom)
- Fullscreen slideshow mode with automatic photo transitions every 10 seconds
- Tap to exit fullscreen mode
- Device stays awake during slideshow
## Requirements
- iOS 16.0+
- Xcode 15.0+
- Swift 5.9+
## Installation
1. Clone the repository
2. Open the project in Xcode
3. Build and run on your iOS device or simulator
## Usage
1. Launch the app
2. Tap "Select Photos" to choose images from your photo library
3. Select a clock style (Digital, Analog, or Minimal)
4. Choose a transition animation (Fade, Slide, or Zoom)
5. Tap "Start Slideshow" to enter fullscreen mode
6. Tap anywhere on the screen to exit fullscreen mode
## Privacy
The app requires access to your photo library to display selected images in the slideshow. Photos are only accessed within the app and are not shared or stored externally.
+153
View File
@@ -0,0 +1,153 @@
//
// ClockPreviewView.swift
// image-flow
//
// Created by Junv on 1/3/2025.
//
import SwiftUI
struct ClockPreviewView: View {
let clockStyle: ClockStyle
@State private var currentTime = Date()
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue.opacity(0.2))
.frame(height: 100)
Group {
switch clockStyle {
case .digital:
digitalPreview
case .analog:
analogPreview
case .minimal:
minimalPreview
}
}
}
.onReceive(timer) { _ in
currentTime = Date()
}
}
private var digitalPreview: some View {
VStack(spacing: 2) {
Text(timeFormatter.string(from: currentTime))
.font(.system(size: 28, weight: .bold, design: .rounded))
.foregroundColor(.primary)
Text(dateFormatter.string(from: currentTime))
.font(.system(size: 12, weight: .medium, design: .rounded))
.foregroundColor(.secondary)
}
}
private var analogPreview: some View {
ZStack {
// Clock face
Circle()
.fill(Color.white)
.frame(width: 70, height: 70)
.shadow(radius: 2)
// Hour markers
ForEach(0..<12) { hour in
Rectangle()
.fill(Color.black)
.frame(width: 2, height: hour % 3 == 0 ? 6 : 3)
.offset(y: -30)
.rotationEffect(.degrees(Double(hour) * 30))
}
// Hour hand
Rectangle()
.fill(Color.black)
.frame(width: 2, height: 20)
.cornerRadius(1)
.offset(y: -10)
.rotationEffect(.degrees(Double(hour) * 360))
// Minute hand
Rectangle()
.fill(Color.black)
.frame(width: 1.5, height: 28)
.cornerRadius(0.75)
.offset(y: -14)
.rotationEffect(.degrees(Double(minute) * 360))
// Second hand
Rectangle()
.fill(Color.red)
.frame(width: 1, height: 30)
.cornerRadius(0.5)
.offset(y: -15)
.rotationEffect(.degrees(Double(second) * 360))
// Center cap
Circle()
.fill(Color.black)
.frame(width: 4, height: 4)
}
}
private var minimalPreview: some View {
Text(minimalTimeFormatter.string(from: currentTime))
.font(.system(size: 36, weight: .thin, design: .rounded))
.foregroundColor(.primary)
}
// MARK: - Helper Properties
private var timeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}
private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE, MMM d"
return formatter
}
private var minimalTimeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
return formatter
}
private var calendar: Calendar {
return Calendar.current
}
private var hour: CGFloat {
let hour = CGFloat(calendar.component(.hour, from: currentTime) % 12)
let minute = CGFloat(calendar.component(.minute, from: currentTime))
return (hour + minute / 60) / 12
}
private var minute: CGFloat {
let minute = CGFloat(calendar.component(.minute, from: currentTime))
let second = CGFloat(calendar.component(.second, from: currentTime))
return (minute + second / 60) / 60
}
private var second: CGFloat {
let second = CGFloat(calendar.component(.second, from: currentTime))
return second / 60
}
}
#Preview {
VStack(spacing: 20) {
ClockPreviewView(clockStyle: .digital)
ClockPreviewView(clockStyle: .analog)
ClockPreviewView(clockStyle: .minimal)
}
.padding()
}
+202 -6
View File
@@ -6,16 +6,212 @@
//
import SwiftUI
import PhotosUI
struct ContentView: View {
@StateObject private var photoManager = PhotoPickerManager()
@State private var clockStyle: ClockStyle = .digital
@State private var isFullscreen = false
@State private var currentImageIndex = 0
@State private var transitionAnimation: TransitionAnimation = .fade
@State private var showingPermissionAlert = false
let timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
if isFullscreen {
SlideshowView(
images: photoManager.selectedImages,
clockStyle: clockStyle,
currentIndex: $currentImageIndex,
animation: transitionAnimation
)
.statusBar(hidden: true)
.ignoresSafeArea()
.onTapGesture {
isFullscreen = false
}
.onReceive(timer) { _ in
withAnimation {
currentImageIndex = (currentImageIndex + 1) % max(photoManager.selectedImages.count, 1)
}
}
} else {
NavigationStack {
List {
Section("Select Photos") {
PhotosPicker(
selection: $photoManager.selectedItems,
matching: .images,
photoLibrary: .shared()
) {
HStack {
Image(systemName: "photo.on.rectangle.angled")
Text("Select Photos")
}
}
.onChange(of: photoManager.selectedItems) { _ in
Task {
await photoManager.loadImages()
}
}
if photoManager.isLoading {
HStack {
Spacer()
ProgressView()
.padding()
Spacer()
}
} else if !photoManager.selectedImages.isEmpty {
ScrollView(.horizontal) {
LazyHStack {
ForEach(0..<photoManager.selectedImages.count, id: \.self) { index in
Image(uiImage: photoManager.selectedImages[index])
.resizable()
.scaledToFill()
.frame(width: 100, height: 100)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
.padding(.vertical, 8)
}
.frame(height: 120)
Button(action: {
photoManager.clearSelection()
}) {
Text("Clear Selection")
.foregroundColor(.red)
}
}
}
Section("Clock Style") {
Picker("Clock Style", selection: $clockStyle) {
ForEach(ClockStyle.allCases, id: \.self) { style in
Text(style.displayName).tag(style)
}
}
.pickerStyle(.segmented)
ClockPreviewView(clockStyle: clockStyle)
.padding(.vertical, 8)
}
Section("Transition Animation") {
Picker("Animation", selection: $transitionAnimation) {
ForEach(TransitionAnimation.allCases, id: \.self) { animation in
Text(animation.displayName).tag(animation)
}
}
.pickerStyle(.segmented)
// Animation preview
HStack {
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
Image(systemName: getTransitionIcon())
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
Image(systemName: "photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
}
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
}
Section {
Button(action: {
if !photoManager.selectedImages.isEmpty {
Task {
let status = photoManager.checkPhotoLibraryPermission()
if status == .authorized {
isFullscreen = true
} else if status == .notDetermined {
let newStatus = await photoManager.requestPhotoLibraryPermission()
if newStatus == .authorized {
isFullscreen = true
} else {
showingPermissionAlert = true
}
} else {
showingPermissionAlert = true
}
}
}
}) {
Text("Start Slideshow")
.frame(maxWidth: .infinity)
.padding()
.background(photoManager.selectedImages.isEmpty ? Color.gray : Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.disabled(photoManager.selectedImages.isEmpty)
.listRowInsets(EdgeInsets())
.padding(.vertical, 8)
}
}
.navigationTitle("Image Flow")
.alert("Photo Library Access", isPresented: $showingPermissionAlert) {
Button("OK", role: .cancel) { }
Button("Open Settings") {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
} message: {
Text("Please allow access to your photo library in Settings to use this feature.")
}
}
}
}
private func getTransitionIcon() -> String {
switch transitionAnimation {
case .fade:
return "arrow.left.and.right.square"
case .slide:
return "arrow.right.square"
case .zoom:
return "arrow.up.left.and.arrow.down.right"
}
}
}
enum ClockStyle: String, CaseIterable {
case digital
case analog
case minimal
var displayName: String {
switch self {
case .digital: return "Digital"
case .analog: return "Analog"
case .minimal: return "Minimal"
}
}
}
enum TransitionAnimation: String, CaseIterable {
case fade
case slide
case zoom
var displayName: String {
switch self {
case .fade: return "Fade"
case .slide: return "Slide"
case .zoom: return "Zoom"
}
.padding()
}
}
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to your photo library to display selected images in the slideshow.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
</dict>
</plist>
+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="gfx-5g-7RV">
<rect key="frame" x="96.666666666666686" y="363.66666666666669" width="200" height="125"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="photo.on.rectangle.angled" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="XYw-Iq-Fzd">
<rect key="frame" x="50" y="0.6666666666666643" width="100" height="79"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="Lfg-Yd-Xvf"/>
<constraint firstAttribute="height" constant="80" id="eTY-5h-GRb"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Image Flow" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zzh-Gu-Hkl">
<rect key="frame" x="34" y="96" width="132" height="29"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="24"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="200" id="Ygf-Oe-lYL"/>
</constraints>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBlueColor"/>
<constraints>
<constraint firstItem="gfx-5g-7RV" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="Qjw-Gy-Ufr"/>
<constraint firstItem="gfx-5g-7RV" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="pNV-Yd-xgK"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="photo.on.rectangle.angled" catalog="system" width="128" height="98"/>
<systemColor name="systemBlueColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
+84
View File
@@ -0,0 +1,84 @@
//
// PhotoPickerManager.swift
// image-flow
//
// Created by Junv on 1/3/2025.
//
import SwiftUI
import PhotosUI
class PhotoPickerManager: ObservableObject {
@Published var selectedItems: [PhotosPickerItem] = []
@Published var selectedImages: [UIImage] = []
@Published var isLoading = false
func loadImages() async {
DispatchQueue.main.async {
self.isLoading = true
}
var newImages: [UIImage] = []
for item in selectedItems {
if let data = try? await item.loadTransferable(type: Data.self),
let uiImage = UIImage(data: data) {
newImages.append(uiImage)
}
}
DispatchQueue.main.async {
self.selectedImages = newImages
self.isLoading = false
}
}
func clearSelection() {
selectedItems = []
selectedImages = []
}
}
// Extension to check and request photo library permission
extension PhotoPickerManager {
enum PhotoLibraryPermissionStatus {
case authorized
case denied
case restricted
case notDetermined
}
func checkPhotoLibraryPermission() -> PhotoLibraryPermissionStatus {
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
switch status {
case .authorized, .limited:
return .authorized
case .denied:
return .denied
case .restricted:
return .restricted
case .notDetermined:
return .notDetermined
@unknown default:
return .notDetermined
}
}
func requestPhotoLibraryPermission() async -> PhotoLibraryPermissionStatus {
let status = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
switch status {
case .authorized, .limited:
return .authorized
case .denied:
return .denied
case .restricted:
return .restricted
case .notDetermined:
return .notDetermined
@unknown default:
return .notDetermined
}
}
}
+235
View File
@@ -0,0 +1,235 @@
//
// SlideshowView.swift
// image-flow
//
// Created by Junv on 1/3/2025.
//
import SwiftUI
struct SlideshowView: View {
let images: [UIImage]
let clockStyle: ClockStyle
@Binding var currentIndex: Int
let animation: TransitionAnimation
@State private var currentTime = Date()
@State private var previousIndex: Int?
@State private var isAnimating = false
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
// Background image
if !images.isEmpty {
GeometryReader { geometry in
ZStack {
// Current image
Image(uiImage: images[currentIndex])
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
.transition(transitionEffect())
// Overlay gradient for better clock visibility
LinearGradient(
gradient: Gradient(colors: [Color.black.opacity(0.4), Color.clear]),
startPoint: .top,
endPoint: .center
)
}
}
.ignoresSafeArea()
.animation(.easeInOut(duration: 1.0), value: currentIndex)
}
// Clock overlay
VStack {
clockView
.padding(.top, 50)
Spacer()
// Image counter
if images.count > 1 {
Text("\(currentIndex + 1) / \(images.count)")
.font(.caption)
.padding(8)
.background(Color.black.opacity(0.6))
.foregroundColor(.white)
.cornerRadius(8)
.padding(.bottom, 20)
}
}
}
.onReceive(timer) { _ in
currentTime = Date()
}
}
private var clockView: some View {
Group {
switch clockStyle {
case .digital:
DigitalClockView(currentTime: currentTime)
case .analog:
AnalogClockView(currentTime: currentTime)
case .minimal:
MinimalClockView(currentTime: currentTime)
}
}
}
private func transitionEffect() -> AnyTransition {
switch animation {
case .fade:
return AnyTransition.opacity
case .slide:
return AnyTransition.asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
)
case .zoom:
return AnyTransition.scale(scale: 0.8).combined(with: .opacity)
}
}
}
struct DigitalClockView: View {
let currentTime: Date
private var timeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
return formatter
}
private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EEEE, MMMM d, yyyy"
return formatter
}
var body: some View {
VStack(spacing: 4) {
Text(timeFormatter.string(from: currentTime))
.font(.system(size: 60, weight: .bold, design: .rounded))
.foregroundColor(.white)
.shadow(color: .black.opacity(0.5), radius: 2, x: 0, y: 1)
Text(dateFormatter.string(from: currentTime))
.font(.system(size: 20, weight: .medium, design: .rounded))
.foregroundColor(.white)
.shadow(color: .black.opacity(0.5), radius: 2, x: 0, y: 1)
}
.padding()
.background(Color.black.opacity(0.3))
.cornerRadius(16)
}
}
struct AnalogClockView: View {
let currentTime: Date
private var calendar: Calendar {
return Calendar.current
}
private var hour: CGFloat {
let hour = CGFloat(calendar.component(.hour, from: currentTime) % 12)
let minute = CGFloat(calendar.component(.minute, from: currentTime))
return (hour + minute / 60) / 12
}
private var minute: CGFloat {
let minute = CGFloat(calendar.component(.minute, from: currentTime))
let second = CGFloat(calendar.component(.second, from: currentTime))
return (minute + second / 60) / 60
}
private var second: CGFloat {
let second = CGFloat(calendar.component(.second, from: currentTime))
return second / 60
}
var body: some View {
ZStack {
// Clock face
Circle()
.fill(Color.black.opacity(0.5))
.frame(width: 200, height: 200)
.overlay(
Circle()
.stroke(Color.white, lineWidth: 4)
)
// Hour markers
ForEach(0..<12) { hour in
Rectangle()
.fill(Color.white)
.frame(width: 4, height: hour % 3 == 0 ? 15 : 8)
.offset(y: -85)
.rotationEffect(.degrees(Double(hour) * 30))
}
// Hour hand
Rectangle()
.fill(Color.white)
.frame(width: 4, height: 60)
.cornerRadius(2)
.offset(y: -30)
.rotationEffect(.degrees(Double(hour) * 360))
// Minute hand
Rectangle()
.fill(Color.white)
.frame(width: 3, height: 80)
.cornerRadius(1.5)
.offset(y: -40)
.rotationEffect(.degrees(Double(minute) * 360))
// Second hand
Rectangle()
.fill(Color.red)
.frame(width: 1, height: 90)
.cornerRadius(0.5)
.offset(y: -45)
.rotationEffect(.degrees(Double(second) * 360))
// Center cap
Circle()
.fill(Color.white)
.frame(width: 12, height: 12)
}
.frame(width: 220, height: 220)
.shadow(color: .black.opacity(0.5), radius: 5, x: 0, y: 2)
}
}
struct MinimalClockView: View {
let currentTime: Date
private var timeFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
return formatter
}
var body: some View {
Text(timeFormatter.string(from: currentTime))
.font(.system(size: 80, weight: .thin, design: .rounded))
.foregroundColor(.white)
.shadow(color: .black.opacity(0.5), radius: 2, x: 0, y: 1)
}
}
#Preview {
SlideshowView(
images: [UIImage(systemName: "photo")!],
clockStyle: .digital,
currentIndex: .constant(0),
animation: .fade
)
}
+11
View File
@@ -6,12 +6,23 @@
//
import SwiftUI
import UIKit
@main
struct image_flowApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Prevent screen from dimming during slideshow
UIApplication.shared.isIdleTimerDisabled = true
return true
}
}