Skip to content

Style/#365 나의여정 UI 및 WriteQuestVC 리팩토링#382

Merged
y-eonee merged 39 commits intodevelopfrom
style/#365-나의여정-ui
Feb 26, 2026

Hidden character warning

The head ref may contain hidden characters: "style/#365-\ub098\uc758\uc5ec\uc815-ui"
Merged

Style/#365 나의여정 UI 및 WriteQuestVC 리팩토링#382
y-eonee merged 39 commits intodevelopfrom
style/#365-나의여정-ui

Conversation

@y-eonee
Copy link
Collaborator

@y-eonee y-eonee commented Feb 24, 2026

🔗 연결된 이슈

📄 작업 내용

  • 퀘스트 관련 UI를 전반적으로 리팩토링하고 변경사항을 반영했습니다.
  • WriteQuest 부분을 WriteQuestBaseViewController를 만들어 상속받도록 리팩토링 했습니다.
  • 공통여정 분기처리했습니다.
  • 변경사항을 반영하면서 삭제된 파일이 있습니다. (Complete~View, Archive에서 사용되었던 컴포넌트들)
  • 또한 기존에 WriteQuest로 폴더가 분리되어있었는데, Quest 폴더에 하나로 통합했습니다.
  • 주리언니가 Archive에 버튼 만들어둔것은 충돌 해결해서 반영되어있습니당
구현 내용 퀘스트 작성 아카이브
GIF

영상을 다시 찍기가 귀찮은데 부들부들한 텍스트필드가 될 수 있도록 리팩토링했습니다. 기존에 QuestionType은 스크롤뷰 없이 뷰를 구현했는데 ScrollView로 UI를 바꿨습니다!!

image

+) 근데 소희언니가 피알 염탐하고 이거 틀렷다고 알려줘서 키보드 위에 text count label 오도록 수정햇슴다 ^^*!!

2026.02.27 최종

구현 내용 질문형 행동형
GIF

💻 주요 코드 설명

WriteQuestBaseViewController

ActiveType VC와 QuestionType VC는 앱잼때부터 가져오던 역사가 긴 친구들로... 점점 기능이 추가되고, 공통된 로직도 많다보니 뷰컨트롤러 코드를 보기가 너무 힘들더라구요... 그래서 공통된 로직은 따로 WriteQuestBaseVC에 넣어 상속받도록 했습니다.
WriteQuestBaseProtocol은 View에서 채택하는 프로토콜로, 부모 뷰컨에서 필요한 컴포넌트를 가져오는 역할을 합니다.

protocol WriteQuestBaseProtocol where Self: UIView { // View에서 채택 
    var scrollView: UIScrollView { get }
    var questTextView: UITextView { get }
    var questCountLabelView: UIView { get }
    var tipTagView: UIView { get }
}

class WriteQuestBaseViewController<RootView: BaseView & WriteQuestBaseProtocol>:
    BaseViewController, UIGestureRecognizerDelegate, BackNavigable {
    
    let rootView: RootView
    var isKeyboardUsed: Bool = false
    private var keyboardFrameInWindow: CGRect = .zero
    private var currentKeyboardOffset: CGFloat = 0
    private var previousTextViewHeight: CGFloat = 0

이곳에는 키보드 관련 로직이 들어있습니다! 또한 공통되는 objc 함수들이 포함되어있습니다.

키보드 대응

새롭게 바뀐 키보드 대응방식을 반영했습니다.

  1. 현재 포커스가 키보드에 가까워질 때, 뷰 높이가 그에 맞춰 조금씩 올라갑니다. => TextGrowth 함수
  2. 입력 중 키보드를 내리고, 다시 키보드를 열면 포커스된 위치에 맞추어 뷰가 내려가야합니다. => CaretRect 함수
    CaretRect은 저도 처음 알게 되었는데 현재 커서 위치의 삽입 좌표점이라고 합니다 !!

공통 여정 분기처리

공통 질문의 경우, 질문형 퀘스트밖에 존재하지 않고 + questNumber가 nil 이라고 합니다. 그래서 configure에 있는 questNumber를 옵셔널로 바꿔주었습니다. (shout out 개맹님) 옵셔널인지에 따라 questScope라는 값으로 구분하게 됩니다!

UITextView 높이 동적대응

AS-IS

  • 텍스트 필드 내에서 스크롤이 가능했음.
  • height 고정

TO-BE

  • 최소 높이 지정
  • 최소 높이보다 내용을 더 많이 작성하게되면 뷰 높이가 늘어남. 이를 구현하기 위헤 UITextView 스크롤 불가 설정
func updateTextViewHeight() {
        let width = self.frame.width
        let fittingSize = CGSize(width: width, height: .greatestFiniteMagnitude) // 높이를 무한대로 열고 필요한 높이 계산 
        let estimatedHeight = ceil(textView.sizeThatFits(fittingSize).height) // 예상 높이 
        let containerMinHeight = 268.adjustedH
        let textViewMinHeight = 196.adjustedH
        
        containerHeightConsraint?.update(offset: max(containerMinHeight, estimatedHeight + 72.adjustedH)) // 72: 둘 사이 bottom offset이 72임 
        textViewHeightConstraint?.update(offset: max(textViewMinHeight, estimatedHeight))
        superview?.layoutIfNeeded()
        layoutIfNeeded()
    }
}

