Restructured everything
This commit is contained in:
parent
903a063257
commit
bbbfbcb7c5
|
@ -18,8 +18,7 @@ public extension View {
|
|||
/// - chunk: The `FlowChunk` associated with the current view, which determines the spotlight shape, background, and other properties.
|
||||
///
|
||||
/// - Returns: The modified view with the matched geometry effect applied, allowing it to be highlighted by the spotlight effect.
|
||||
func configureChunkForSpotlight<Chunk: FlowChunk>(parentIdentifier: String, chunk: Chunk) -> some View {
|
||||
let namespace = getNamespace(from: parentIdentifier)
|
||||
func configureChunkForSpotlight<Chunk: FlowChunk>(namespace: Namespace.ID, parentIdentifier: String, chunk: Chunk) -> some View {
|
||||
|
||||
return self
|
||||
.matchedGeometryEffect(
|
||||
|
@ -42,22 +41,19 @@ public extension View {
|
|||
/// - chunks: An array of `FlowChunk` instances associated with the child views to be highlighted by the spotlight effect.
|
||||
///
|
||||
/// - Returns: The modified view with the `HighlightingView` applied, containing the spotlight effect and its child views.
|
||||
func assembleSpotlightChunks<Chunk: FlowChunk>(uniqueIdentifier: String, chunks: [Chunk], showTutorial: Binding<Bool>) -> some View {
|
||||
let namespace = getNamespace(from: uniqueIdentifier)
|
||||
func assembleSpotlightChunks<Chunk: FlowChunk>(namespace: Namespace.ID, uniqueIdentifier: String, chunks: [Chunk], showTutorial: Binding<Bool>, _ onStepChange: ((_ chunk: Chunk) -> ())? = nil) -> some View {
|
||||
|
||||
return HighlightingView(
|
||||
namespace: namespace,
|
||||
showTutorial: showTutorial,
|
||||
chunks: chunks
|
||||
) {
|
||||
self
|
||||
}
|
||||
}
|
||||
return (
|
||||
HighlightingView(
|
||||
namespace: namespace,
|
||||
showTutorial: showTutorial,
|
||||
chunks: chunks
|
||||
) {
|
||||
self
|
||||
}
|
||||
.onStepChange(onStepChange ?? { _ in })
|
||||
)
|
||||
|
||||
func convertToInstructionsView(position: InstructionsViewPosition) -> InstructionsView {
|
||||
InstructionsView(position: position) {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,17 +66,3 @@ public extension View {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Helper Methods
|
||||
fileprivate extension View {
|
||||
func getNamespace(from identifier: String) -> Namespace.ID {
|
||||
if let existingNamespace = FutureFlowManager.currentNamespaces[identifier] {
|
||||
return existingNamespace
|
||||
} else {
|
||||
let newNamespace = Namespace().wrappedValue
|
||||
FutureFlowManager.currentNamespaces[identifier] = newNamespace
|
||||
return newNamespace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,12 @@ public protocol FlowChunk: Hashable, Identifiable {
|
|||
var spotlightShape: SpotlightShape { get }
|
||||
var spotlightBackground: SpotlightBackground { get }
|
||||
|
||||
var instructionsViewType: InstructionsViewType? { get }
|
||||
@InstructionsViewBuilder func instructionsView(
|
||||
_ next: @escaping () -> (),
|
||||
_ back: @escaping () -> ()
|
||||
) -> AnyInstructionsView?
|
||||
|
||||
var instructionsViewPosition: InstructionsViewPosition { get }
|
||||
}
|
||||
|
||||
public extension FlowChunk {
|
||||
|
@ -27,7 +32,10 @@ public extension FlowChunk {
|
|||
return .black
|
||||
}
|
||||
|
||||
var instructionsViewType: InstructionsViewType? {
|
||||
return nil
|
||||
func instructionsView(
|
||||
_ next: @escaping () -> (),
|
||||
_ back: @escaping () -> ()
|
||||
) -> AnyInstructionsView? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// FutureFlowManager.swift
|
||||
//
|
||||
//
|
||||
// Created by Muhand Jumah on 4/23/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
internal class FutureFlowManager {
|
||||
internal static var currentNamespaces: [String:Namespace.ID] = [:]
|
||||
}
|
|
@ -7,14 +7,21 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
public final class Manager<Chunk: FlowChunk>: ObservableObject {
|
||||
@Published var totalChunks: [Chunk] = []
|
||||
@Published var currentStepIndex: Int = 0
|
||||
}
|
||||
|
||||
internal struct HighlightingView<Content: View, Chunk: FlowChunk>: View {
|
||||
@StateObject var manager: Manager<Chunk> = .init()
|
||||
|
||||
// Binding variables
|
||||
@Binding var showTutorial: Bool
|
||||
|
||||
// Private variables
|
||||
let namespace: Namespace.ID
|
||||
let chunks: [Chunk]
|
||||
let content: Content
|
||||
let content: () -> Content
|
||||
|
||||
#if DEBUG
|
||||
// State variables (TODO: Delete)
|
||||
|
@ -23,62 +30,65 @@ internal struct HighlightingView<Content: View, Chunk: FlowChunk>: View {
|
|||
|
||||
@Namespace private var namespace2
|
||||
|
||||
internal init(namespace: Namespace.ID, showTutorial: Binding<Bool>, chunks: [Chunk], @ViewBuilder content: () -> Content) {
|
||||
private var onCurrentChunkChanged: ((_ newChunk: Chunk) -> ())?
|
||||
|
||||
internal init(
|
||||
namespace: Namespace.ID,
|
||||
showTutorial: Binding<Bool>,
|
||||
chunks: [Chunk],
|
||||
@ViewBuilder content: @escaping () -> Content)
|
||||
{
|
||||
self.namespace = namespace
|
||||
self._showTutorial = showTutorial
|
||||
self.chunks = chunks
|
||||
self.content = content()
|
||||
self.onCurrentChunkChanged = nil
|
||||
self.content = content
|
||||
}
|
||||
|
||||
fileprivate init(
|
||||
namespace: Namespace.ID,
|
||||
showTutorial: Binding<Bool>,
|
||||
chunks: [Chunk],
|
||||
onCurrentChunkChanged: @escaping (_ chunk: Chunk) -> (),
|
||||
content: @escaping () -> Content)
|
||||
{
|
||||
self.namespace = namespace
|
||||
self._showTutorial = showTutorial
|
||||
self.chunks = chunks
|
||||
self.onCurrentChunkChanged = onCurrentChunkChanged
|
||||
self.content = content
|
||||
}
|
||||
|
||||
internal var body: some View {
|
||||
ZStack {
|
||||
self.content
|
||||
self.content()
|
||||
|
||||
ZStack {
|
||||
if (self.showTutorial) {
|
||||
let chunk = self.chunks[self.currentIndex]
|
||||
|
||||
SpotlightView(
|
||||
chunk: self.chunks[self.currentIndex],
|
||||
chunk: chunk,
|
||||
namespace: self.namespace,
|
||||
namespace2: self.namespace2
|
||||
)
|
||||
.overlay(
|
||||
GeometryReader { reader in
|
||||
instructionsOverlay(reader: reader)
|
||||
.animation(.easeOut, value: self.showTutorial ? self.currentIndex : nil)
|
||||
// Text("\(reader.size.width) X \(reader.size.height)")
|
||||
// ZStack {
|
||||
// Color.blue
|
||||
// }
|
||||
}
|
||||
.matchedGeometryEffect(id: 10000, in: self.namespace2, isSource: false)
|
||||
// .overlay(
|
||||
// GeometryReader { reader in
|
||||
// Text("\(reader.size.width) X \(reader.size.height)")
|
||||
// }
|
||||
// )
|
||||
)
|
||||
.opacity(self.showTutorial ? 1 : 0)
|
||||
.animation(
|
||||
.easeOut,
|
||||
value: self.showTutorial ? self.currentIndex : nil
|
||||
)
|
||||
.opacity(self.showTutorial ? 1 : 0)
|
||||
.animation(.easeOut, value: self.showTutorial ? self.currentIndex : nil)
|
||||
|
||||
|
||||
chunk.instructionsView(self.advance, self.previous)
|
||||
.frame(width:UIScreen.main.bounds.width * 0.8)
|
||||
.offset(x:0, y:self.chunks[self.currentIndex].instructionsViewPosition == .below ? 20 : -20)
|
||||
.matchedGeometryEffect(id: 10001,
|
||||
in: self.namespace2,
|
||||
properties: .position,
|
||||
anchor: self.chunks[self.currentIndex].instructionsViewPosition == .below ? .top : .bottom,
|
||||
isSource: false)
|
||||
.animation(.easeOut, value: self.showTutorial ? self.currentIndex : nil)
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
.animation(.linear, value: self.showTutorial)
|
||||
|
||||
|
||||
//#if DEBUG
|
||||
// Button(action: {
|
||||
// if self.currentIndex < self.chunks.count - 1{
|
||||
// self.currentIndex += 1
|
||||
// } else {
|
||||
// self.currentIndex = 0
|
||||
// }
|
||||
// }) {
|
||||
// Text("NEXT")
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
.onChange(of: self.showTutorial) { newValue in
|
||||
if(newValue == false) {
|
||||
|
@ -87,6 +97,19 @@ internal struct HighlightingView<Content: View, Chunk: FlowChunk>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: self.currentIndex) { newValue in
|
||||
if let callback = self.onCurrentChunkChanged {
|
||||
callback(self.chunks[newValue])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onStepChange(_ callback: @escaping (_ chunk: Chunk) -> ()) -> HighlightingView {
|
||||
.init(namespace: self.namespace,
|
||||
showTutorial: self.$showTutorial,
|
||||
chunks: self.chunks,
|
||||
onCurrentChunkChanged: callback,
|
||||
content: self.content)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,9 +120,7 @@ private extension HighlightingView {
|
|||
return
|
||||
}
|
||||
|
||||
// withAnimation(.easeOut) {
|
||||
self.showTutorial = false
|
||||
// }
|
||||
self.showTutorial = false
|
||||
}
|
||||
|
||||
func previous() {
|
||||
|
@ -107,88 +128,4 @@ private extension HighlightingView {
|
|||
self.currentIndex -= 1
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func instructionsOverlay(reader: GeometryProxy) -> some View {
|
||||
if let instructionsViewType = self.chunks[self.currentIndex].instructionsViewType {
|
||||
let v = instructionsViewType
|
||||
.body(
|
||||
currentIndex: self.$currentIndex,
|
||||
maxCount: self.chunks.count,
|
||||
advance: self.advance,
|
||||
previous: self.previous
|
||||
)
|
||||
ZStack {
|
||||
// TODO: Find a better way than this hacky method (this is needed for now because we need the `v`'s frame in overlay.
|
||||
v
|
||||
.opacity(0)
|
||||
}
|
||||
.overlay(
|
||||
GeometryReader { rr in
|
||||
v
|
||||
.frame(width: UIScreen.main.bounds.size.width)
|
||||
.offset(
|
||||
x: -(UIScreen.main.bounds.size.width - reader.size.width) / 2,
|
||||
y: self.getYOffset(position: v.position, in: v.position == .below ? reader.size : rr.size)
|
||||
)
|
||||
}
|
||||
.opacity(self.showTutorial ? 1.0 : 0.0)
|
||||
|
||||
)
|
||||
// v
|
||||
// .overlay(
|
||||
// Text("\(reader.size.height)").allowsHitTesting(false))
|
||||
|
||||
// .offset(
|
||||
// x: -(UIScreen.main.bounds.size.width - reader.size.width) / 2,
|
||||
// y: self.getYOffset(position: v.position, in: reader.size)
|
||||
// )
|
||||
|
||||
// y: self.getYOffset(position: v.position, in: reader.size)
|
||||
// )
|
||||
}
|
||||
// ZStack {
|
||||
|
||||
// if let sle
|
||||
|
||||
// if(self.chunks[self.currentIndex].instruction != nil) {
|
||||
// SimpleInstructionsBubble(text: "TEST")
|
||||
// .frame(width: 200, height: 100, alignment: .center)
|
||||
// self.chunks[self.currentIndex].instruction!.bubble.body(instruction: self.chunks[self.currentIndex].instruction!)
|
||||
// }
|
||||
// .offset(x: 0, y: reader.size.height + 20)
|
||||
|
||||
// if let instruction = self.chunks[self.currentIndex].instruction {
|
||||
// self
|
||||
// .chunks[self.currentIndex]
|
||||
// .instructionsViewType!
|
||||
//// .inWithEnum()
|
||||
// .body(adv: self.advance, prev: self.previous)
|
||||
// .instructionsView(advance: self.advance, previous: self.previous)
|
||||
// instruction.bubble.body(instruction: instruction)
|
||||
// .padding(.horizontal, 15)
|
||||
// .frame(width: UIScreen.main.bounds.size.width)
|
||||
//// .offset(x: 0, y: 400)
|
||||
// .offset(
|
||||
// x: -(UIScreen.main.bounds.size.width - reader.size.width) / 2,
|
||||
// y: self.getYOffset(position: instruction.position, in: reader.size)
|
||||
// )
|
||||
// }
|
||||
|
||||
// if (self.chunk.instructionsBubble != nil) {
|
||||
// self.chunk.instructionsBubble!.body
|
||||
// .padding(.horizontal)
|
||||
// .frame(width: UIScreen.main.bounds.size.width)
|
||||
// .offset(x: 0, y: self.getYOffset(position: self.chunk.instructionsBubble!.position, in: reader.size))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func getYOffset(position: InstructionsViewPosition, in frame: CGSize) -> CGFloat {
|
||||
switch position {
|
||||
case .above:
|
||||
return -(frame.height + 20)
|
||||
default:
|
||||
return (frame.height + 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// AnyInstructionsView.swift
|
||||
//
|
||||
//
|
||||
// Created by Muhand Jumah on 4/29/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct AnyInstructionsView: InstructionsView {
|
||||
private let _view: () -> AnyView
|
||||
|
||||
public var body: some View {
|
||||
_view()
|
||||
}
|
||||
|
||||
public init<I: InstructionsView>(_ instructionsView: I) {
|
||||
_view = {
|
||||
instructionsView
|
||||
.eraseToAnyView()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,21 +7,13 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
public struct InstructionsView : View {
|
||||
internal let position: InstructionsViewPosition
|
||||
private let content: AnyView
|
||||
public protocol InstructionsView: View {
|
||||
}
|
||||
|
||||
internal init<Content: View>
|
||||
(
|
||||
position: InstructionsViewPosition = .below,
|
||||
@ViewBuilder _ content: () -> Content
|
||||
) {
|
||||
self.position = position
|
||||
self.content = content().eraseToAnyView()
|
||||
}
|
||||
|
||||
@_spi(Private)
|
||||
public var body: some View {
|
||||
self.content
|
||||
extension InstructionsView {
|
||||
func eraseToAnyInstructionsView() -> AnyInstructionsView {
|
||||
AnyInstructionsView(
|
||||
self
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// InstructionsViewBuilder.swift
|
||||
//
|
||||
//
|
||||
// Created by Muhand Jumah on 4/29/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@resultBuilder
|
||||
public struct InstructionsViewBuilder {
|
||||
public static func buildBlock<Content: InstructionsView>(_ instructionsView: Content?) -> AnyInstructionsView? {
|
||||
instructionsView?
|
||||
.eraseToAnyInstructionsView()
|
||||
}
|
||||
|
||||
public static func buildEither<Content: InstructionsView>(first component: Content?) -> AnyInstructionsView? {
|
||||
component?
|
||||
.eraseToAnyInstructionsView()
|
||||
}
|
||||
|
||||
public static func buildEither<Content: InstructionsView>(second component: Content?) -> AnyInstructionsView? {
|
||||
component?
|
||||
.eraseToAnyInstructionsView()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,127 +1,128 @@
|
|||
////
|
||||
//// InstructionsViewType.swift
|
||||
////
|
||||
////
|
||||
//// Created by Muhand Jumah on 4/26/23.
|
||||
////
|
||||
//
|
||||
// InstructionsViewType.swift
|
||||
//
|
||||
//
|
||||
// Created by Muhand Jumah on 4/26/23.
|
||||
//
|
||||
|
||||
public enum InstructionsViewType {
|
||||
case simple(instructions: [String], position: InstructionsViewPosition = .below)
|
||||
case custom(view: (_ advance: @escaping () -> (), _ previous: @escaping () -> ()) -> InstructionsView)
|
||||
|
||||
internal func body(
|
||||
currentIndex: Binding<Int>,
|
||||
maxCount: Int,
|
||||
advance: @escaping () -> (),
|
||||
previous: @escaping () -> ()
|
||||
) -> InstructionsView {
|
||||
switch self {
|
||||
case .simple(let instructions, let position):
|
||||
return SimpleInstructionsViewWrapper(currentGlobalIndex: currentIndex,
|
||||
globalMaxCount: maxCount,
|
||||
advance: advance,
|
||||
previous: previous,
|
||||
instructions: instructions)
|
||||
.convertToInstructionsView(position: position)
|
||||
case .custom(let view):
|
||||
return view(advance, previous)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Rework this piece
|
||||
import SwiftUI
|
||||
|
||||
internal struct SimpleInstructionsViewWrapper: View {
|
||||
// Global instructions
|
||||
@Binding var currentGlobalIndex: Int
|
||||
var globalMaxCount: Int
|
||||
let advance: () -> ()
|
||||
let previous: () -> ()
|
||||
|
||||
private(set) var instructions: [String]
|
||||
@State private var currentInstructionIndex: Int = 0
|
||||
@State private var showPrevious: Bool = false
|
||||
@State private var showNext: Bool = false
|
||||
|
||||
var body: some View {
|
||||
SimpleInstructionsView(
|
||||
text: self.instructions[self.currentInstructionIndex],
|
||||
showPrevious: .init (
|
||||
get: {
|
||||
return (self.currentGlobalIndex > 0 && self.globalMaxCount > 0) || (self.currentInstructionIndex > 0 && self.instructions.count > 0)
|
||||
},
|
||||
set: { _ in}
|
||||
),
|
||||
showNext: .init(
|
||||
get: {
|
||||
return self.currentInstructionIndex < instructions.count || self.currentGlobalIndex < self.globalMaxCount
|
||||
},
|
||||
set: { _ in }
|
||||
)
|
||||
)
|
||||
.onTapNext {
|
||||
guard currentInstructionIndex == instructions.count - 1 else {
|
||||
currentInstructionIndex += 1
|
||||
return
|
||||
}
|
||||
|
||||
advance()
|
||||
self.currentInstructionIndex = 0
|
||||
}
|
||||
.onTapPrev {
|
||||
guard currentInstructionIndex == 0 else {
|
||||
currentInstructionIndex -= 1
|
||||
return
|
||||
}
|
||||
// guard self.currentInstructionIndex > 0 else {
|
||||
// return
|
||||
// }
|
||||
// self.currentInstructionIndex -= 1
|
||||
self.currentInstructionIndex = 0
|
||||
previous()
|
||||
// self.currentInstructionIndex = instructions.count
|
||||
|
||||
}
|
||||
.padding(.horizontal, 15)
|
||||
// .onChange(of: self.currentInstructionIndex, perform: { newValue in
|
||||
// // For the next button
|
||||
// if(newValue < instructions.count - 1 || currentGlobalIndex < globalMaxCount - 1) {
|
||||
// self.showNext = true
|
||||
// } else {
|
||||
// self.showNext = false
|
||||
// }
|
||||
//
|
||||
// if(newValue > 0 && self.instructions.count > 0) {
|
||||
// self.showPrevious = true
|
||||
// } else {
|
||||
// self.showPrevious = false
|
||||
// }
|
||||
// })
|
||||
.onAppear {
|
||||
// Next
|
||||
if(currentInstructionIndex < instructions.count - 1 || currentGlobalIndex < globalMaxCount) {
|
||||
self.showNext = true
|
||||
} else {
|
||||
self.showNext = false
|
||||
}
|
||||
// if(currentInstructionIndex < instructions.count - 1) {
|
||||
// self.showNext = true
|
||||
// } else {
|
||||
// self.showNext = false
|
||||
// }
|
||||
//
|
||||
if(currentInstructionIndex > 0 && self.instructions.count > 0) {
|
||||
self.showPrevious = true
|
||||
} else {
|
||||
self.showPrevious = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//struct SimpleInstructionsViewWrapper_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// SimpleInstructionsViewWrapper(instructions: ["Hello", "World", "askdlfjalk shfjlka dhfkj ahsdfkhdj"])
|
||||
// }
|
||||
//public enum InstructionsViewType {
|
||||
//// case simple(instructions: [String], position: InstructionsViewPosition = .below)
|
||||
//// case custom(view: (_ advance: @escaping () -> (), _ previous: @escaping () -> ()) -> InstructionsView)
|
||||
// case custom(view:(_ advance: @escaping () -> (), _ previous: @escaping () -> ()) -> AnyInstructionsView)
|
||||
////
|
||||
//// internal func body(
|
||||
//// currentIndex: Binding<Int>,
|
||||
//// maxCount: Int,
|
||||
//// advance: @escaping () -> (),
|
||||
//// previous: @escaping () -> ()
|
||||
//// ) -> InstructionsView {
|
||||
//// switch self {
|
||||
//// case .simple(let instructions, let position):
|
||||
//// return SimpleInstructionsViewWrapper(currentGlobalIndex: currentIndex,
|
||||
//// globalMaxCount: maxCount,
|
||||
//// advance: advance,
|
||||
//// previous: previous,
|
||||
//// instructions: instructions)
|
||||
//// .convertToInstructionsView(position: position)
|
||||
//// case .custom(let view):
|
||||
//// return view(advance, previous)
|
||||
//// }
|
||||
//// }
|
||||
//}
|
||||
//
|
||||
////// TODO: Rework this piece
|
||||
////import SwiftUI
|
||||
////
|
||||
////internal struct SimpleInstructionsViewWrapper: View {
|
||||
//// // Global instructions
|
||||
//// @Binding var currentGlobalIndex: Int
|
||||
//// var globalMaxCount: Int
|
||||
//// let advance: () -> ()
|
||||
//// let previous: () -> ()
|
||||
////
|
||||
//// private(set) var instructions: [String]
|
||||
//// @State private var currentInstructionIndex: Int = 0
|
||||
//// @State private var showPrevious: Bool = false
|
||||
//// @State private var showNext: Bool = false
|
||||
////
|
||||
//// var body: some View {
|
||||
//// SimpleInstructionsView(
|
||||
//// text: self.instructions[self.currentInstructionIndex],
|
||||
//// showPrevious: .init (
|
||||
//// get: {
|
||||
//// return (self.currentGlobalIndex > 0 && self.globalMaxCount > 0) || (self.currentInstructionIndex > 0 && self.instructions.count > 0)
|
||||
//// },
|
||||
//// set: { _ in}
|
||||
//// ),
|
||||
//// showNext: .init(
|
||||
//// get: {
|
||||
//// return self.currentInstructionIndex < instructions.count || self.currentGlobalIndex < self.globalMaxCount
|
||||
//// },
|
||||
//// set: { _ in }
|
||||
//// )
|
||||
//// )
|
||||
//// .onTapNext {
|
||||
//// guard currentInstructionIndex == instructions.count - 1 else {
|
||||
//// currentInstructionIndex += 1
|
||||
//// return
|
||||
//// }
|
||||
////
|
||||
//// advance()
|
||||
//// self.currentInstructionIndex = 0
|
||||
//// }
|
||||
//// .onTapPrev {
|
||||
//// guard currentInstructionIndex == 0 else {
|
||||
//// currentInstructionIndex -= 1
|
||||
//// return
|
||||
//// }
|
||||
////// guard self.currentInstructionIndex > 0 else {
|
||||
////// return
|
||||
////// }
|
||||
////// self.currentInstructionIndex -= 1
|
||||
//// self.currentInstructionIndex = 0
|
||||
//// previous()
|
||||
////// self.currentInstructionIndex = instructions.count
|
||||
////
|
||||
//// }
|
||||
//// .padding(.horizontal, 15)
|
||||
////// .onChange(of: self.currentInstructionIndex, perform: { newValue in
|
||||
////// // For the next button
|
||||
////// if(newValue < instructions.count - 1 || currentGlobalIndex < globalMaxCount - 1) {
|
||||
////// self.showNext = true
|
||||
////// } else {
|
||||
////// self.showNext = false
|
||||
////// }
|
||||
//////
|
||||
////// if(newValue > 0 && self.instructions.count > 0) {
|
||||
////// self.showPrevious = true
|
||||
////// } else {
|
||||
////// self.showPrevious = false
|
||||
////// }
|
||||
////// })
|
||||
//// .onAppear {
|
||||
//// // Next
|
||||
//// if(currentInstructionIndex < instructions.count - 1 || currentGlobalIndex < globalMaxCount) {
|
||||
//// self.showNext = true
|
||||
//// } else {
|
||||
//// self.showNext = false
|
||||
//// }
|
||||
////// if(currentInstructionIndex < instructions.count - 1) {
|
||||
////// self.showNext = true
|
||||
////// } else {
|
||||
////// self.showNext = false
|
||||
////// }
|
||||
//////
|
||||
//// if(currentInstructionIndex > 0 && self.instructions.count > 0) {
|
||||
//// self.showPrevious = true
|
||||
//// } else {
|
||||
//// self.showPrevious = false
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
//////struct SimpleInstructionsViewWrapper_Previews: PreviewProvider {
|
||||
////// static var previews: some View {
|
||||
////// SimpleInstructionsViewWrapper(instructions: ["Hello", "World", "askdlfjalk shfjlka dhfkj ahsdfkhdj"])
|
||||
////// }
|
||||
//////}
|
||||
|
|
|
@ -1,146 +1,147 @@
|
|||
// TODO: Create a default view
|
||||
////
|
||||
//// SimpleInstructionsView.swift
|
||||
////
|
||||
////
|
||||
//// Created by Muhand Jumah on 4/23/23.
|
||||
////
|
||||
//
|
||||
// SimpleInstructionsView.swift
|
||||
//
|
||||
//import SwiftUI
|
||||
//
|
||||
// Created by Muhand Jumah on 4/23/23.
|
||||
//struct SimpleInstructionsView: View {
|
||||
// private var text: String
|
||||
// private var nextTapped: () -> ()
|
||||
// private var prevTapped: () -> ()
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SimpleInstructionsView: View {
|
||||
private var text: String
|
||||
private var nextTapped: () -> ()
|
||||
private var prevTapped: () -> ()
|
||||
|
||||
@Binding var showPrevious: Bool
|
||||
@Binding var showNext: Bool
|
||||
|
||||
public init(
|
||||
text: String,
|
||||
showPrevious: Binding<Bool> = .constant(false),
|
||||
showNext: Binding<Bool> = .constant(false)
|
||||
) {
|
||||
self.text = text
|
||||
self.nextTapped = { }
|
||||
self.prevTapped = { }
|
||||
self._showPrevious = showPrevious
|
||||
self._showNext = showNext
|
||||
}
|
||||
|
||||
fileprivate init(
|
||||
text: String,
|
||||
nextTapped: @escaping () -> (),
|
||||
prevTapped: @escaping () -> (),
|
||||
showPrevious: Binding<Bool> = .constant(false),
|
||||
showNext: Binding<Bool> = .constant(false)
|
||||
) {
|
||||
self.text = text
|
||||
self.nextTapped = nextTapped
|
||||
self.prevTapped = prevTapped
|
||||
self._showPrevious = showPrevious
|
||||
self._showNext = showNext
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
// Color.purple
|
||||
VStack(alignment:.leading, spacing: 20) {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
Image(systemName: "lightbulb.circle.fill")
|
||||
.font(.system(size: 32))
|
||||
.foregroundColor(.blue)
|
||||
|
||||
Text(self.text)
|
||||
.font(.body)
|
||||
}
|
||||
|
||||
if (self.showPrevious || self.showNext) {
|
||||
HStack(alignment: .center, spacing:15) {
|
||||
Spacer()
|
||||
if(self.showPrevious) {
|
||||
Button(action: {
|
||||
self.prevTapped()
|
||||
}) {
|
||||
Text("Previous")
|
||||
.font(.callout)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
|
||||
if(self.showNext) {
|
||||
|
||||
Button(action: {
|
||||
self.nextTapped()
|
||||
}) {
|
||||
Text("Next")
|
||||
.font(.callout)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 5)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(3)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 15)
|
||||
.background(Color.white)
|
||||
.cornerRadius(10)
|
||||
.shadow(color: .black.opacity(0.2), radius: 5, x: 0, y: 0)
|
||||
}
|
||||
|
||||
func onTapNext(_ callback: @escaping () -> ()) -> Self {
|
||||
return .init(
|
||||
text: self.text,
|
||||
nextTapped: callback,
|
||||
prevTapped: self.prevTapped,
|
||||
showPrevious: self.$showPrevious,
|
||||
showNext: self.$showNext
|
||||
)
|
||||
}
|
||||
|
||||
func onTapPrev(_ callback: @escaping () -> ()) -> Self {
|
||||
return .init(
|
||||
text: self.text,
|
||||
nextTapped: self.nextTapped,
|
||||
prevTapped: callback,
|
||||
showPrevious: self.$showPrevious,
|
||||
showNext: self.$showNext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct SimpleInstructionsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ZStack {
|
||||
Color.white
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
VStack(spacing: 5) {
|
||||
SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.")
|
||||
|
||||
SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showNext: .constant(true))
|
||||
.onTapNext {
|
||||
print("Next Tapped")
|
||||
}
|
||||
|
||||
SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showPrevious: .constant(true))
|
||||
.onTapPrev {
|
||||
print("Previous Tapped")
|
||||
}
|
||||
|
||||
SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showPrevious: .constant(true), showNext: .constant(true))
|
||||
.onTapNext {
|
||||
print("Next Tapped")
|
||||
}
|
||||
.onTapPrev {
|
||||
print("Previous Tapped")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
// @Binding var showPrevious: Bool
|
||||
// @Binding var showNext: Bool
|
||||
//
|
||||
// public init(
|
||||
// text: String,
|
||||
// showPrevious: Binding<Bool> = .constant(false),
|
||||
// showNext: Binding<Bool> = .constant(false)
|
||||
// ) {
|
||||
// self.text = text
|
||||
// self.nextTapped = { }
|
||||
// self.prevTapped = { }
|
||||
// self._showPrevious = showPrevious
|
||||
// self._showNext = showNext
|
||||
// }
|
||||
//
|
||||
// fileprivate init(
|
||||
// text: String,
|
||||
// nextTapped: @escaping () -> (),
|
||||
// prevTapped: @escaping () -> (),
|
||||
// showPrevious: Binding<Bool> = .constant(false),
|
||||
// showNext: Binding<Bool> = .constant(false)
|
||||
// ) {
|
||||
// self.text = text
|
||||
// self.nextTapped = nextTapped
|
||||
// self.prevTapped = prevTapped
|
||||
// self._showPrevious = showPrevious
|
||||
// self._showNext = showNext
|
||||
// }
|
||||
//
|
||||
// var body: some View {
|
||||
//// Color.purple
|
||||
// VStack(alignment:.leading, spacing: 20) {
|
||||
// HStack(alignment: .top, spacing: 10) {
|
||||
// Image(systemName: "lightbulb.circle.fill")
|
||||
// .font(.system(size: 32))
|
||||
// .foregroundColor(.blue)
|
||||
//
|
||||
// Text(self.text)
|
||||
// .font(.body)
|
||||
// }
|
||||
//
|
||||
// if (self.showPrevious || self.showNext) {
|
||||
// HStack(alignment: .center, spacing:15) {
|
||||
// Spacer()
|
||||
// if(self.showPrevious) {
|
||||
// Button(action: {
|
||||
// self.prevTapped()
|
||||
// }) {
|
||||
// Text("Previous")
|
||||
// .font(.callout)
|
||||
// .fontWeight(.medium)
|
||||
// .foregroundColor(.blue)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(self.showNext) {
|
||||
//
|
||||
// Button(action: {
|
||||
// self.nextTapped()
|
||||
// }) {
|
||||
// Text("Next")
|
||||
// .font(.callout)
|
||||
// .fontWeight(.semibold)
|
||||
// .padding(.horizontal, 10)
|
||||
// .padding(.vertical, 5)
|
||||
// .background(Color.blue)
|
||||
// .cornerRadius(3)
|
||||
// .foregroundColor(.white)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// .padding(.horizontal, 20)
|
||||
// .padding(.vertical, 15)
|
||||
// .background(Color.white)
|
||||
// .cornerRadius(10)
|
||||
// .shadow(color: .black.opacity(0.2), radius: 5, x: 0, y: 0)
|
||||
// }
|
||||
//
|
||||
// func onTapNext(_ callback: @escaping () -> ()) -> Self {
|
||||
// return .init(
|
||||
// text: self.text,
|
||||
// nextTapped: callback,
|
||||
// prevTapped: self.prevTapped,
|
||||
// showPrevious: self.$showPrevious,
|
||||
// showNext: self.$showNext
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// func onTapPrev(_ callback: @escaping () -> ()) -> Self {
|
||||
// return .init(
|
||||
// text: self.text,
|
||||
// nextTapped: self.nextTapped,
|
||||
// prevTapped: callback,
|
||||
// showPrevious: self.$showPrevious,
|
||||
// showNext: self.$showNext
|
||||
// )
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//struct SimpleInstructionsView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// ZStack {
|
||||
// Color.white
|
||||
// .edgesIgnoringSafeArea(.all)
|
||||
//
|
||||
// VStack(spacing: 5) {
|
||||
// SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.")
|
||||
//
|
||||
// SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showNext: .constant(true))
|
||||
// .onTapNext {
|
||||
// print("Next Tapped")
|
||||
// }
|
||||
//
|
||||
// SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showPrevious: .constant(true))
|
||||
// .onTapPrev {
|
||||
// print("Previous Tapped")
|
||||
// }
|
||||
//
|
||||
// SimpleInstructionsView(text: "This is just a tutorial asjdkfhla sdfhlakjsd hflkajsd hflkjasdhf lkjasdhf kljasd lajsdfh lkjsdhf lakjsdhf akjsl hfakjsd hadffajsdkfhlkajs hflkjas hfklja hsdjkfl haljskd fhljasdfhlaksd hflas dhfklj asdh fljasdlfjsdhfl.", showPrevious: .constant(true), showNext: .constant(true))
|
||||
// .onTapNext {
|
||||
// print("Next Tapped")
|
||||
// }
|
||||
// .onTapPrev {
|
||||
// print("Previous Tapped")
|
||||
// }
|
||||
// }
|
||||
// .padding()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
// SpotlightView.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
// Created by Muhand Jumah on 4/23/23.
|
||||
//
|
||||
|
@ -11,60 +11,36 @@ internal struct SpotlightView<Chunk: FlowChunk>: View {
|
|||
private var chunk: Chunk
|
||||
private var namespace: Namespace.ID
|
||||
private var namespace2: Namespace.ID
|
||||
|
||||
|
||||
internal init(chunk: Chunk, namespace: Namespace.ID, namespace2: Namespace.ID) {
|
||||
self.chunk = chunk
|
||||
self.namespace = namespace
|
||||
self.namespace2 = namespace2
|
||||
}
|
||||
|
||||
|
||||
internal var body: some View {
|
||||
ZStack {
|
||||
self.chunk.spotlightBackground.body(self.namespace)
|
||||
|
||||
// GeometryReader { reader in
|
||||
self.chunk.spotlightShape.body(self.namespace)
|
||||
.blendMode(.destinationOut)
|
||||
.matchedGeometryEffect(id: 10000, in: self.namespace2, isSource: true)
|
||||
// .overlay(
|
||||
// instructionsOverlay(reader: reader),
|
||||
// alignment: .top
|
||||
// )
|
||||
// }
|
||||
.matchedGeometryEffect(
|
||||
id: self.chunk.id,
|
||||
in: namespace,
|
||||
properties: .frame, anchor: .center,
|
||||
isSource: false
|
||||
)
|
||||
// .blendMode(.destinationOut)
|
||||
|
||||
|
||||
self.chunk.spotlightShape.body(self.namespace)
|
||||
.blendMode(.destinationOut)
|
||||
.matchedGeometryEffect(
|
||||
id: 10001,
|
||||
in: self.namespace2,
|
||||
properties: .position,
|
||||
anchor: self.chunk.instructionsViewPosition == .above ? .top : .bottom,
|
||||
isSource: true
|
||||
)
|
||||
|
||||
.matchedGeometryEffect(
|
||||
id: self.chunk.id,
|
||||
in: namespace,
|
||||
properties: .frame,
|
||||
anchor: .center,
|
||||
isSource: false
|
||||
)
|
||||
}
|
||||
.compositingGroup()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper Methods
|
||||
//private extension SpotlightView {
|
||||
// func instructionsOverlay(reader: GeometryProxy) -> some View {
|
||||
// ZStack {
|
||||
// if(self.chunk.instructions.count > 0) {
|
||||
// Text("TEST")
|
||||
// }
|
||||
//// if (self.chunk.instructionsBubble != nil) {
|
||||
//// self.chunk.instructionsBubble!.body
|
||||
//// .padding(.horizontal)
|
||||
//// .frame(width: UIScreen.main.bounds.size.width)
|
||||
//// .offset(x: 0, y: self.getYOffset(position: self.chunk.instructionsBubble!.position, in: reader.size))
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func getYOffset(position: InstructionsBubblePosition, in frame: CGSize) -> CGFloat {
|
||||
// switch position {
|
||||
// case .above:
|
||||
// return -(frame.height + 20)
|
||||
// default:
|
||||
// return (frame.height + 20)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
B69AAB4529F31E6700026789 /* FutureFlow in Frameworks */ = {isa = PBXBuildFile; productRef = B69AAB4429F31E6700026789 /* FutureFlow */; };
|
||||
B69AAB4729F42F8900026789 /* Demo2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB4629F42F8900026789 /* Demo2.swift */; };
|
||||
B69AAB4929F44D2A00026789 /* Demo10.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB4829F44D2A00026789 /* Demo10.swift */; };
|
||||
B6B551EE29FDCB6E006CA32D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = B6B551ED29FDCB6E006CA32D /* README.md */; };
|
||||
B6C55E9729F5C75200E9E281 /* Demo11.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C55E9629F5C75200E9E281 /* Demo11.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -50,6 +51,7 @@
|
|||
B69AAB3429F31CF500026789 /* FutureFlowDemoUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FutureFlowDemoUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||
B69AAB4629F42F8900026789 /* Demo2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Demo2.swift; sourceTree = "<group>"; };
|
||||
B69AAB4829F44D2A00026789 /* Demo10.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Demo10.swift; sourceTree = "<group>"; };
|
||||
B6B551ED29FDCB6E006CA32D /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
B6C55E9629F5C75200E9E281 /* Demo11.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Demo11.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -103,13 +105,11 @@
|
|||
B69AAB1629F31CF300026789 /* FutureFlowDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B6B551EC29FDCB4E006CA32D /* NotRelated */,
|
||||
B69AAB1729F31CF300026789 /* FutureFlowDemoApp.swift */,
|
||||
B69AAB1929F31CF300026789 /* ContentView.swift */,
|
||||
B69AAB4629F42F8900026789 /* Demo2.swift */,
|
||||
B69AAB1D29F31CF400026789 /* Preview Content */,
|
||||
B69AAB1B29F31CF400026789 /* Assets.xcassets */,
|
||||
B69AAB4829F44D2A00026789 /* Demo10.swift */,
|
||||
B6C55E9629F5C75200E9E281 /* Demo11.swift */,
|
||||
);
|
||||
path = FutureFlowDemo;
|
||||
sourceTree = "<group>";
|
||||
|
@ -146,6 +146,17 @@
|
|||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B6B551EC29FDCB4E006CA32D /* NotRelated */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B69AAB4829F44D2A00026789 /* Demo10.swift */,
|
||||
B6C55E9629F5C75200E9E281 /* Demo11.swift */,
|
||||
B69AAB4629F42F8900026789 /* Demo2.swift */,
|
||||
B6B551ED29FDCB6E006CA32D /* README.md */,
|
||||
);
|
||||
path = NotRelated;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -253,6 +264,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B6B551EE29FDCB6E006CA32D /* README.md in Resources */,
|
||||
B69AAB1F29F31CF400026789 /* Preview Assets.xcassets in Resources */,
|
||||
B69AAB1C29F31CF400026789 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
|
|
|
@ -8,12 +8,73 @@
|
|||
import SwiftUI
|
||||
import FutureFlow
|
||||
|
||||
struct ParentWrapper: InstructionsView {
|
||||
@Binding var tutorial: TutorialChunks
|
||||
let next: () -> ()
|
||||
let back: () -> ()
|
||||
|
||||
@State private var instructions: [String] = []
|
||||
@Namespace var ns
|
||||
|
||||
var body: some View {
|
||||
RectanglesTutorial(instructions: self.$instructions)
|
||||
.onDone({ withNext in
|
||||
if(withNext) {
|
||||
self.next()
|
||||
} else {
|
||||
self.back()
|
||||
}
|
||||
})
|
||||
.animation(.easeOut, value: self.tutorial)
|
||||
.onAppear {
|
||||
switch self.tutorial {
|
||||
case .red1:
|
||||
self.instructions = ["This is red"]
|
||||
break
|
||||
case .red2:
|
||||
self.instructions = ["Red is purple now??? WHAT?"]
|
||||
break
|
||||
case .red3:
|
||||
self.instructions = ["No way, IT IS BLUE!!"]
|
||||
break
|
||||
case .yellow:
|
||||
self.instructions = ["This is yellow"]
|
||||
break
|
||||
case .green:
|
||||
self.instructions = ["This is green"]
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
.onChange(of: self.tutorial) { newValue in
|
||||
switch newValue {
|
||||
case .red1:
|
||||
self.instructions = ["This is red"]
|
||||
break
|
||||
case .red2:
|
||||
self.instructions = ["Red is purple now??? WHAT?"]
|
||||
break
|
||||
case .red3:
|
||||
self.instructions = ["No way, IT IS BLUE!!"]
|
||||
break
|
||||
case .yellow:
|
||||
self.instructions = ["This is yellow"]
|
||||
break
|
||||
case .green:
|
||||
self.instructions = ["This is green"]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TutorialChunks: FlowChunk, CaseIterable {
|
||||
case red
|
||||
case red1
|
||||
case red2
|
||||
case red3
|
||||
case green
|
||||
case yellow
|
||||
|
||||
|
||||
var spotlightShape: SpotlightShape {
|
||||
switch self {
|
||||
case .yellow:
|
||||
|
@ -22,7 +83,7 @@ enum TutorialChunks: FlowChunk, CaseIterable {
|
|||
return .rectangle()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var spotlightBackground: SpotlightBackground {
|
||||
switch self {
|
||||
case .green:
|
||||
|
@ -31,62 +92,117 @@ enum TutorialChunks: FlowChunk, CaseIterable {
|
|||
return .black
|
||||
}
|
||||
}
|
||||
|
||||
var instructionsViewType: InstructionsViewType? {
|
||||
|
||||
var instructionsViewPosition: InstructionsViewPosition {
|
||||
switch self {
|
||||
case .red:
|
||||
return .simple(instructions: ["Text1"], position: .below)
|
||||
case .red1, .red2, .red3:
|
||||
return .below
|
||||
case .green:
|
||||
return .simple(instructions: ["Text2", "Text22", "Text222", "Text2222"], position: .below)
|
||||
return .above
|
||||
case .yellow:
|
||||
return .simple(instructions: ["Text3"], position: .above)
|
||||
return .above
|
||||
}
|
||||
}
|
||||
|
||||
func instructionsView(_ next: @escaping () -> (), _ back: @escaping () -> ()) -> FutureFlow.AnyInstructionsView? {
|
||||
switch self {
|
||||
case .red1, .red2:
|
||||
ParentWrapper(tutorial:
|
||||
.init(
|
||||
get: {
|
||||
self
|
||||
|
||||
}, set: { _ in }
|
||||
),
|
||||
next: next,
|
||||
back: back
|
||||
)
|
||||
default:
|
||||
ParentWrapper(tutorial:
|
||||
.init(
|
||||
get: {
|
||||
self
|
||||
|
||||
}, set: { _ in }
|
||||
),
|
||||
next: next,
|
||||
back: back
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
switch self {
|
||||
case .red1, .red2, .red3:
|
||||
hasher.combine(0)
|
||||
case .green:
|
||||
hasher.combine(1)
|
||||
case .yellow:
|
||||
hasher.combine(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
private var uniqueIdentifier: String = UUID().uuidString
|
||||
private var tutorial: [TutorialChunks] =
|
||||
[
|
||||
.red,
|
||||
.green,
|
||||
.yellow
|
||||
]
|
||||
@State private var showTutorial: Bool = true
|
||||
|
||||
struct ContentView: View {
|
||||
@State private var rectColor: Color = .red
|
||||
private var uniqueIdentifier: String = UUID().uuidString
|
||||
|
||||
@State private var showTutorial: Bool = true
|
||||
@Namespace var namespace
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 50) {
|
||||
Rectangle()
|
||||
.frame(width: 256, height: 256, alignment: .center)
|
||||
.foregroundColor(Color.red)
|
||||
.foregroundColor(self.rectColor)
|
||||
.configureChunkForSpotlight(
|
||||
namespace: self.namespace,
|
||||
parentIdentifier: self.uniqueIdentifier,
|
||||
chunk: self.tutorial[0]
|
||||
chunk: TutorialChunks.red1
|
||||
)
|
||||
|
||||
|
||||
Rectangle()
|
||||
.frame(width: 64, height: 128, alignment: .center)
|
||||
.foregroundColor(Color.green)
|
||||
.configureChunkForSpotlight(
|
||||
namespace: self.namespace,
|
||||
parentIdentifier: self.uniqueIdentifier,
|
||||
chunk: self.tutorial[1]
|
||||
chunk: TutorialChunks.green
|
||||
)
|
||||
|
||||
|
||||
Circle()
|
||||
.frame(width: 128, height: 128, alignment: .center)
|
||||
.foregroundColor(.yellow)
|
||||
.configureChunkForSpotlight(
|
||||
namespace: self.namespace,
|
||||
parentIdentifier: self.uniqueIdentifier,
|
||||
chunk: self.tutorial[2]
|
||||
chunk: TutorialChunks.yellow
|
||||
)
|
||||
.onTapGesture {
|
||||
// withAnimation(.linear) {
|
||||
self.showTutorial = true
|
||||
// }
|
||||
// withAnimation(.linear) {
|
||||
self.showTutorial = true
|
||||
// }
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.assembleSpotlightChunks(uniqueIdentifier: self.uniqueIdentifier, chunks: Array(TutorialChunks.allCases), showTutorial: self.$showTutorial)
|
||||
.assembleSpotlightChunks(namespace:self.namespace, uniqueIdentifier: self.uniqueIdentifier, chunks: Array(TutorialChunks.allCases), showTutorial: self.$showTutorial) {
|
||||
chunk in
|
||||
withAnimation(.easeOut) {
|
||||
switch chunk {
|
||||
case .red2:
|
||||
self.rectColor = .purple
|
||||
break
|
||||
case .red3:
|
||||
self.rectColor = .blue
|
||||
break
|
||||
default:
|
||||
self.rectColor = .red
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,3 +211,108 @@ struct ContentView_Previews: PreviewProvider {
|
|||
ContentView()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal struct RectanglesTutorial: InstructionsView {
|
||||
@Binding private var instructions: [String]
|
||||
|
||||
@Namespace var positionNamespace
|
||||
private var positionId: String = "steps-position-id"
|
||||
@State private var showNext: Bool = true
|
||||
@State private var showPrevious: Bool = true
|
||||
@State private var currentStep: Int = 0
|
||||
|
||||
private var onDone: ((_ withNext: Bool) -> ())?
|
||||
|
||||
internal init(instructions: Binding<[String]>) {
|
||||
self._instructions = instructions
|
||||
// self.onDone = { _ in }
|
||||
self.onDone = nil
|
||||
}
|
||||
|
||||
fileprivate init(instructions: Binding<[String]>, onDone: @escaping (_ withNext: Bool) -> ()) {
|
||||
self._instructions = instructions
|
||||
self.onDone = onDone
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
HStack(alignment: .top, spacing: 10) {
|
||||
Image(systemName: "lightbulb.circle.fill")
|
||||
.font(.system(size: 32))
|
||||
.foregroundColor(.blue)
|
||||
if(self.currentStep < self.instructions.count) {
|
||||
Text(self.instructions[self.currentStep])
|
||||
.font(.body)
|
||||
.animation(.easeOut, value: self.currentStep)
|
||||
}
|
||||
}
|
||||
.matchedGeometryEffect(id: positionId, in: self.positionNamespace, properties: .position, anchor: .bottomTrailing, isSource: true)
|
||||
|
||||
HStack(alignment: .center, spacing:15) {
|
||||
if(self.showPrevious) {
|
||||
Button(action: {
|
||||
guard self.currentStep > 0 else {
|
||||
if let onDone = self.onDone {
|
||||
onDone(false)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self.currentStep -= 1
|
||||
}) {
|
||||
Text("Previous")
|
||||
.font(.callout)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
|
||||
if(self.showNext) {
|
||||
Button(action: {
|
||||
guard self.currentStep < self.instructions.count - 1 else {
|
||||
if let onDone = self.onDone {
|
||||
onDone(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
self.currentStep += 1
|
||||
}) {
|
||||
Text("Next")
|
||||
.font(.callout)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 5)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(3)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
.transition(.slide)
|
||||
.animation(.linear, value: self.showNext)
|
||||
.animation(.linear, value: self.showPrevious)
|
||||
.padding(.top, 10)
|
||||
.matchedGeometryEffect(id: positionId, in: self.positionNamespace, properties: .position, anchor: .topTrailing, isSource: false)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 15)
|
||||
.background(Color.white)
|
||||
.cornerRadius(10)
|
||||
.shadow(color: .black.opacity(0.2), radius: 5, x: 0, y: 0)
|
||||
.animation(.easeOut, value: self.currentStep)
|
||||
.onAppear {
|
||||
self.currentStep = 0
|
||||
}
|
||||
.onChange(of: self.instructions) { newV in
|
||||
self.currentStep = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension RectanglesTutorial {
|
||||
func onDone(_ callback: @escaping (_ withNext: Bool) -> ()) -> RectanglesTutorial {
|
||||
.init(instructions: self.$instructions, onDone: callback)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
////
|
||||
//// Demo11.swift
|
||||
//// FutureFlowDemo
|
||||
////
|
||||
//// Created by Muhand Jumah on 4/23/23.
|
||||
////
|
||||
//
|
||||
//import SwiftUI
|
||||
//
|
||||
//struct Demo11: View {
|
||||
//
|
||||
//}
|
||||
//
|
||||
//struct Demo11_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
//
|
||||
//
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,216 @@
|
|||
//////
|
||||
////// Demo11.swift
|
||||
////// FutureFlowDemo
|
||||
//////
|
||||
////// Created by Muhand Jumah on 4/23/23.
|
||||
//////
|
||||
////
|
||||
////import SwiftUI
|
||||
////
|
||||
////struct Demo11: View {
|
||||
//// var body: some View {
|
||||
//// ScrollView {
|
||||
//// Text (
|
||||
//// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec et orci metus. Donec consectetur laoreet finibus. Pellentesque at maximus leo. Donec nec elementum metus, ut interdum libero. Cras hendrerit neque non magna sodales, eu lacinia justo laoreet. Phasellus pharetra vehicula nibh, porttitor elementum velit vestibulum id. Pellentesque quis mattis risus, eget feugiat justo. Maecenas a tortor neque. Nullam placerat non mauris eget tristique. Nam auctor dolor sit amet tincidunt pulvinar.\n\nNunc cursus eget quam ut convallis. In hac habitasse platea dictumst. Integer id elit ac tellus vehicula viverra eu ac metus. Cras tempus nisl eget nulla porttitor, placerat sollicitudin justo tincidunt. Phasellus et leo mi. Curabitur convallis malesuada mi tincidunt scelerisque. Sed rutrum ultricies ligula. Pellentesque nisl metus, euismod venenatis dui sit amet, lobortis lacinia quam. Nullam ipsum est, tempus et nisl nec, consectetur fermentum nulla. Fusce velit augue, consectetur nec purus id, maximus bibendum elit. Phasellus ornare odio sit amet erat tristique dapibus. Aliquam erat volutpat. Nam hendrerit diam eget arcu dapibus lacinia. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ligula augue, aliquam nec condimentum quis, consectetur a erat.\nFusce at ipsum leo. Morbi egestas sem sit amet velit vestibulum gravida. Nullam eu finibus lorem. Nam ut lacus turpis. Nullam eu sagittis dui. Ut et condimentum purus. Sed et lectus pretium, imperdiet nulla sit amet, pulvinar enim. Suspendisse blandit tempus varius. Sed at consequat orci. Quisque feugiat malesuada mauris nec bibendum. Aenean condimentum ornare leo, quis porttitor risus malesuada vitae. Duis interdum odio ut scelerisque aliquet. Nam et viverra felis. Nulla eu nisi eu nisi condimentum bibendum at in odio. Aenean ut arcu quam.\nInteger non tristique turpis. Vestibulum non dolor metus. Phasellus odio lorem, consequat quis fermentum nec, faucibus scelerisque orci. Sed id placerat nibh, quis ultrices diam. Sed laoreet quis massa vel tincidunt. Proin maximus accumsan convallis. Morbi sed mi ac turpis condimentum faucibus. Donec eros nunc, dignissim vitae nibh a, pulvinar suscipit mi. Nam libero odio, rhoncus id eleifend sed, porttitor ut metus. Nunc fringilla eget ligula vel dictum. Aenean eget posuere eros. Sed placerat nec sem at faucibus. Sed vehicula sollicitudin lectus, ut consequat quam commodo in.\nIn id facilisis ligula, ut aliquet urna. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam dictum blandit metus vitae dignissim. Nunc ac orci sed lorem volutpat tincidunt. Nulla vel purus nunc. Duis rutrum lorem et semper scelerisque. Quisque rutrum lorem eu arcu ultricies semper. Mauris auctor vulputate est non aliquam. Vivamus viverra ullamcorper sem, et consectetur est dapibus ut. Aliquam at massa sed lectus hendrerit aliquam in et mi. Sed venenatis tincidunt sem, ut rutrum augue vestibulum vitae. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Phasellus hendrerit quam et erat finibus tempor. Donec vehicula ut mi non ullamcorper. Sed lorem elit, hendrerit eu volutpat ut, tincidunt vel leo. "
|
||||
//// )
|
||||
//// .fixedSize(horizontal: false, vertical: true)
|
||||
//// .frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
//// }.frame(maxHeight: .infinity).padding()
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////struct Demo11_Previews: PreviewProvider {
|
||||
//// static var previews: some View {
|
||||
//// Demo11()
|
||||
//// }
|
||||
////}
|
||||
//
|
||||
//import SwiftUI
|
||||
//@available(iOS 16.0, *)
|
||||
//extension Image {
|
||||
// @MainActor func frame(width: CGFloat, height: CGFloat) -> Image {
|
||||
// let imageRenderer: ImageRenderer = .init(
|
||||
// content:
|
||||
// self
|
||||
// .resizable()
|
||||
// .frame(width: width, height: height, alignment: .center)
|
||||
// )
|
||||
//
|
||||
// if let uiImage = imageRenderer.uiImage {
|
||||
// return Image(uiImage: uiImage)
|
||||
// } else {
|
||||
// return
|
||||
// Image(systemName: "x.circle")
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//@available(iOS 16.0, *)
|
||||
//struct Demo11: View {
|
||||
// var body: some View {
|
||||
//// ScrollView(.vertical) {
|
||||
//// VStack(alignment: .leading, spacing: 10) {
|
||||
//// HStack(alignment: .top, spacing: 10) {
|
||||
//// Image(systemName: "square.fill")
|
||||
//// .resizable()
|
||||
//// .frame(width: 80, height: 80)
|
||||
//// .foregroundColor(.black)
|
||||
////
|
||||
//// Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum vulputate euismod. In facilisis diam et mollis consequat jkahdfk ladshfkljahsd fjlkahsd fjlkhasd kjlhasdjk faljk dsfljah sdfjlk hasdklj asdnf msdnf m,.asnf asdjhf ljaskhf lkjasdh fljakshd fljkash. ")
|
||||
//// }
|
||||
////
|
||||
//// Text("Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia, dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum. ")
|
||||
//// }
|
||||
//// }
|
||||
//// .padding()
|
||||
//
|
||||
// ScrollView(.vertical) {
|
||||
//// Text("Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia") + Text(Image(systemName: "square.fill")) + Text(", dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum.")
|
||||
// AsyncImage(url: .init(string: "https://1000logos.net/wp-content/uploads/2016/10/Apple-Logo.png")) { img in
|
||||
//// Text("Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia") +
|
||||
////// Text(img.frame(width: 80, height: 80)) +
|
||||
//// Text(Image(systemName: "x.circle")) +
|
||||
//// Text(", dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum.")
|
||||
//
|
||||
// Text(
|
||||
// "Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia"
|
||||
// + Image(systemName: "x.circle")
|
||||
// + ", dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum."
|
||||
// )
|
||||
//// let m: Image = img
|
||||
////// .resizable()
|
||||
////// .scaledToFill()
|
||||
////// .frame(width: 80, height: 80)
|
||||
////
|
||||
//// Text("TEST") +
|
||||
//// Text(m.frame(width: 80, height: 80)).font(.system(size: 100)) +
|
||||
//// Text("test2")
|
||||
// } placeholder: {
|
||||
// Text("Loading...")
|
||||
// }
|
||||
//
|
||||
//// HStack(alignment: .top, spacing: 10) {
|
||||
//// VStack(alignment: .leading, spacing: 10) {
|
||||
//// Image(systemName: "square.fill")
|
||||
//// .resizable()
|
||||
//// .frame(width: 80, height: 80)
|
||||
//// .foregroundColor(.black)
|
||||
////
|
||||
//// Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum vulputate euismod. In facilisis diam et mollis consequat jkahdfk ladshfkljahsd fjlkahsd fjlkhasd kjlhasdjk faljk dsfljah sdfjlk hasdklj asdnf msdnf m,.asnf asdjhf ljaskhf lkjasdh fljakshd fljkash. ")
|
||||
//// }
|
||||
//// .frame(maxWidth: 80, alignment: .topLeading)
|
||||
////
|
||||
//// Text("Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia, dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum. ")
|
||||
//// }
|
||||
// }
|
||||
// .padding()
|
||||
//
|
||||
//
|
||||
//// Text("Ut vulputate ultrices erat, sit amet auctor felis tristique vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi felis leo, blandit cursus tortor eu, ullamcorper volutpat orci. Donec ornare velit sed neque scelerisque, sit amet malesuada nunc laoreet. Ut non lobortis felis, vel ultrices ligula. Nam massa mauris, volutpat et felis lacinia") +
|
||||
////// Text(img.frame(width: 80, height: 80)) +
|
||||
//// Text(Image(systemName: "x.circle")).font(.system(size: 20)) +
|
||||
//// Text(", dignissim efficitur arcu. Vestibulum euismod in risus at consequat. Nunc a vestibulum dolor. Vivamus sed augue lectus. Duis felis leo, gravida quis est ac, volutpat suscipit nulla. Pellentesque pharetra vulputate erat ut lobortis. Vivamus sed nibh enim. Phasellus pharetra malesuada justo quis molestie. Sed eget lectus at turpis elementum posuere quis ut velit. Morbi suscipit venenatis tempus. Ut sed arcu ipsum.")
|
||||
// }
|
||||
//}
|
||||
//
|
||||
////struct DynamicTextModifier: ViewModifier {
|
||||
//// let imageWidth: CGFloat
|
||||
//// let text: String
|
||||
//// let font: Font
|
||||
////
|
||||
//// func body(content: Content) -> some View {
|
||||
//// GeometryReader { geometry in
|
||||
//// let lineWidth = geometry.size.width - imageWidth
|
||||
//// let textFragments = splitTextToFitWidth(text: text, lineWidth: lineWidth, font: font)
|
||||
////
|
||||
//// VStack(alignment: .leading, spacing: 0) {
|
||||
//// ForEach(textFragments.indices, id: \.self) { index in
|
||||
//// if index == 0 {
|
||||
//// HStack {
|
||||
//// Spacer(minLength: imageWidth)
|
||||
//// Text(textFragments[index]).font(font)
|
||||
//// }
|
||||
//// } else {
|
||||
//// Text(textFragments[index]).font(font)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// private func splitTextToFitWidth(text: String, lineWidth: CGFloat, font: Font) -> [String] {
|
||||
//// let words = text.split(separator: " ")
|
||||
//// var textFragments: [String] = []
|
||||
//// var currentLine = ""
|
||||
////
|
||||
//// for word in words {
|
||||
//// let newLine = currentLine.isEmpty ? "\(word)" : "\(currentLine) \(word)"
|
||||
//// let newSize = newLine.size(usingFont: font)
|
||||
////
|
||||
//// if newSize.width <= lineWidth {
|
||||
//// currentLine = newLine
|
||||
//// } else {
|
||||
//// textFragments.append(currentLine)
|
||||
//// currentLine = "\(word)"
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// if !currentLine.isEmpty {
|
||||
//// textFragments.append(currentLine)
|
||||
//// }
|
||||
////
|
||||
//// return textFragments
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////extension String {
|
||||
//// func size(usingFont font: Font) -> CGSize {
|
||||
//// let nsString = self as NSString
|
||||
//// let fontAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: font.size)] // Adjust this line if you use a custom font
|
||||
//// return nsString.size(withAttributes: fontAttributes)
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////extension Font {
|
||||
//// var size: CGFloat {
|
||||
//// switch self {
|
||||
//// case .largeTitle: return UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
|
||||
//// case .title: return UIFont.preferredFont(forTextStyle: .title1).pointSize
|
||||
//// case .title2: return UIFont.preferredFont(forTextStyle: .title2).pointSize
|
||||
//// case .title3: return UIFont.preferredFont(forTextStyle: .title3).pointSize
|
||||
//// case .headline: return UIFont.preferredFont(forTextStyle: .headline).pointSize
|
||||
//// case .subheadline: return UIFont.preferredFont(forTextStyle: .subheadline).pointSize
|
||||
//// case .body: return UIFont.preferredFont(forTextStyle: .body).pointSize
|
||||
//// case .callout: return UIFont.preferredFont(forTextStyle: .callout).pointSize
|
||||
//// case .caption: return UIFont.preferredFont(forTextStyle: .caption1).pointSize
|
||||
//// case .caption2: return UIFont.preferredFont(forTextStyle: .caption2).pointSize
|
||||
//// case .footnote: return UIFont.preferredFont(forTextStyle: .footnote).pointSize
|
||||
//// default: return UIFont.preferredFont(forTextStyle: .body).pointSize
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////import SwiftUI
|
||||
////
|
||||
////struct Demo11: View {
|
||||
//// @State private var dynamicText: String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut dapibus malesuada turpis, non posuere est laoreet nec. Sed maximus bibendum odio, a rhoncus nisl facilisis a. Curabitur vitae consequat justo."
|
||||
////
|
||||
//// var body: some View {
|
||||
//// ZStack(alignment: .topLeading) {
|
||||
//// Color.clear.modifier(DynamicTextModifier(imageWidth: 80, text: dynamicText, font: .body))
|
||||
////
|
||||
//// Image(systemName: "square.fill") // Replace with your own image
|
||||
//// .resizable()
|
||||
//// .frame(width: 100, height: 100) // Adjust the frame size to match your image size
|
||||
//// .background(Color.red) // Optional, just to visualize the image frame
|
||||
//// }
|
||||
//// .padding()
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
//@available(iOS 16.0, *)
|
||||
//struct Demo11_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// Demo11()
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1 @@
|
|||
**NOTE:** Ignore this directory. Nothing here is important, this directory will be deleted soon.
|
Loading…
Reference in New Issue