Skip to content

Commit

Permalink
Merge pull request #231 from Team-Smeme/feat/#222-CoachingViewBinding
Browse files Browse the repository at this point in the history
[Feat] #222 - 코칭뷰 UI 구현
  • Loading branch information
joonBaek12 authored Nov 30, 2024
2 parents d5e34c3 + c928ff8 commit 3e12678
Show file tree
Hide file tree
Showing 12 changed files with 685 additions and 57 deletions.
131 changes: 78 additions & 53 deletions Smeem-iOS/Smeem-iOS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

45 changes: 45 additions & 0 deletions Smeem-iOS/Smeem-iOS/Global/HighlightModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// HighlightModifier.swift
// Smeem-iOS
//
// Created by Joon Baek on 11/26/24.
//

import SwiftUI

/*
# 사용법
- `diaryText`: 강조 표시를 적용할 전체 텍스트 (String)
- `corrections`: 강조할 문장의 목록 (CoachingResponse 배열)
- `highlightIndex`: 강조할 문장의 인덱스 (Int)

`Text` 뷰에 `.modifier(HighlightModifier(...))`로 적용

# 예제 코드

Text(diaryText)
.modifier(HighlightModifier(
diaryText: diaryText,
corrections: coachingResponse.corrections,
highlightIndex: currentIndex
))
*/