ByeBooNavigationBar 타입 추가

퀘스트 작성 부분의 네비게이션바 타입이 기존에는 없던 case여서 confirmAndBack 이라는 case를 추가했습니다.
또한, 텍스트의 bar Appearance를 지정해주었고, makeBarItem에 이미지 뿐만 아니라 String으로도 아이템을 만들 수 있도록 파라미터를 수정했습니다. 확장성있는 컴포넌트..개맹업개맹업

private static func makeBarButtonItem(
        image: UIImage? = nil, // 이미지와 String을 옵셔널로 지정했습니다. 
        title: String? = nil,
        target: BaseViewController,
        action: Selector?
    ) -> UIBarButtonItem {
        if let image {
            return UIBarButtonItem(
                image: image.withTintColor(.white).withRenderingMode(.alwaysOriginal),
                style: .plain,
                target: target,
                action: action
            )
        }
        
        if let title {
            return UIBarButtonItem(
                title: title,
                style: .plain,
                target: target,
                action: action
            )
        }
        
        return UIBarButtonItem( // 이건 오류 방지용 기본 리턴입니당 
            title: "기본",
            style: .plain,
            target: target,
            action: action
        )
    }

이에 따라 confirm Button을 텍스트필드에 입력된 값에 따라 update해주던 함수를 사용하지 않고, 대신 navigationItem에 접근하여 isEnabled를 t/f로 바꿔주는 방식을 사용합니다.

image

@y-eonee y-eonee requested review from dev-domo and juri123123 and removed request for juri123123 February 24, 2026 15:12
@y-eonee y-eonee self-assigned this Feb 24, 2026
@y-eonee y-eonee changed the title Style/#365 나의여정 UI Style/#365 나의여정 UI 및 WriteQuestVC 리팩토링 Feb 24, 2026
@sohee6989
Copy link

부들부들 텍필 기대되네여~~~~~

Copy link
Collaborator

@dev-domo dev-domo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정말정말 고생하셨습니다. QuestScope 잘 추가해주셨네요!


import UIKit

protocol WriteQuestBaseProtocol where Self: UIView {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고능하디 고능하옵나이다 파트장님

Copy link
Collaborator

@juri123123 juri123123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이견없습니다

@@ -31,19 +28,19 @@ final class WriteActiveTypeQuestView: BaseView {
private let grayTag = ByeBooFilledTag(tagType: .smallGray, text: "선택")
private let thinkTitleLabel = UILabel()
private(set) var questTextField = QuestTextField(type: .activation)
private(set) var confirmButton = ByeBooButton(titleText: "완료하기", type: .disabled)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼이 사라졌나요 ?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼이 사라지고 네비게이션바로 옮겨갔습니다 !!

var questTextView: UITextView {
questTextField.textView
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공백 없애줍시당 !

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LEGEND

final class WriteActiveTypeQuestViewController: BaseViewController {

private let rootView = WriteActiveTypeQuestView()
final class WriteActiveTypeQuestViewController: WriteQuestBaseViewController<WriteActiveTypeQuestView> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow

}

class WriteQuestBaseViewController<RootView: BaseView & WriteQuestBaseProtocol>:
BaseViewController, UIGestureRecognizerDelegate, BackNavigable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하는 extension에서 채택해주고 나눠주면 좋을 것 같아용

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 익스텐션으로 나누고 싶었는데 .. 제네릭 클래스여서 extension에서 @objc를 붙이는건 지원이 따로 안되는 것 같아요 .. 에러가 나네요 ㅠㅠ

Conformance of generic class 'WriteQuestBaseViewController<RootView>' to @objc protocol 'BackNavigable' cannot be in an extension

Comment on lines +137 to +147
func adjustViewForKeyboard(mode: KeyboardAdjustMode) {
guard isKeyboardUsed else { return }
guard !keyboardFrameInWindow.isEmpty else { return }

switch mode {
case .textGrowth:
adjustViewForTextGrowth()
case .caretTracking:
adjustViewForCurrentCaret()
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

진짜 레전드인거같애

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

대 나 연
이보다 완벽한코드는없다

@y-eonee y-eonee merged commit defd3e3 into develop Feb 26, 2026
@y-eonee y-eonee deleted the style/#365-나의여정-ui branch February 26, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants