Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: BBToolTip Action 처리 및 마이그레이션 작업 해요 #693

Merged
merged 11 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle
private let memoryCountLabel: BBLabel = BBLabel(.body1Regular, textColor: .gray200)
private let tipButton: UIButton = UIButton(type: .system)

private let toolTipView: BBToolTipView = BBToolTipView()
private let toolTipView: BBToolTip = BBToolTip(.monthlyCalendar)

// MARK: - Properties

Expand All @@ -47,18 +47,15 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle

private func bindOutput(reactor: Reactor) {
reactor.pulse(\.$hiddenTooltipView)
.bind(with: self) {
$1 ? $0.toolTipView.hidePopover()
: $0.toolTipView.showPopover()
}
.bind(to: toolTipView.rx.isHidden)
.disposed(by: disposeBag)
}


public override func setupUI() {
super.setupUI()

self.addSubviews(labelStack, memoryCountLabel, toolTipView)
self.addSubviews(labelStack, memoryCountLabel)
labelStack.addArrangedSubviews(titleLabel, tipButton)
}

Expand All @@ -79,10 +76,6 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle
$0.size.equalTo(20)
}

toolTipView.snp.makeConstraints {
$0.top.equalTo(tipButton.snp.bottom).offset(4)
$0.leading.equalToSuperview().offset(57.5)
}
}

public override func setupAttributes() {
Expand All @@ -102,9 +95,9 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle
$0.distribution = .fill
}

toolTipView.hidePopover()
toolTipView.toolTipType = .monthlyCalendar
// toolTipView.anchorPoint = CGPoint(x: 0.3, y: 0)
toolTipView.do {
$0.superview = tipButton
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@ public final class ManagementTableHeaderReactor {

// MARK: - Action

public enum Action { }
public enum Action {
case didTappedToolTipButton
}


// MARK: - Mutation

public enum Mutation { }
public enum Mutation {
case setToolTipHidden(Bool)
}


// MARK: - State

public struct State { }
public struct State {
@Pulse var isHidden: Bool = false
}


// MARK: - Properties
Expand All @@ -39,4 +45,19 @@ public final class ManagementTableHeaderReactor {
self.initialState = State()
}

public func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .didTappedToolTipButton:
return .just(.setToolTipHidden(!currentState.isHidden))
}
}

public func reduce(state: State, mutation: Mutation) -> State {
var newState = state
switch mutation {
case let .setToolTipHidden(isHidden):
newState.isHidden = isHidden
}
return newState
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public final class ManagementTableHeaderView: BaseView<ManagementTableHeaderReac
private let familyNameLabel: BBLabel = BBLabel(.head1, textColor: .gray200)
private let memberCountLabel: BBLabel = BBLabel(.body1Regular, textColor: .gray400)
private let familyNameEditButton: BBButton = BBButton()

private let familyNameToolTipeView: BBToolTip = BBToolTip(.familyNameEdit)

// MARK: - Properties

Expand Down Expand Up @@ -58,6 +58,9 @@ public final class ManagementTableHeaderView: BaseView<ManagementTableHeaderReac
$0.setImage(DesignSystemAsset.edit.image, for: .normal)
$0.addTarget(self, action: #selector(didTapFamilyNameEditButton(_:event:)), for: .touchUpInside)
}
familyNameToolTipeView.do {
$0.superview = familyNameEditButton
}
}

public override func setupAutoLayout() {
Expand All @@ -75,6 +78,18 @@ public final class ManagementTableHeaderView: BaseView<ManagementTableHeaderReac
}
}

