@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic1.jpg", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic2.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 275 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic3.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 202 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic4.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 137 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic5.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 134 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic6.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 130 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic7.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 50 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic8.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 84 KiB |
@ -0,0 +1,21 @@ |
||||
{ |
||||
"images" : [ |
||||
{ |
||||
"filename" : "pic9.png", |
||||
"idiom" : "universal", |
||||
"scale" : "1x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "2x" |
||||
}, |
||||
{ |
||||
"idiom" : "universal", |
||||
"scale" : "3x" |
||||
} |
||||
], |
||||
"info" : { |
||||
"author" : "xcode", |
||||
"version" : 1 |
||||
} |
||||
} |
||||
|
After Width: | Height: | Size: 80 KiB |
@ -0,0 +1,32 @@ |
||||
// |
||||
// Sound.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 27/01/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
|
||||
enum Sound : String, CaseIterable, Identifiable { |
||||
|
||||
var id: String { return self.rawValue } |
||||
|
||||
case sound1 |
||||
case pouet |
||||
|
||||
} |
||||
|
||||
enum CoolPic : String, CaseIterable, Identifiable { |
||||
|
||||
var id: String { return self.rawValue } |
||||
|
||||
case pic1 |
||||
case pic2 |
||||
case pic3 |
||||
case pic4 |
||||
case pic5 |
||||
case pic6 |
||||
case pic7 |
||||
case pic8 |
||||
case pic9 |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22A400" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> |
||||
<entity name="Activity" representedClassName="Activity" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="name" attributeType="String" defaultValueString=""/> |
||||
<relationship name="countdowns" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Countdown" inverseName="activity" inverseEntity="Countdown"/> |
||||
<relationship name="records" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Record" inverseName="activity" inverseEntity="Record"/> |
||||
</entity> |
||||
<entity name="Countdown" representedClassName="Countdown" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="duration" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/> |
||||
<attribute name="image" optional="YES" attributeType="String"/> |
||||
<attribute name="order" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> |
||||
<attribute name="sound" optional="YES" attributeType="String"/> |
||||
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="countdowns" inverseEntity="Activity"/> |
||||
</entity> |
||||
<entity name="Record" representedClassName="Record" syncable="YES" codeGenerationType="class"> |
||||
<attribute name="end" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/> |
||||
<attribute name="start" attributeType="Date" defaultDateTimeInterval="696425400" usesScalarValueType="NO"/> |
||||
<relationship name="activity" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Activity" inverseName="records" inverseEntity="Activity"/> |
||||
</entity> |
||||
</model> |
||||
@ -0,0 +1,35 @@ |
||||
// |
||||
// ViewModifiers.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 27/01/2023. |
||||
// |
||||
|
||||
import Foundation |
||||
import SwiftUI |
||||
|
||||
extension View { |
||||
|
||||
func roundedCorner(selected: Bool) -> some View { |
||||
modifier(RoundedCornerSelection(selected: selected)) |
||||
} |
||||
|
||||
} |
||||
|
||||
struct RoundedCornerSelection: ViewModifier { |
||||
|
||||
var selected: Bool |
||||
|
||||
func body(content: Content) -> some View { |
||||
if selected { |
||||
content |
||||
.overlay( |
||||
RoundedRectangle(cornerRadius: 40) |
||||
.stroke(Color.blue, lineWidth: 5) |
||||
) |
||||
} else { |
||||
content |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,83 @@ |
||||
// |
||||
// CountdownFormView.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 27/01/2023. |
||||
// |
||||
|
||||
import SwiftUI |
||||
|
||||
|
||||
struct CountdownFormView : View { |
||||
|
||||
var secondsBinding: Binding<String> |
||||
var minutesBinding: Binding<String> |
||||
var nameBinding: Binding<String> |
||||
|
||||
var soundBinding: Binding<Sound> |
||||
var imageBinding: Binding<CoolPic> |
||||
|
||||
var textFieldIsFocused: FocusState<Bool>.Binding |
||||
|
||||
var body: some View { |
||||
Form { |
||||
Section(header: Text("Duration")) { |
||||
TextField("minutes", text: minutesBinding) |
||||
.keyboardType(.numberPad) |
||||
.focused(textFieldIsFocused) |
||||
TextField("seconds", text: secondsBinding) |
||||
.keyboardType(.numberPad) |
||||
.focused(textFieldIsFocused) |
||||
} |
||||
Section(header: Text("Name for tracking the activity")) { |
||||
TextField("name", text: nameBinding) |
||||
.focused(textFieldIsFocused) |
||||
} |
||||
|
||||
Section(header: Text("Properties")) { |
||||
|
||||
Picker(selection: soundBinding) { |
||||
ForEach(Sound.allCases) { sound in |
||||
Text(sound.rawValue).tag(sound) |
||||
} |
||||
} label: { |
||||
Text("Sound") |
||||
} |
||||
|
||||
} |
||||
|
||||
Section { |
||||
NavigationLink { |
||||
ImageSelectionView(imageBinding: imageBinding) |
||||
} label: { |
||||
Group { |
||||
if let image = self.imageBinding.wrappedValue { |
||||
Image(image.rawValue) |
||||
} else { |
||||
Image(imageBinding.wrappedValue.rawValue) |
||||
} |
||||
} .font(Font.system(size: 90.0)) |
||||
.aspectRatio(1, contentMode: .fit) |
||||
.frame(width: 100.0, height: 100.0) |
||||
.cornerRadius(40.0) |
||||
|
||||
|
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
struct CountdownFormView_Previews: PreviewProvider { |
||||
|
||||
@FocusState static var textFieldIsFocused: Bool |
||||
|
||||
static var previews: some View { |
||||
CountdownFormView(secondsBinding: .constant(""), minutesBinding: .constant(""), |
||||
nameBinding: .constant(""), soundBinding: .constant(.sound1), |
||||
imageBinding: .constant(.pic3), textFieldIsFocused: $textFieldIsFocused) |
||||
} |
||||
} |
||||
@ -0,0 +1,58 @@ |
||||
// |
||||
// ImageSelectionView.swift |
||||
// LeCountdown |
||||
// |
||||
// Created by Laurent Morvillier on 27/01/2023. |
||||
// |
||||
|
||||
import SwiftUI |
||||
|
||||
struct ImageSelectionView: View { |
||||
|
||||
var imageBinding: Binding<CoolPic> |
||||
|
||||
private let columns: [GridItem] = [ |
||||
GridItem(spacing: 10.0), |
||||
GridItem(spacing: 10.0), |
||||
] |
||||
|
||||
var body: some View { |
||||
|
||||
NavigationStack { |
||||
|
||||
ScrollView { |
||||
|
||||
LazyVGrid( |
||||
columns: columns, |
||||
spacing: 10.0 |
||||
) { |
||||
|
||||
ForEach(CoolPic.allCases) { coolPic in |
||||
|
||||
Group { |
||||
Image(coolPic.rawValue) |
||||
.aspectRatio(contentMode: .fill) |
||||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) |
||||
.aspectRatio(1, contentMode: .fit) |
||||
.cornerRadius(40.0) |
||||
.roundedCorner(selected: self.imageBinding.wrappedValue == coolPic) |
||||
|
||||
} |
||||
.onTapGesture { |
||||
self.imageBinding.wrappedValue = coolPic |
||||
print("coolPic = \(coolPic), image = \(String(describing: self.imageBinding.wrappedValue))") |
||||
} |
||||
|
||||
} |
||||
}.padding(10.0) |
||||
}.navigationTitle("Background") |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
struct ImageSelectionView_Previews: PreviewProvider { |
||||
static var previews: some View { |
||||
ImageSelectionView(imageBinding: .constant(.pic3)) |
||||
} |
||||
} |
||||