made some changes

This commit is contained in:
Muhand Jumah 2023-04-27 01:47:33 -04:00
parent 4ad926722f
commit 1c3f4bd74e
35 changed files with 2499 additions and 202 deletions

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:FutureFlow">
</FileRef>
<FileRef
location = "group:FutureFlowDemo/FutureFlowDemo.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

33
FutureFlow/Package.swift Normal file
View File

@ -0,0 +1,33 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "FutureFlow",
platforms: [
.iOS(.v14),
.macOS(.v11),
.tvOS(.v14),
.watchOS(.v7)
],
products: [
.library(
name: "FutureFlow",
targets: ["FutureFlow"]
),
],
targets: [
.target(
name: "FutureFlow",
dependencies: [],
path: "Sources"
),
.testTarget(
name: "FutureFlowTests",
dependencies: ["FutureFlow"],
path: "Tests/FutureFlowTests"
),
],
swiftLanguageVersions: [.v5]
)

View File

@ -0,0 +1,27 @@
//
// BlurView.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
#if os(iOS)
import SwiftUI
internal struct BlurView: UIViewRepresentable {
private var style: UIBlurEffect.Style
internal init(style: UIBlurEffect.Style) {
self.style = style
}
internal func makeUIView(context: Context) -> UIVisualEffectView {
let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
return view
}
internal func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
uiView.effect = UIBlurEffect(style: style)
}
}
#endif

View File

@ -0,0 +1,86 @@
//
// View.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
// MARK: - Public API for FlowChunk ONLY
public extension View {
/// Prepares the current view to be highlighted by the spotlight effect, associating it with the specified `FlowChunk`.
///
/// This function should be used to mark individual child views within a parent view that has the `assembleSpotlightChunks` function applied to it.
///
/// - Parameters:
/// - parentIdentifier: A unique string identifier for the parent view containing the spotlight effect.
/// - 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)
return self
.matchedGeometryEffect(
id: chunk.id,
in: namespace,
properties: .frame,
anchor: .center,
isSource: true
)
}
/// Sets up the parent view to contain the spotlight effect, using the provided `FlowChunk` instances.
///
/// This function should be used to wrap the parent view containing child views that have the `configureChunkForSpotlight` function applied to them.
///
/// The chunks provided must be of the same `FlowChunk` type as the one used by the child views, to ensure that the spotlight effect works as intended.
///
/// - Parameters:
/// - uniqueIdentifier: A unique string identifier for the parent view. This identifier must be the same as the one provided to the `configureChunkForSpotlight` function for child views.
/// - 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]) -> some View {
let namespace = getNamespace(from: uniqueIdentifier)
return HighlightingView(
namespace: namespace,
showTutorial: .constant(true),
chunks: chunks
) {
self
}
}
func convertToInstructionsView(position: InstructionsViewPosition) -> InstructionsView {
InstructionsView(position: position) {
self
}
}
}
// MARK: - Helpful public extensions.
/// These are core and not related to FlowChunk directly.
public extension View {
func eraseToAnyView() -> AnyView {
AnyView(
self
)
}
}
// 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
}
}
}

View File

@ -0,0 +1,33 @@
//
// FlowChunk.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
public protocol FlowChunk: Hashable, Identifiable {
var spotlightShape: SpotlightShape { get }
var spotlightBackground: SpotlightBackground { get }
var instructionsViewType: InstructionsViewType? { get }
}
public extension FlowChunk {
var id: Int {
self.hashValue
}
var spotlightShape: SpotlightShape {
return .rectangle()
}
var spotlightBackground: SpotlightBackground {
return .black
}
var instructionsViewType: InstructionsViewType? {
return nil
}
}

View File

@ -0,0 +1,12 @@
//
// FutureFlowManager.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
internal class FutureFlowManager {
internal static var currentNamespaces: [String:Namespace.ID] = [:]
}

View File

@ -0,0 +1,179 @@
//
// HighlightingView.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
internal struct HighlightingView<Content: View, Chunk: FlowChunk>: View {
// Binding variables
@Binding var showTutorial: Bool
// Private variables
let namespace: Namespace.ID
let chunks: [Chunk]
let content: Content
#if DEBUG
// State variables (TODO: Delete)
@State private var currentIndex: Int = 0
#endif
@Namespace private var namespace2
internal init(namespace: Namespace.ID, showTutorial: Binding<Bool>, chunks: [Chunk], @ViewBuilder content: () -> Content) {
self.namespace = namespace
self._showTutorial = showTutorial
self.chunks = chunks
self.content = content()
}
internal var body: some View {
ZStack {
self.content
SpotlightView(
chunk: self.chunks[self.currentIndex],
namespace: self.namespace,
namespace2: self.namespace2
)
.opacity(self.showTutorial ? 1 : 0)
.animation(
.easeOut,
value: self.showTutorial ? self.currentIndex : nil
)
.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)")
// }
// )
)
//#if DEBUG
// Button(action: {
// if self.currentIndex < self.chunks.count - 1{
// self.currentIndex += 1
// } else {
// self.currentIndex = 0
// }
// }) {
// Text("NEXT")
// }
//#endif
}
}
}
private extension HighlightingView {
func advance() {
if self.currentIndex < self.chunks.count - 1{
self.currentIndex += 1
} else {
self.currentIndex = 0
}
}
func previous() {
if self.currentIndex > 0 {
self.currentIndex -= 1
} else {
self.currentIndex = self.chunks.count - 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
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)
)
}
)
// 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)
}
}
}