struct HighlightModifier: ViewModifier {
let diaryText: String
let corrections: [CoachingResponse]
let highlightIndex: Int

func body(content: Content) -> some View {
var attributedText = AttributedString(diaryText)

for (index, correction) in corrections.enumerated() {
if index == highlightIndex, let range = attributedText.range(of: correction.original_sentence) {
attributedText[range].backgroundColor = Color(UIColor.point)
attributedText[range].foregroundColor = Color(UIColor.smeemWhite)
}
}

return Text(attributedText)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// CoachingComparisonView.swift
// Smeem-iOS
//
// Created by 황찬미 on 11/1/24.
//

import SwiftUI

struct CoachingComparisonView: View {
@Binding var coachingResponse: CoachingResponse
@State private var textHeight: CGFloat = 0 // Text의 높이를 저장할 변수

var body: some View {
VStack(spacing: 20) {
VStack(alignment: .leading, spacing: 8) {
HStack {
Rectangle()
.frame(width: 2, height: textHeight)
.foregroundStyle(Color(UIColor.black))

Text("나의 일기")
.font(Font.custom("Pretendard", size: 16).weight(.medium))
.foregroundColor(Color(UIColor.black))
.background(GeometryReader { geometry in
Color.clear
.preference(key: TextHeightPreferenceKey.self, value: geometry.size.height)
})
}
.onPreferenceChange(TextHeightPreferenceKey.self) { value in
textHeight = value
}

Text(coachingResponse.original_sentence)
.font(Font.custom("Pretendard", size: 14)).fontWeight(.regular)
.frame(maxWidth: .infinity, alignment: .topLeading)
}
.padding(.leading, 18)
.padding(.trailing, 18)

VStack(alignment: .leading, spacing: 8) {
HStack {
Rectangle()
.frame(width: 2, height: textHeight)
.foregroundStyle(Color(UIColor.point))

Text("고친 문장")
.font(Font.custom("Pretendard", size: 16).weight(.medium))
.foregroundColor(Color(UIColor.point))
}

Text(coachingResponse.corrected_sentence)
.font(Font.custom("Pretendard", size: 14)).fontWeight(.medium)
.foregroundColor(Color(UIColor.point))
.frame(maxWidth: .infinity, alignment: .topLeading)
}
.padding(.leading, 18)
.padding(.trailing, 18)
}
}
}

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

//#Preview {
// CoachingComparisonView()
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CoachingTextView.swift
// Smeem-iOS
//
// Created by Joon Baek on 2024/10/16.
//

import SwiftUI

struct CoachingTextView: View {
@Binding var coachingText: String

var body: some View {

VStack(spacing: 16) {
Text("일기를 잘 작성하셨어요! \n내용이 명확하고 흥미로웠습니다.\n이제 몇 가지 문법적 오류를 수정해 볼까요?")
.font(Font.custom("Pretendard", size: 16))
// Colors 상수 등록 필요
.foregroundColor(Color(UIColor.smeemBlack))
.frame(maxWidth: .infinity, alignment: .leading)

ScrollView {
Text(coachingText)
.font(Font.custom("Pretendard", size: 16))
.foregroundColor(Color(UIColor.gray400))
.lineSpacing(0.375)
}

Spacer()
}
.padding(.horizontal, screenWidth * 0.048)
}
}
//
//@available (iOS 17, *)
//#Preview {
// CoachingTextView(coachingText: "")
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// CoachingContentView.swift
// Smeem-iOS
//
// Created by Joon Baek on 11/21/24.
//

import SwiftUI

struct CoachingContentView: View {
@Binding var currentIndex: Int
@Binding var coachingResponse: CoachingsResponse

var body: some View {
VStack(spacing: screenHeight * (20 / screenHeight)) {
Rectangle()
.frame(height: screenHeight * (8 / screenHeight))
.foregroundStyle(Color(UIColor.gray100))

TabView(selection: $currentIndex) {
ForEach(coachingResponse.corrections.indices, id: \.self) { item in
ScrollView {
VStack(spacing: screenWidth * (8 / screenWidth)) {
CoachingComparisonView(coachingResponse: $coachingResponse.corrections[item])

CoachingExplanationView(coachingResponse: $coachingResponse.corrections[item])
}
}
}
}
.frame(width: screenWidth, height: screenHeight * (286 / screenHeight), alignment: .top)
.tabViewStyle(.page(indexDisplayMode: .never))
.padding(.horizontal, screenWidth * (16 / screenWidth))

PageControl(currentPage: $currentIndex,
coachingResponse: $coachingResponse)
}
}
}

#Preview {
@State var defaultIndex: Int = 0
@State var coachingResponse = CoachingsResponse.empty
CoachingContentView(currentIndex: $defaultIndex, coachingResponse: $coachingResponse)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CoachingExplanationView.swift
// Smeem-iOS
//
// Created by 황찬미 on 11/16/24.
//

import SwiftUI

struct CoachingExplanationView: View {
@Binding var coachingResponse: CoachingResponse

var body: some View {
HStack() {

ZStack(alignment: .leading) {
GeometryReader { geomerty in
Rectangle()
.fill(Color(UIColor.gray100))
.frame(height: geomerty.size.height)
.cornerRadius(3)
}

Text(coachingResponse.reason)
.font(Font.custom("Pretendard", size: 14).weight(.regular))
.foregroundStyle(.black)
.padding(12)
}
.fixedSize(horizontal: false, vertical: true)
.padding(.leading, 18)
.padding(.trailing, 18)
}
}
}

//#Preview {
// CoachingExplanationView()
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// CustomSegmentedControl.swift
// Smeem-iOS
//
// Created by Joon Baek on 2024/10/22.
//

import SwiftUI

struct CustomSegmentedControl: View {
@Binding var selectedIndex: Int
let options: [String]

var body: some View {
HStack(spacing: 0) {
ForEach(options.indices, id: \.self) { index in
SegmentButton(
title: options[index],
isSelected: selectedIndex == index,
action: { selectedIndex = index }
)
}
}
.background(Color.gray.opacity(0.2))
.cornerRadius(8)
}
}

struct SegmentButton: View {
let title: String
let isSelected: Bool
let action: () -> Void

var body: some View {
Button(action: action) {
Text(title)
.padding(.vertical, 8)
.padding(.horizontal, 11)
.frame(maxWidth: .infinity)
.background(backgroundColor)
.foregroundColor(foregroundColor)
.font(Font(UIFont.c5))
}
}

private var backgroundColor: Color {
isSelected ? (isCoachingOn ? Color(UIColor.point) : Color(UIColor.gray200)) : Color(UIColor.gray100)
}

private var foregroundColor: Color {
isSelected ? .white : Color(UIColor.gray500)
}

private var isCoachingOn: Bool {
title == "코칭 ON"
}
}

struct PreviewWrapper: View {
@State private var selectedIndex = 0
let options = ["코칭 OFF", "코칭 ON"]

var body: some View {
CustomSegmentedControl(selectedIndex: $selectedIndex, options: options)
.frame(height: 40)
.padding(117)
}
}

@available(iOS 17.0, *)
#Preview {
PreviewWrapper()
}

Loading

0 comments on commit 3e12678

Please sign in to comment.