From 9511cab7caee3e8810b9fa363a5ef0aa5c9f9f94 Mon Sep 17 00:00:00 2001 From: Steffan Andrews Date: Wed, 23 Oct 2024 18:17:58 -0700 Subject: [PATCH] Updated docs --- .../Documentation.docc/Timecode-Components.md | 1 + .../Documentation.docc/Timecode-Encoding.md | 92 ++++++++++++++++--- .../SwiftUI/TimecodeField/TimecodeField.swift | 1 + .../SwiftUI/TimecodeText/TimecodeText.swift | 1 + 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Sources/TimecodeKitCore/Documentation.docc/Timecode-Components.md b/Sources/TimecodeKitCore/Documentation.docc/Timecode-Components.md index d0d670e5..c885a8af 100644 --- a/Sources/TimecodeKitCore/Documentation.docc/Timecode-Components.md +++ b/Sources/TimecodeKitCore/Documentation.docc/Timecode-Components.md @@ -38,3 +38,4 @@ Primitive storage type for timecode component values. - ``isWithinValidDigitCounts(at:base:)`` - ``validRange(of:using:)`` - ``validRange(of:at:base:limit:)`` +- ``ComponentRanges`` diff --git a/Sources/TimecodeKitCore/Documentation.docc/Timecode-Encoding.md b/Sources/TimecodeKitCore/Documentation.docc/Timecode-Encoding.md index 6c8ab617..353323d0 100644 --- a/Sources/TimecodeKitCore/Documentation.docc/Timecode-Encoding.md +++ b/Sources/TimecodeKitCore/Documentation.docc/Timecode-Encoding.md @@ -61,22 +61,42 @@ struct TimecodeListView: View { // Provide default properties in case only a timecode string is present, // which could be the case if the user copies a plain-text timecode string // from another application. If the timecode data on the pasteboard was - // originally created using Timecode's itemProviders() method, then - // these properties will be ignored, as the pasteboard will contain lossless - // data with which to decode to the new Timecode instance. - let properties = Timecode.Properties(rate: .fps24) + // originally created using Timecode's `itemProviders()` method or its + // `Transferable` representation, then these properties will be ignored, + // as the pasteboard will contain lossless data with which to decode to + // the new Timecode instance. + let properties = model.last?.properties + ?? Timecode.Properties(rate: .fps24) let timecode = try await Timecode( from: itemProviders, propertiesForString: properties ) - - // Timecode's default Identifiable implementation uses self + + // Here is where you can validate pasted timecode before accepting it. + // See the `validate()` method inline help or Encoding section in the + // documentation for details. + let validatedTimecode = TimecodeField.validate(pastedTimecode: timecode, ... ) + + // Finally, accept the pasted timecode + // Timecode's default `Identifiable` implementation uses self // which means we cannot have two identical timecodes in a SwiftUI array - if !model.contains(timecode) { model.append(timecode) } + if !model.contains(validatedTimecode) { + model.append(validatedTimecode) + } } } ``` +See the section on how to validate pasted timecode. + +> Important: +> +> If SwiftUI view modifiers using `NSItemProviders` are invoked without exporting `Timecode`'s UT Type, this error will be thrown: +> +> `Type "com.orchetect.TimecodeKit.timecode" was expected to be declared and exported in the Info.plist of MyApp.app, but it was not found.` +> +> See the section for information on how to export this type in your app. + ## Transferable ``Timecode`` conforms to the [`Transferable`](https://developer.apple.com/documentation/coretransferable/transferable) protocol which allows instances to be dragged & dropped or copied to/from the clipboard using declarative SwiftUI syntax. @@ -105,22 +125,68 @@ struct TimecodeListView: View { } func add(items: [Timecode]) { - // Timecode's default Identifiable implementation uses self - // which means we cannot have two identical timecodes in a SwiftUI array - items.forEach { - if !model.contains($0) { model.append($0) } + for item in items { + // Here is where you can validate pasted timecode before accepting it. + // See the `validate()` method inline help or Encoding section in the + // documentation for details. + let validatedTimecode = TimecodeField.validate(pastedTimecode: item, ... ) + + // Timecode's default `Identifiable` implementation uses self + // which means we cannot have two identical timecodes in a SwiftUI array + if !model.contains(validatedTimecode) { model.append(validatedTimecode) } } } } ``` +See the section on how to validate pasted timecode. + > Important: > -> If SwiftUI methods using the [`Transferable`](https://developer.apple.com/documentation/coretransferable/transferable) protocol are invoked without exporting this UT Type, this error will be thrown: +> If SwiftUI view modifiers using the [`Transferable`](https://developer.apple.com/documentation/coretransferable/transferable) protocol are invoked without exporting `Timecode`'s UT Type, this error will be thrown: > > `Type "com.orchetect.TimecodeKit.timecode" was expected to be declared and exported in the Info.plist of MyApp.app, but it was not found.` > -> See UT Types section above for information on how to export this type in your app. +> See the section for information on how to export this type in your app. + +## Pasted Timecode Validation Against Local Context + +When accepting timecode pasted from the clipboard, it is common to validate it against a local context. + +For example, you may want to constrain pasted timecode to a certain frame rate, subframes base and upper limit. + +`TimecodeKitUI` offers static API to perform this validation by supplying policies to validate against. + +```swift +@TimecodeState private var timecode: Timecode + +// pass in the `Timecode` instance received from the pasteboard +// from the `pasteDestination()` or `onPasteCommand()` view modifiers: +func validate(pastedTimecode: Timecode) { + guard let newTimecode = TimecodeField.validate( + pastedTimecode: pastedTimecode, + localTimecodeProperties: timecode.properties, + pastePolicy: .preserveLocalProperties, + validationPolicy: .enforceValid + ) else { return } + + timecode = newTimecode +} +``` + +> Note: +> +> This method is offered on `TimecodeField` since it is the same API the field uses internally when handling its own paste events. +> +> Because `TimecodeField` implements copy and paste functionality under the hood, calling the `validate` method is unnecessary as it is already being called internally on user paste events. +> +> Instead, use the corresponding view modifiers to specify the policies on your `TimecodeField` instance: +> +> - `timecodeFieldPastePolicy(_:)` +> - `timecodeFieldValidationPolicy(_:)` +> - `timecodeFieldInputStyle(_:)` +> +> See `TimecodeField` documentation in the `TimecodeKitUI` module for more information, or try out the **Timecode UI** example project located in the **Examples** folder within this repo. ## Topics diff --git a/Sources/TimecodeKitUI/SwiftUI/TimecodeField/TimecodeField.swift b/Sources/TimecodeKitUI/SwiftUI/TimecodeField/TimecodeField.swift index 6b949445..17b82e2a 100644 --- a/Sources/TimecodeKitUI/SwiftUI/TimecodeField/TimecodeField.swift +++ b/Sources/TimecodeKitUI/SwiftUI/TimecodeField/TimecodeField.swift @@ -53,6 +53,7 @@ import TimecodeKitCore /// ```swift /// TimecodeField(timecode: $timecode) /// // appearance +/// .font(.title) // font size and family may be set as usual /// .foregroundColor(.primary) // default text color /// .timecodeFormat([.showSubFrames]) // enable subframes component /// .timecodeSeparatorStyle(.secondary) // colorize separators diff --git a/Sources/TimecodeKitUI/SwiftUI/TimecodeText/TimecodeText.swift b/Sources/TimecodeKitUI/SwiftUI/TimecodeText/TimecodeText.swift index 6c8887e9..71b80e97 100644 --- a/Sources/TimecodeKitUI/SwiftUI/TimecodeText/TimecodeText.swift +++ b/Sources/TimecodeKitUI/SwiftUI/TimecodeText/TimecodeText.swift @@ -48,6 +48,7 @@ import TimecodeKitCore /// /// ```swift /// TimecodeText(timecode) +/// .font(.title) // font size and family may be set as usual /// .foregroundColor(.primary) // default text color /// .timecodeFormat([.showSubFrames]) // enable subframes component /// .timecodeSeparatorStyle(.secondary) // colorize separators