View File

@ -0,0 +1,27 @@
//
// InstructionsView.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
public struct InstructionsView : View {
internal let position: InstructionsViewPosition
private let content: AnyView
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
}
}

View File

@ -0,0 +1,11 @@
//
// InstructionsViewPosition.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
public enum InstructionsViewPosition {
case above
case below
}

View File

@ -0,0 +1,136 @@
//
// 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 - 1 || self.currentGlobalIndex < self.globalMaxCount - 1
},
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
}
.overlay(
VStack {
Text("\(self.globalMaxCount)")
Text("\(self.currentGlobalIndex)")
Text("\(self.instructions.count)")
Text("\(self.currentInstructionIndex)")
}
.offset(x: 0, y: 100)
)
.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"])
// }
//}

View File

@ -0,0 +1,146 @@
//
// SimpleInstructionsView.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
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()
}
}
}

View File

@ -0,0 +1,78 @@
//
// SpotlightBackground.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
fileprivate let MATCHED_GEOMETRY_ID: Int = 9999
public enum SpotlightBackground {
case ultraThinMaterial
case black
case customColor (
_ color: Color,
opacity: CGFloat = 0.5
)
case custom(
view: () -> AnyView
)
}
// MARK: - Internal API
internal extension SpotlightBackground {
func body(_ namespace: Namespace.ID) -> some View {
ZStack {
switch self {
case .ultraThinMaterial:
self.defaultUltraThinMaterial(namespace)
case .black:
self.defaultBlackBackground(namespace)
case .customColor(let color, let opacity):
self.defaultCustomColorBackground(namespace, color: color, opacity: opacity)
case .custom(let av):
av()
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
}
}
}
}
// MARK: - Default Backgrounds
private extension SpotlightBackground {
func defaultUltraThinMaterial(_ namespace: Namespace.ID) -> some View {
if #available(iOS 15, macOS 12, *) {
return Color.clear
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
.background(.ultraThinMaterial)
.edgesIgnoringSafeArea(.all)
} else {
#if os(iOS)
return BlurView(style: .extraLight)
#else
return self.defaultBlackBackground(namespace)
#endif
}
}
func defaultBlackBackground(_ namespace: Namespace.ID) -> some View {
return defaultCustomColorBackground(namespace, color: .black, opacity: 0.5)
}
func defaultCustomColorBackground(_ namespace: Namespace.ID, color: Color, opacity: CGFloat) -> some View {
color
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
.edgesIgnoringSafeArea(.all)
.opacity(opacity)
}
}

View File

@ -0,0 +1,63 @@
//
// SpotlightShape.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
fileprivate let MATCHED_GEOMETRY_ID: Int = 9998
public enum SpotlightShape {
case rectangle (
color: Color = .white,
blurRadius: CGFloat = 1.0
)
case circle (
color: Color = .white,
blurRadius: CGFloat = 1.0
)
case custom (
view: () -> AnyView
)
}
// MARK: - Internal API
internal extension SpotlightShape {
func body(_ namespace: Namespace.ID) -> some View {
ZStack {
switch self {
case .rectangle(let color, let blurRadius):
self.defaultRectangle(namespace, foregroundColor: color, blurRadius: blurRadius)
case .circle(let color, let blurRadius):
self.defaultCircle(namespace, foregroundColor: color, blurRadius: blurRadius)
case .custom(let av):
av()
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
}
}
}
}
// MARK: - Default Shapes
private extension SpotlightShape {
func defaultRectangle(_ namespace: Namespace.ID, foregroundColor: Color, blurRadius: CGFloat) -> some View {
Rectangle()
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
.foregroundColor(foregroundColor)
.blur(radius: blurRadius)
}
func defaultCircle(_ namespace: Namespace.ID, foregroundColor: Color, blurRadius: CGFloat) -> some View {
Circle()
.matchedGeometryEffect(id: MATCHED_GEOMETRY_ID,
in: namespace)
.foregroundColor(foregroundColor)
.blur(radius: blurRadius)
}
}

View File

@ -0,0 +1,70 @@
//
// SpotlightView.swift
//
//
// Created by Muhand Jumah on 4/23/23.
//
import SwiftUI
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)
}
.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)
// }
// }
//}