public override func bind(reactor: Reactor) {
familyNameEditButton.rx.tap
.throttle(RxInterval._300milliseconds, scheduler: RxScheduler.main)
.map { Reactor.Action.didTappedToolTipButton }
.bind(to: reactor.action)
.disposed(by: disposeBag)

reactor.pulse(\.$isHidden)
.bind(to: familyNameToolTipeView.rx.isHidden)
.disposed(by: disposeBag)
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,52 @@ public typealias BBComponentPresentable = BBComponentShowable & BBComponentClosa
/// **Animate**, **CGAffineTransform**, **CABasicAnimation** 을 활용한 Animation 메서드를 정의하는 Protocol 입니다.
/// 해당 **BBComponentShowable** 프로토콜은 Component 객체를 보여주는 애니메이션을 정의하는 프로토콜입니다.
public protocol BBComponentShowable {
func showPopover(duration: TimeInterval, options: UIView.AnimationOptions, transform: CGAffineTransform, alpha: CGFloat)
func show(duration: TimeInterval, options: UIView.AnimationOptions, transform: CGAffineTransform, alpha: CGFloat)
}

/// **Animate**, **CGAffineTransform**, **CABasicAnimation** 을 활용한 Animation 메서드를 정의하는 Protocol 입니다.
/// 해당 **BBComponentClosable** 프로토콜은 Component 객체를 숨기는 애니메이션을 정의하는 프로토콜입니다.
public protocol BBComponentClosable {
func hidePopover(duration: TimeInterval, options: UIView.AnimationOptions, transform: CGAffineTransform, alpha: CGFloat)
func hide(duration: TimeInterval, options: UIView.AnimationOptions, transform: CGAffineTransform, alpha: CGFloat)
}


//MARK: - Extensions

public extension BBComponentShowable where Self: UIView {
/// showPopover 메서드 호출 시 Popover 애니메이션 효과를 실행합니다.
func showPopover(duration: TimeInterval = 0.3, options: UIView.AnimationOptions = [.curveEaseInOut], transform: CGAffineTransform = CGAffineTransform(scaleX: 0.1, y: 0.1), alpha: CGFloat = 1) {
self.transform = transform
self.alpha = alpha
public extension BBComponentShowable where Self: BBToolTip {
func show(duration: TimeInterval = 0.3, options: UIView.AnimationOptions = [.curveEaseInOut], transform: CGAffineTransform = CGAffineTransform(scaleX: 0.1, y: 0.1), alpha: CGFloat = 1) {

UIView.animate(withDuration: duration, delay: 0, options: options) { [weak self] in
guard let self else { return }
self.transform = CGAffineTransform.identity
self.alpha = 1
guard let contentView else {
assertionFailure("No contentView assigned to BBToolTip")
return
}

superview?.addSubview(contentView)
updateLayout()

UIView.animate(withDuration: duration, delay: 0, options: options) {
self.contentView?.transform = CGAffineTransform.identity
Copy link
Collaborator

Choose a reason for hiding this comment

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

..UIView.animate는 weak self를 쓸 필요가 없다고 합니당...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아하 UIView.animate에는 쓸필요가 없네요 감사합니다 :)

  • 추가로 GCD에도 쓸 필요가 없었네요 기존 GCD에 적용해 놨던 weak self 코드도 따로 이슈 파서 수정해 놓을 게요

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.

제가 이해한 바로는 GCD 같은 경우 타입 프로퍼티, 타입 메서드로 구성이 되어 있기때문에 weak self를 사용하지 않아도 되요

  • 다만 기존에 있던 작업을 다른 스레드에 보낸다면 순환 참조 문제가 발생 할 수 있다고 하더라고여

self.contentView?.alpha = 1
}
}
}

public extension BBComponentClosable where Self: UIView {
/// hidePopover 메서드 호출 시 Popover 애니메이션 효과를 제거합니다.
func hidePopover(duration: TimeInterval = 0.3, options: UIView.AnimationOptions = [.curveEaseInOut], transform: CGAffineTransform = CGAffineTransform(scaleX: 0.1, y: 0.1), alpha: CGFloat = 0) {
public extension BBComponentClosable where Self: BBToolTip {
func hide(
duration: TimeInterval = 0.3,
options: UIView.AnimationOptions = [.curveEaseInOut],
transform: CGAffineTransform = CGAffineTransform(scaleX: 0.1, y: 0.1),
alpha: CGFloat = 0
) {

UIView.animate(withDuration: duration, delay: 0, options: options) { [weak self] in
guard let self else { return }
self.transform = transform
self.alpha = alpha
} completion: { _ in
self.transform = CGAffineTransform.identity
}
UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
self.contentView?.transform = transform
self.contentView?.alpha = 0
}, completion: { _ in
self.contentView?.removeFromSuperview()
self.contentView?.transform = .identity
})

self.contentView?.layoutIfNeeded()
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
//
// BBDrawable.swift
// BBBaseToolTipView.swift
// Core
//
// Created by Kim dohyun on 9/19/24.
// Created by 김도현 on 10/27/24.
//

import UIKit

/// **UIBezierPath**, ** CALayer**, **CGMutablePath**을 활용한 draw 메서드를 정의하는 Protocol입니다.
protocol BBDrawable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

요거 없앤 이유가 있나영? 결국 메서드들은 쓰는 . 것같은뎅..?!

func drawToolTip(_ frame: CGRect, type: BBToolTipType, context: CGContext)
func drawToolTipArrowShape(_ frame: CGRect, type: BBToolTipType, path: CGMutablePath)
func drawToolTipBottomShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath)
func drawToolTipTopShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath)
}

import SnapKit
import Then

extension BBDrawable {
public class BBBaseToolTipView: UIView {
// MARK: - Properties
public var toolTipType: BBToolTipType

// MARK: - Intializer
public init(toolTipType: BBToolTipType) {
self.toolTipType = toolTipType
super.init(frame: .zero)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
context.saveGState()
drawToolTip(rect, type: toolTipType, context: context)
context.restoreGState()
}

/// drawToolTip 메서드 호출 시 **BBToolTipType** 에 해당하는 ToolTip Layout을 **CGContext** 내에서 드로잉 하는 메서드입니다.
///
/// drawToolTip에 frame은 UIView의 **draw(_: )** 메서드에서 호출되고 있습니다.
/// ToolTip Layout을 변경할 경우 **setNeedsDisplay** 메서드를 호출하시면 됩니다.
func drawToolTip(_ frame: CGRect, type: BBToolTipType, context: CGContext) {
// MARK: - Configure
private func drawToolTip(_ frame: CGRect, type: BBToolTipType, context: CGContext) {
let toolTipPath = CGMutablePath()

switch type {
Expand All @@ -40,13 +50,10 @@ extension BBDrawable {
context.setFillColor(type.configure.backgroundColor.cgColor)
context.fillPath()
}

/// drawToolTipArrowShape 메서드 호출 시 ToolTip에 Arrow 모양을 드로잉 하도록 실행합니다.
///
/// **BBToolTipType** 에 따라 Arrow의 위치가 배치됩니다.
func drawToolTipArrowShape(_ frame: CGRect, type: BBToolTipType, path: CGMutablePath) {

private func drawToolTipArrowShape(_ frame: CGRect, type: BBToolTipType, path: CGMutablePath) {
let margin: CGFloat = 16
let arrowTipXPosition = type.xPosition.rawValue * frame.width
let arrowTipXPosition = type.configure.xPosition.rawValue * frame.width
let adjustedArrowTipXPosition = min(max(arrowTipXPosition, margin + type.configure.arrowWidth / 2), frame.width - margin - type.configure.arrowWidth / 2)
let arrowLeft = adjustedArrowTipXPosition - type.configure.arrowWidth / 2
let arrowRight = adjustedArrowTipXPosition + type.configure.arrowWidth / 2
Expand All @@ -62,25 +69,20 @@ extension BBDrawable {
path.addLine(to: CGPoint(x: arrowRight, y: frame.height - type.configure.arrowHeight))
}
}

/// drawToolTipTopShape 메서드 실행 시 ToolTip의 **ContentShape** 영역들을 드로잉 하도록 실행합니다.
///
/// Note: - 해당 메서드는 **BBToolTipVerticalPosition** 이 Top일 경우 실행합니다.
func drawToolTipTopShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath) {

private func drawToolTipTopShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath) {
path.addArc(tangent1End: CGPoint(x: frame.maxX, y: toolTipType.configure.arrowHeight), tangent2End: CGPoint(x: frame.maxX, y: frame.maxY + frame.height), radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: frame.maxX, y: frame.maxY), tangent2End: CGPoint(x: frame.minX, y: frame.maxY), radius: cornerRadius)

path.addArc(tangent1End: CGPoint(x: frame.minX, y: frame.maxY), tangent2End: CGPoint(x: frame.minX, y: toolTipType.configure.arrowHeight), radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: frame.minX, y: toolTipType.configure.arrowHeight), tangent2End: CGPoint(x: frame.maxX, y: toolTipType.configure.arrowHeight), radius: cornerRadius)
}

/// drawToolTipBottomShape 메서드 실행 시 ToolTip의 **ContentShape** 영역들을 드로잉 하도록 실행합니다.
///
/// Note: - 해당 메서드는 **BBToolTipVerticalPosition** 이 Bottom일 경우 실행합니다.
func drawToolTipBottomShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath) {
private func drawToolTipBottomShape(_ frame: CGRect, toolTipType: BBToolTipType, cornerRadius: CGFloat, path: CGMutablePath) {
path.addArc(tangent1End: CGPoint(x: frame.maxX, y: frame.height - toolTipType.configure.arrowHeight), tangent2End: CGPoint(x: frame.maxX, y: 0), radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: frame.maxX, y: 0), tangent2End: CGPoint(x: frame.minX, y: 0), radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: frame.minX, y: 0), tangent2End: CGPoint(x: frame.minX, y: frame.height - toolTipType.configure.arrowHeight), radius: cornerRadius)
path.addArc(tangent1End: CGPoint(x: frame.minX, y: frame.height - toolTipType.configure.arrowHeight), tangent2End: CGPoint(x: frame.maxX, y: frame.height - toolTipType.configure.arrowHeight), radius: cornerRadius)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// BBTextToolTipView.swift
// Core
//
// Created by 김도현 on 10/27/24.
//

import UIKit

import SnapKit
import Then


public class BBTextToolTipView: BBBaseToolTipView {
// MARK: - Properties
private var contentLabel: BBLabel = BBLabel()

// MARK: - Intializer
public override var intrinsicContentSize: CGSize {
let contentWidth = contentLabel.intrinsicContentSize.width + 32
let contentHeight = contentLabel.intrinsicContentSize.height + toolTipType.configure.arrowHeight + 20
return CGSize(width: contentWidth, height: contentHeight)
}


public override init(toolTipType: BBToolTipType) {
super.init(toolTipType: toolTipType)
setupToolTipUI()
setupToolTipContent()
setupAutoLayout()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Configure
private func setupToolTipUI() {
addSubview(contentLabel)
}

private func setupToolTipContent() {
contentLabel.do {
$0.text = toolTipType.configure.contentText
$0.fontStyle = toolTipType.configure.font
$0.textAlignment = .center
$0.numberOfLines = 0
$0.textColor = toolTipType.configure.foregroundColor
$0.sizeToFit()
}

self.do {
$0.backgroundColor = .clear
}
}

private func setupAutoLayout() {
let position = toolTipType.configure.yPosition
let arrowHeight: CGFloat = toolTipType.configure.arrowHeight
let textPadding: CGFloat = 10

switch position {
case .bottom:
contentLabel.snp.makeConstraints {
$0.horizontalEdges.equalToSuperview().inset(16)
$0.bottom.equalToSuperview().inset((arrowHeight + textPadding))
$0.top.equalToSuperview().inset(textPadding)
}
case .top:
contentLabel.snp.makeConstraints {
$0.horizontalEdges.equalToSuperview().inset(16)
$0.top.equalToSuperview().inset((arrowHeight + textPadding))
$0.bottom.equalToSuperview().inset(textPadding)
}
}
}
}
Loading
Loading