Skip to content

Commit

Permalink
[CAT-176] Onboarding paging뷰 ui 및 기능구현 (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jihyun247 authored Aug 15, 2024
1 parent 6560bbd commit 205135d
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 91 deletions.
7 changes: 3 additions & 4 deletions Projects/Feature/Feature/Sources/AppCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import SplashFeature
import HomeFeature
import HomeFeatureInterface
import OnboardingFeature
import OnboardingFeatureInterface
import PushService

import ComposableArchitecture
Expand Down Expand Up @@ -92,14 +91,14 @@ public struct AppCore {
state.onboarding = OnboardingCore.State()
return .none

case .splash:
return .none

case .home:
return .none

case .onboarding:
return .none

default:
return .none
}
}
}
1 change: 0 additions & 1 deletion Projects/Feature/Feature/Sources/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import SplashFeature
import HomeFeature
import HomeFeatureInterface
import OnboardingFeature
import OnboardingFeatureInterface

import ComposableArchitecture

Expand Down

This file was deleted.

28 changes: 0 additions & 28 deletions Projects/Feature/OnboardingFeature/Interface/OnboardingView.swift

This file was deleted.

3 changes: 1 addition & 2 deletions Projects/Feature/OnboardingFeature/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ let project: Project = .makeTMABasedProject(
scripts: [],
targets: [
.sources,
.interface,
.tests,
.testing,
.example
],
dependencies: [
.interface: [
.sources: [
.dependency(rootModule: Domain.self)
]
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// OffsetReader.swift
// OnboardingFeature
//
// Created by 김지현 on 8/14/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import SwiftUI

struct OffsetKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}

extension View {
@ViewBuilder
func offsetX(_ addObserver: Bool, completion: @escaping (CGFloat) -> Void) -> some View {
self
.frame(maxWidth: .infinity)
.overlay {
if addObserver {
GeometryReader { geometry in
let minX = geometry.frame(in: .global).minX
Color.clear
.preference(key: OffsetKey.self, value: minX)
.onPreferenceChange(OffsetKey.self, perform: completion)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// OnboardingCore.swift
// OnboardingFeature
//
// Created by devMinseok on 7/22/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import Foundation

import ComposableArchitecture

@Reducer
public struct OnboardingCore {
@ObservableState
public struct State: Equatable {
public init() { }
var data: [OnboardingItem] = OnboardingItemsData
var fakedData: [OnboardingItem] = []
var width: CGFloat = .zero
var currentIdx: Int = 0
var currentItemID: String = ""
}

public enum Action: BindableAction {
case onApear
case calculateOffset(CGFloat, OnboardingItem)
case dragStart
case _timerStart
case _timerEnd
case _timerTicked
case _nextPage(Int)
case _resetTofront
case binding(BindingAction<State>)
}

public init() { }

@Dependency(\.continuousClock) var clock
@Dependency(\.mainQueue) var mainQueue

public var body: some ReducerOf<Self> {
BindingReducer()
Reduce(self.core)
}

private func core(_ state: inout State, _ action: Action) -> EffectOf<Self> {
enum CancelID { case timer, timerDebounce }

switch action {
case .onApear:
state.currentItemID = state.data.first!.id.uuidString
state.fakedData = state.data
guard var first = state.data.first,
var last = state.data.last else { return .none }
first.id = .init()
last.id = .init()
state.fakedData.append(first)
state.fakedData.insert(last, at: 0)
return .run { send in
await send(._timerStart)
}

case .calculateOffset(let minX, let item):
let fakeIndex = state.fakedData.firstIndex(of: item) ?? 0
state.currentIdx = state.data.firstIndex { item in
item.id.uuidString == state.currentItemID
} ?? 0
let pageOffset = minX - state.width * CGFloat(fakeIndex)

let pageProgress: CGFloat = pageOffset / state.width
if -pageProgress < 1.0 {
if state.fakedData.indices.contains(state.fakedData.count - 1) {
state.currentItemID = state.fakedData[state.fakedData.count - 1].id.uuidString
}
}
if -pageProgress > CGFloat(state.fakedData.count - 1) {
if state.fakedData.indices.contains(1) {
state.currentItemID = state.fakedData[1].id.uuidString
}
}
return .none

case .dragStart:
let timerEndAction: Effect<Action> = .run { send in await send(._timerEnd) }
let timerStartAction: Effect<Action> = .run { send in await send(._timerStart) }
.debounce(id: CancelID.timerDebounce, for: .seconds(2), scheduler: self.mainQueue)
return .merge(
timerEndAction,
timerStartAction
)

case ._timerStart:
return .run { send in
for await _ in self.clock.timer(interval: .seconds(3)) {
await send(._timerTicked)
}
}
.cancellable(id: CancelID.timer)

case ._timerEnd:
return .cancel(id: CancelID.timer)

case ._timerTicked:
let index = state.fakedData.firstIndex { item in
item.id.uuidString == state.currentItemID
} ?? 0
return .run { [index = index] send in
if index == 4 {
await send(._resetTofront)
await send(._nextPage(2), animation: .easeInOut(duration: 0.3))
} else {
await send(._nextPage(index + 1), animation: .easeInOut(duration: 0.3))
}
}

case ._nextPage(let index):
state.currentItemID = state.fakedData[index].id.uuidString
return .none

case ._resetTofront:
state.currentItemID = state.fakedData[1].id.uuidString
return .none

default:
return .none
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// OnboardingItem.swift
// OnboardingFeature
//
// Created by 김지현 on 8/9/24.
// Copyright © 2024 PomoNyang. All rights reserved.
//

import SwiftUI

import DesignSystem

public struct OnboardingItem: Equatable, Identifiable {
public var id: UUID = .init()
let image: Image
var title: String
let subTitle: String
}

let OnboardingItemsData: [OnboardingItem] = [
OnboardingItem(
image: Image(systemName: "star"),
title: "모하냥과 함께 집중시간을 늘려보세요",
subTitle: "고양이 종에 따라 성격이 달라요.\n취향에 맞는 고양이를 선택해 몰입해 보세요."),
OnboardingItem(
image: Image(systemName: "star.fill"),
title: "다른 앱을 실행하면 방해 알림을 보내요",
subTitle: "뽀모도로를 실행한 후, 다른 앱을 사용하면\n설정한 주기로 방해 알림을 보내드려요."),
OnboardingItem(
image: Image(systemName: "house"),
title: "집중과 휴식 반복을 통해 몰입을 관리해요",
subTitle: "일정 시간 집중과 휴식을 반복해 번아웃을 방지하고\n짧은 시간의 몰입을 경험해보세요.")
]

Loading

0 comments on commit 205135d

Please sign in to comment.