View File

@ -0,0 +1,622 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
B69AAB1829F31CF300026789 /* FutureFlowDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB1729F31CF300026789 /* FutureFlowDemoApp.swift */; };
B69AAB1A29F31CF300026789 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB1929F31CF300026789 /* ContentView.swift */; };
B69AAB1C29F31CF400026789 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B69AAB1B29F31CF400026789 /* Assets.xcassets */; };
B69AAB1F29F31CF400026789 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B69AAB1E29F31CF400026789 /* Preview Assets.xcassets */; };
B69AAB2929F31CF500026789 /* FutureFlowDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB2829F31CF500026789 /* FutureFlowDemoTests.swift */; };
B69AAB3329F31CF500026789 /* FutureFlowDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB3229F31CF500026789 /* FutureFlowDemoUITests.swift */; };
B69AAB3529F31CF500026789 /* FutureFlowDemoUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69AAB3429F31CF500026789 /* FutureFlowDemoUITestsLaunchTests.swift */; };
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 */; };
B6C55E9729F5C75200E9E281 /* Demo11.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C55E9629F5C75200E9E281 /* Demo11.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
B69AAB2529F31CF500026789 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B69AAB0C29F31CF300026789 /* Project object */;
proxyType = 1;
remoteGlobalIDString = B69AAB1329F31CF300026789;
remoteInfo = FutureFlowDemo;
};
B69AAB2F29F31CF500026789 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B69AAB0C29F31CF300026789 /* Project object */;
proxyType = 1;
remoteGlobalIDString = B69AAB1329F31CF300026789;
remoteInfo = FutureFlowDemo;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
B69AAB1429F31CF300026789 /* FutureFlowDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FutureFlowDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
B69AAB1729F31CF300026789 /* FutureFlowDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FutureFlowDemoApp.swift; sourceTree = "<group>"; };
B69AAB1929F31CF300026789 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B69AAB1B29F31CF400026789 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B69AAB1E29F31CF400026789 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
B69AAB2429F31CF500026789 /* FutureFlowDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FutureFlowDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B69AAB2829F31CF500026789 /* FutureFlowDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FutureFlowDemoTests.swift; sourceTree = "<group>"; };
B69AAB2E29F31CF500026789 /* FutureFlowDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FutureFlowDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B69AAB3229F31CF500026789 /* FutureFlowDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FutureFlowDemoUITests.swift; sourceTree = "<group>"; };
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>"; };
B6C55E9629F5C75200E9E281 /* Demo11.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Demo11.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B69AAB1129F31CF300026789 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B69AAB4529F31E6700026789 /* FutureFlow in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2129F31CF500026789 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2B29F31CF500026789 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B69AAB0B29F31CF300026789 = {
isa = PBXGroup;
children = (
B69AAB1629F31CF300026789 /* FutureFlowDemo */,
B69AAB2729F31CF500026789 /* FutureFlowDemoTests */,
B69AAB3129F31CF500026789 /* FutureFlowDemoUITests */,
B69AAB1529F31CF300026789 /* Products */,
B69AAB4329F31E6700026789 /* Frameworks */,
);
sourceTree = "<group>";
};
B69AAB1529F31CF300026789 /* Products */ = {
isa = PBXGroup;
children = (
B69AAB1429F31CF300026789 /* FutureFlowDemo.app */,
B69AAB2429F31CF500026789 /* FutureFlowDemoTests.xctest */,
B69AAB2E29F31CF500026789 /* FutureFlowDemoUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
B69AAB1629F31CF300026789 /* FutureFlowDemo */ = {
isa = PBXGroup;
children = (
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>";
};
B69AAB1D29F31CF400026789 /* Preview Content */ = {
isa = PBXGroup;
children = (
B69AAB1E29F31CF400026789 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
B69AAB2729F31CF500026789 /* FutureFlowDemoTests */ = {
isa = PBXGroup;
children = (
B69AAB2829F31CF500026789 /* FutureFlowDemoTests.swift */,
);
path = FutureFlowDemoTests;
sourceTree = "<group>";
};
B69AAB3129F31CF500026789 /* FutureFlowDemoUITests */ = {
isa = PBXGroup;
children = (
B69AAB3229F31CF500026789 /* FutureFlowDemoUITests.swift */,
B69AAB3429F31CF500026789 /* FutureFlowDemoUITestsLaunchTests.swift */,
);
path = FutureFlowDemoUITests;
sourceTree = "<group>";
};
B69AAB4329F31E6700026789 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B69AAB1329F31CF300026789 /* FutureFlowDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = B69AAB3829F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemo" */;
buildPhases = (
B69AAB1029F31CF300026789 /* Sources */,
B69AAB1129F31CF300026789 /* Frameworks */,
B69AAB1229F31CF300026789 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = FutureFlowDemo;
packageProductDependencies = (
B69AAB4429F31E6700026789 /* FutureFlow */,
);
productName = FutureFlowDemo;
productReference = B69AAB1429F31CF300026789 /* FutureFlowDemo.app */;
productType = "com.apple.product-type.application";
};
B69AAB2329F31CF500026789 /* FutureFlowDemoTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B69AAB3B29F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemoTests" */;
buildPhases = (
B69AAB2029F31CF500026789 /* Sources */,
B69AAB2129F31CF500026789 /* Frameworks */,
B69AAB2229F31CF500026789 /* Resources */,
);
buildRules = (
);
dependencies = (
B69AAB2629F31CF500026789 /* PBXTargetDependency */,
);
name = FutureFlowDemoTests;
productName = FutureFlowDemoTests;
productReference = B69AAB2429F31CF500026789 /* FutureFlowDemoTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B69AAB2D29F31CF500026789 /* FutureFlowDemoUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = B69AAB3E29F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemoUITests" */;
buildPhases = (
B69AAB2A29F31CF500026789 /* Sources */,
B69AAB2B29F31CF500026789 /* Frameworks */,
B69AAB2C29F31CF500026789 /* Resources */,
);
buildRules = (
);
dependencies = (
B69AAB3029F31CF500026789 /* PBXTargetDependency */,
);
name = FutureFlowDemoUITests;
productName = FutureFlowDemoUITests;
productReference = B69AAB2E29F31CF500026789 /* FutureFlowDemoUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B69AAB0C29F31CF300026789 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1430;
TargetAttributes = {
B69AAB1329F31CF300026789 = {
CreatedOnToolsVersion = 14.3;
};
B69AAB2329F31CF500026789 = {
CreatedOnToolsVersion = 14.3;
TestTargetID = B69AAB1329F31CF300026789;
};
B69AAB2D29F31CF500026789 = {
CreatedOnToolsVersion = 14.3;
TestTargetID = B69AAB1329F31CF300026789;
};
};
};
buildConfigurationList = B69AAB0F29F31CF300026789 /* Build configuration list for PBXProject "FutureFlowDemo" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = B69AAB0B29F31CF300026789;
productRefGroup = B69AAB1529F31CF300026789 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B69AAB1329F31CF300026789 /* FutureFlowDemo */,
B69AAB2329F31CF500026789 /* FutureFlowDemoTests */,
B69AAB2D29F31CF500026789 /* FutureFlowDemoUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B69AAB1229F31CF300026789 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B69AAB1F29F31CF400026789 /* Preview Assets.xcassets in Resources */,
B69AAB1C29F31CF400026789 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2229F31CF500026789 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2C29F31CF500026789 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B69AAB1029F31CF300026789 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B69AAB1A29F31CF300026789 /* ContentView.swift in Sources */,
B69AAB4729F42F8900026789 /* Demo2.swift in Sources */,
B6C55E9729F5C75200E9E281 /* Demo11.swift in Sources */,
B69AAB4929F44D2A00026789 /* Demo10.swift in Sources */,
B69AAB1829F31CF300026789 /* FutureFlowDemoApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2029F31CF500026789 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B69AAB2929F31CF500026789 /* FutureFlowDemoTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B69AAB2A29F31CF500026789 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B69AAB3529F31CF500026789 /* FutureFlowDemoUITestsLaunchTests.swift in Sources */,
B69AAB3329F31CF500026789 /* FutureFlowDemoUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B69AAB2629F31CF500026789 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B69AAB1329F31CF300026789 /* FutureFlowDemo */;
targetProxy = B69AAB2529F31CF500026789 /* PBXContainerItemProxy */;
};
B69AAB3029F31CF500026789 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B69AAB1329F31CF300026789 /* FutureFlowDemo */;
targetProxy = B69AAB2F29F31CF500026789 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
B69AAB3629F31CF500026789 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
B69AAB3729F31CF500026789 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
B69AAB3929F31CF500026789 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"FutureFlowDemo/Preview Content\"";
DEVELOPMENT_TEAM = 3Q76DGNCRR;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
B69AAB3A29F31CF500026789 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"FutureFlowDemo/Preview Content\"";
DEVELOPMENT_TEAM = 3Q76DGNCRR;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
B69AAB3C29F31CF500026789 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 3Q76DGNCRR;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemoTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FutureFlowDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FutureFlowDemo";
};
name = Debug;
};
B69AAB3D29F31CF500026789 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 3Q76DGNCRR;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemoTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FutureFlowDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FutureFlowDemo";
};
name = Release;
};
B69AAB3F29F31CF500026789 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 3Q76DGNCRR;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemoUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = FutureFlowDemo;
};
name = Debug;
};
B69AAB4029F31CF500026789 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 3Q76DGNCRR;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.xyfuture.FutureFlowDemoUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_TARGET_NAME = FutureFlowDemo;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B69AAB0F29F31CF300026789 /* Build configuration list for PBXProject "FutureFlowDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B69AAB3629F31CF500026789 /* Debug */,
B69AAB3729F31CF500026789 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B69AAB3829F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B69AAB3929F31CF500026789 /* Debug */,
B69AAB3A29F31CF500026789 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B69AAB3B29F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemoTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B69AAB3C29F31CF500026789 /* Debug */,
B69AAB3D29F31CF500026789 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B69AAB3E29F31CF500026789 /* Build configuration list for PBXNativeTarget "FutureFlowDemoUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B69AAB3F29F31CF500026789 /* Debug */,
B69AAB4029F31CF500026789 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */
B69AAB4429F31E6700026789 /* FutureFlow */ = {
isa = XCSwiftPackageProductDependency;
productName = FutureFlow;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = B69AAB0C29F31CF300026789 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB1329F31CF300026789"
BuildableName = "FutureFlowDemo.app"
BlueprintName = "FutureFlowDemo"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB2329F31CF500026789"
BuildableName = "FutureFlowDemoTests.xctest"
BlueprintName = "FutureFlowDemoTests"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB2D29F31CF500026789"
BuildableName = "FutureFlowDemoUITests.xctest"
BlueprintName = "FutureFlowDemoUITests"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB1329F31CF300026789"
BuildableName = "FutureFlowDemo.app"
BlueprintName = "FutureFlowDemo"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB1329F31CF300026789"
BuildableName = "FutureFlowDemo.app"
BlueprintName = "FutureFlowDemo"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB2329F31CF500026789"
BuildableName = "FutureFlowDemoTests.xctest"
BlueprintName = "FutureFlowDemoTests"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B69AAB2D29F31CF500026789"
BuildableName = "FutureFlowDemoUITests.xctest"
BlueprintName = "FutureFlowDemoUITests"
ReferencedContainer = "container:FutureFlowDemo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,91 @@
//
// ContentView.swift
// FutureFlowDemo
//
// Created by Muhand Jumah on 4/21/23.
//
import SwiftUI
import FutureFlow
enum TutorialChunks: FlowChunk, CaseIterable {
case red
case green
case yellow
var spotlightShape: SpotlightShape {
switch self {
case .yellow:
return .circle()
default:
return .rectangle()
}
}
var spotlightBackground: SpotlightBackground {
switch self {
case .green:
return .ultraThinMaterial
default:
return .black
}
}
var instructionsViewType: InstructionsViewType? {
switch self {
case .red:
return .simple(instructions: ["Text1"], position: .below)
case .green:
return .simple(instructions: ["Text2", "Text22", "Text222", "Text2222"], position: .below)
case .yellow:
return .simple(instructions: ["Text3"], position: .above)
}
}
}
struct ContentView: View {
private var uniqueIdentifier: String = UUID().uuidString
private var tutorial: [TutorialChunks] =
[
.red,
.green,
.yellow
]
var body: some View {
VStack(spacing: 50) {
Rectangle()
.frame(width: 256, height: 256, alignment: .center)
.foregroundColor(Color.red)
.configureChunkForSpotlight(
parentIdentifier: self.uniqueIdentifier,
chunk: self.tutorial[0]
)
Rectangle()
.frame(width: 64, height: 128, alignment: .center)
.foregroundColor(Color.green)
.configureChunkForSpotlight(
parentIdentifier: self.uniqueIdentifier,
chunk: self.tutorial[1]
)
Circle()
.frame(width: 128, height: 128, alignment: .center)
.foregroundColor(.yellow)
.configureChunkForSpotlight(
parentIdentifier: self.uniqueIdentifier,
chunk: self.tutorial[2]
)
}
.padding()
.assembleSpotlightChunks(uniqueIdentifier: self.uniqueIdentifier, chunks: Array(TutorialChunks.allCases))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View File

@ -0,0 +1,202 @@
//
// Demo3.swift
// FutureFlowDemo
//
// Created by Muhand Jumah on 4/22/23.
//
import SwiftUI
struct Demo10: View {
enum Highlight: Identifiable {
case first
case second
case third
var id: Int {
self.hashValue
}
}
@State private var sw: Bool = false
@State private var currentHighlight: Highlight = .first
@Namespace private var namespace
var body: some View {
VStack {
ZStack {
VStack(spacing:50) {
// if(!sw) {
Rectangle()
.fill(.red)
.matchedGeometryEffect(id: Highlight.first.id,
in: namespace,
properties: .frame,
anchor: .center,
isSource: true)
.frame(width: 100, height: 200)
Rectangle()
.fill(.orange)
.matchedGeometryEffect(id: Highlight.second.id,
in: namespace,
properties: .frame,
anchor: .center,
isSource: true)
.frame(width: 50, height: 50)
Circle()
.fill(.black)
.matchedGeometryEffect(id: Highlight.third.id,
in: namespace,
properties: .frame,
anchor: .center,
isSource: true)
.frame(width: 200, height: 200)
}
}
// Spotlight goes here
self.currentHighlight.shape(self.namespace)
.matchedGeometryEffect(id: self.currentHighlight.id,
in: namespace,
properties: .frame,
anchor: .center,
isSource: false)
}
.onTapGesture {
withAnimation {
if(self.currentHighlight == .first) {
self.currentHighlight = .second
} else if (self.currentHighlight == .second){
self.currentHighlight = .third
} else {
self.currentHighlight = .first
}
}
}
}
}
struct Demo10_Previews: PreviewProvider {
static var previews: some View {
Demo10()
}
}
extension Demo10.Highlight {
func shape(_ ns: Namespace.ID) -> some View {
ZStack {
switch self {
case .first:
Rectangle()
.foregroundColor(.purple)
.matchedGeometryEffect(id: 998, in: ns)
case .second:
Rectangle()
.foregroundColor(.purple)
.matchedGeometryEffect(id: 998, in: ns)
case .third:
Circle()
.foregroundColor(.purple)
.matchedGeometryEffect(id: 998, in: ns)
}
}
}
}
//import SwiftUI
//
//struct Demo10: View {
// enum TestEnum {
// case first
// case second
//
// func shape(ns: Namespace.ID) -> some View {
// ZStack {
// switch self {
// case .first:
// Rectangle()
// .foregroundColor(.purple)
// .matchedGeometryEffect(id: 0, in: ns)
// .frame(width: 100, height: 100)
//
// case .second:
// Circle()
// .foregroundColor(.red)
// .matchedGeometryEffect(id: 0, in: ns)
// .frame(width: 300, height: 300)
// }
// }
// }
// }
//
// @Namespace private var ns
//
// @State private var currentShape:TestEnum = .first
// @State private var sw: Bool = false
//
// var body: some View {
// VStack {
// VStack(spacing: 0) {
// Button(action: {
// withAnimation {
// if(self.currentShape == .first) {
// self.currentShape = .second
// } else {
// self.currentShape = .first
// }
// }
// }) {
// Text("Switch")
// }
// .frame(height: 50)
//
// self.currentShape.shape(ns: self.ns)
// }
// .frame(maxWidth: 300, maxHeight: 350, alignment: .center)
// .border(Color.blue, width: 2)
//
// Divider()
//
// VStack(spacing: 0) {
// Button(action: {
// withAnimation {
// self.sw.toggle()
// }
// }) {
// Text("Switch")
// }
// .frame(height: 50)
//
// if(!self.sw) {
// Rectangle()
// .foregroundColor(.purple)
// .frame(width: 100, height: 100)
// } else {
// Circle()
// .foregroundColor(.red)
// .frame(width: 300, height: 300)
// }
// }
// .frame(maxWidth: 300, maxHeight: 350, alignment: .center)
// .border(Color.green, width: 2)
// }
// }
//}
//
//struct Demo10_Previews: PreviewProvider {
// static var previews: some View {
// Demo10()
// }
//}

View File

@ -0,0 +1,19 @@
////
//// 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 {
//
//
// }
//}

View File

@ -0,0 +1,257 @@
//
// Demo2.swift
// FutureFlowDemo
//
// Created by Muhand Jumah on 4/22/23.
//
import SwiftUI
struct User: Identifiable, Equatable {
let id: UUID = UUID()
let name: String
var message: String = ""
}
var users: [User] = [
.init(name: "Muhand", message: "This is just a test message, akjsdfh al ladsjf l;aksdj fl;ksajf;lasdfkfjksjfh"),
.init(name: "Hussam"),
.init(name: "Jareny"),
.init(name: "Mays"),
.init(name: "Nahla"),
.init(name: "Abbas"),
.init(name: "Jumah"),
.init(name: "Mathry"),
.init(name: "Katie"),
.init(name: "Valeria"),
.init(name: "Johanna")
]
struct DemoRoot: View {
enum DemoRootViews {
case Demo2
case Demo3
}
@Namespace private var rootNamespace
@Namespace private var namespace
@State private var currentDemoView: DemoRootViews = .Demo2
var body: some View {
VStack {
// Button(action: {
//// .onTapGesture {
// self.currentDemoView = .Demo3
//// }
// }) {
// Text("SWITCH")
// }
switch self.currentDemoView {
case .Demo2:
VStack {
HStack {
Demo2(namespace: self.namespace)
// .matchedGeometryEffect(id: 0, in: self.rootNamespace)
.frame(maxWidth: UIScreen.main.bounds.size.width * 0.7)
.padding(.horizontal, 15)
.onTapGesture {
self.switchViews()
}
Spacer()
}
Spacer()
}
case .Demo3:
Demo3(namespace: self.namespace)
// .matchedGeometryEffect(id: 0, in: self.rootNamespace)
.onTapGesture {
self.switchViews()
}
}
}
}
func switchViews() {
withAnimation(.spring()) {
switch self.currentDemoView {
case .Demo2:
self.currentDemoView = .Demo3
case .Demo3:
self.currentDemoView = .Demo2
}
}
}
}
struct Demo2: View {
var namespace: Namespace.ID
var body: some View {
VStack(alignment:.leading, spacing: 10) {
HStack(alignment:.center, spacing: 5) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing:0) {
ForEach(users, id:\.id) { user in
if
let last = users.last,
user == last {
Text("\(user.name)")
.matchedGeometryEffect(id: "\(user.id)_name", in: self.namespace)
} else {
Text("\(user.name), ")
.matchedGeometryEffect(id: "\(user.id)_name", in: self.namespace)
}
}
}
}
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment:.top, spacing:5) {
ForEach(users, id:\.id) { user in
ZStack {
Circle()
.matchedGeometryEffect(id: "\(user.id)_profile_image", in: self.namespace)
.frame(width: 32, height: 32, alignment: .center)
Text("\(String(user.name.prefix(1)))")
.foregroundColor(Color.white)
.matchedGeometryEffect(id: "\(user.id)_letter", in: self.namespace)
}
}
}
}
.zIndex(500)
}
Divider()
.matchedGeometryEffect(id: "divider", in: self.namespace)
HStack(alignment:.top, spacing:0) {
Text(users[0].message)
.matchedGeometryEffect(id: "\(users[0].id)_msg", in: self.namespace)
.lineLimit(1)
Spacer()
Text("10:51 pm")
.matchedGeometryEffect(id: "\(users[0].id)_msg_time", in: self.namespace)
}
}
.padding()
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(Color.black, lineWidth: 1),
alignment: .center
)
}
}
struct Demo3: View {
var namespace: Namespace.ID
var body: some View {
VStack(spacing:0) {
Spacer()
VStack(spacing:0) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 5) {
ForEach(users, id:\.id) { user in
VStack(spacing:5) {
ZStack {
Circle()
.fill(.black)
.matchedGeometryEffect(id: "\(user.id)_profile_image", in: self.namespace)
.frame(width: 64, height: 64)
Text("\(String(user.name.prefix(1)))")
.foregroundColor(Color.white)
.matchedGeometryEffect(id: "\(user.id)_letter", in: self.namespace)
}
Text(user.name)
.matchedGeometryEffect(id: "\(user.id)_name", in: self.namespace)
}
}
}
}.zIndex(500) // end of scrollview
Divider()
.matchedGeometryEffect(id: "divider", in: self.namespace)
ScrollView(.vertical, showsIndicators: true) {
VStack(alignment:.leading, spacing:0) {
VStack(alignment: .leading, spacing:5) {
HStack(spacing:5) {
Circle()
.matchedGeometryEffect(id: "\(users[0].id)_profile_image", in: self.namespace)
.frame(width: 24, height: 24)
Text("\(users[0].name)")
.matchedGeometryEffect(id: "\(users[0].id)_name", in: self.namespace)
Spacer()
Text("10:51 pm")
.matchedGeometryEffect(id: "\(users[0].id)_msg_time", in: self.namespace)
}
Divider()
Text("\(users[0].message)")
.matchedGeometryEffect(id: "\(users[0].id)_msg", in: self.namespace)
}
.padding(.horizontal, 15)
.padding(.vertical, 8)
.frame(
maxWidth: .infinity,
minHeight: 100,
maxHeight: 100,
alignment: .topLeading
)
.background(Color.white)
.cornerRadius(15)
}
.rotationEffect(.degrees(-180))
}
.rotationEffect(.degrees(-180))
.padding(.horizontal, 15)
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 300,
idealHeight: 300,
maxHeight: 300,
alignment:.topLeading
)
.background(Color.green)
.padding(.horizontal, -15)
}
.padding(.horizontal, 15) // end of vstack
}
.frame(minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .top)
.background(Color.red)
}
}
struct Demo2_Previews: PreviewProvider {
static var previews: some View {
let prevNamespcae: Namespace.ID = Namespace().wrappedValue
Group {
DemoRoot()
.previewDisplayName("DemoRoot")
Demo2(namespace: prevNamespcae)
.padding()
.previewDisplayName("Demo2")
Demo3(namespace: prevNamespcae)
.previewDisplayName("Demo3")
}
}
}

View File

@ -0,0 +1,20 @@
//
// FutureFlowDemoApp.swift
// FutureFlowDemo
//
// Created by Muhand Jumah on 4/21/23.
//
import SwiftUI
@main
struct FutureFlowDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
// Demo10()
// Demo2()
// DemoRoot()
}
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,36 @@
//
// FutureFlowDemoTests.swift
// FutureFlowDemoTests
//
// Created by Muhand Jumah on 4/21/23.
//
import XCTest
@testable import FutureFlowDemo
final class FutureFlowDemoTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -0,0 +1,41 @@
//
// FutureFlowDemoUITests.swift
// FutureFlowDemoUITests
//
// Created by Muhand Jumah on 4/21/23.
//
import XCTest
final class FutureFlowDemoUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}

View File

@ -0,0 +1,32 @@
//
// FutureFlowDemoUITestsLaunchTests.swift
// FutureFlowDemoUITests
//
// Created by Muhand Jumah on 4/21/23.
//
import XCTest
final class FutureFlowDemoUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}

View File

@ -1,202 +0,0 @@
//print("Hello, world!")
//protocol TutorialSteps {
//
//}
//
//import SwiftUI
//
//extension View {
// func tut() {
//
// }
//}
public final class FutureFlow {
public init() {
}
}
// Just a test
enum Tutorial: FlowChunk {
case settings
case listening
case soundwave
var spotLightShape: AnyView {
switch self {
case .listening:
return listeningSpotlight
case .settings:
return rectangleShape
default:
return defaultSpotLight
}
}
var spotlightBackground: AnyView {
switch self {
default:
return self.defaultSpotlightBackground
}
}
}
extension Tutorial {
fileprivate var defaultSpotLight: AnyView {
AnyView(
Rectangle()
.foregroundColor(.white)
.blur(radius: 1)
)
}
fileprivate var listeningSpotlight: AnyView {
AnyView(
Circle()
.foregroundColor(.white)
.blur(radius: 1)
)
}
}
// End of the test
protocol FlowChunk: Hashable {
var spotLightShape: AnyView { get }
var spotlightBackground: AnyView { get }
}
extension FlowChunk {
fileprivate var rectangleShape: AnyView {
AnyView (
Rectangle()
.foregroundColor(Color.white)
.blur(radius: 1)
)
}
fileprivate var circleShape: AnyView {
AnyView (
Circle()
.foregroundColor(Color.white)
.blur(radius: 1)
)
}
fileprivate var defaultSpotlightBackground: AnyView {
AnyView (
Color.black
.edgesIgnoringSafeArea(.all)
)
}
}
import SwiftUI
extension View {
func test<Chunk: FlowChunk>(parentIdentifier: String, chunk: Chunk) -> some View {
// let namespace = Namespace()
// FutureFlowManager.currentNamespaces[uniqueIdentifier] = namespace
var namespace: Namespace!
if let namespace2 = FutureFlowManager.currentNamespaces[parentIdentifier] {
namespace = namespace2
} else {
namespace = Namespace()
FutureFlowManager.currentNamespaces[parentIdentifier] = namespace
}
return self
.matchedGeometryEffect(
id: chunk.hashValue,
in: namespace.wrappedValue,
properties: .frame,
anchor: .center,
isSource: true
)
}
func test3<Chunk: FlowChunk>(uniqueIdentifier: String, chunks: [Chunk]) -> some View {
return HighlightingView(showTutorial: .constant(true),
chunks: chunks) {
}
}
}
internal struct SpotlightView<Chunk: FlowChunk>: View {
internal var chunk: Chunk
internal var namespace: Namespace.ID
internal var body: some View {
ZStack {
self.chunk.spotlightBackground
self.chunk.spotLightShape
.matchedGeometryEffect(
id: self.chunk.hashValue,
in: namespace,
properties: .frame, anchor: .center,
isSource: false
)
.blendMode(.destinationOut)
}
.compositingGroup()
}
}
internal struct HighlightingView<Content: View, Chunk: FlowChunk>: View {
@Namespace private var namespace
@Binding var showTutorial: Bool
let chunks: [Chunk]
let content: Content
@State private var currentIndex: Int = 0
init(showTutorial: Binding<Bool>, chunks: [Chunk], @ViewBuilder content: () -> Content) {
self._showTutorial = showTutorial
self.chunks = chunks
self.content = content()
}
var body: some View {
ZStack {
self.content
SpotlightView(
chunk: self.chunks[self.currentIndex],
namespace: self.namespace
)
.opacity(self.showTutorial ? 0.5 : 0)
.animation(
.easeOut,
value: self.showTutorial ? self.currentIndex : nil
)
}
}
}
struct ContentView: View {
@State private var tuts: [Tutorial] =
[
.listening,
.settings,
.soundwave
]
private let uniqueIdentifier: String = UUID().uuidString
var body: some View {
ZStack {
Text("TEST")
.test(parentIdentifier: self.uniqueIdentifier, chunk: self.tuts[0])//, chunk: self.tuts[0])
}
.test3(uniqueIdentifier: self.uniqueIdentifier, chunks: self.tuts)
}
}
internal class FutureFlowManager {
internal static var currentNamespaces: [String:Namespace] = [:]
}