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 10 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 @@ -48,8 +48,8 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle
private func bindOutput(reactor: Reactor) {
reactor.pulse(\.$hiddenTooltipView)
.bind(with: self) {
$1 ? $0.toolTipView.hidePopover()
: $0.toolTipView.showPopover()
$1 ? $0.toolTipView.hide()
Copy link
Collaborator

Choose a reason for hiding this comment

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

이거 toolTipView에 rx 뚫어주는거 어때용 ?ㅎㅎ 바로 bind(to: tollTipView.rx.isHidden 이렇게 쓰게요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

확실히 Binder 따로 만들어 놓는게 가독성이 좋겠네요!!

  • 따로 Binder 구현해 놓을게요

: $0.toolTipView.show()
}
.disposed(by: disposeBag)
}
Expand All @@ -58,7 +58,7 @@ final public class MemoriesCalendarPageTitleView: BaseView<MemoriesCalendarTitle
public override func setupUI() {
super.setupUI()

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

Expand All @@ -79,10 +79,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 +98,10 @@ 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
$0.hide()
}
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,54 @@ 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) {

guard let contentView else {
assertionFailure("No contentView assigned to BBToolTip")
return
}

superview?.addSubview(contentView)
updateLayout()

UIView.animate(withDuration: duration, delay: 0, options: options) { [weak self] in
guard let self else { return }
self.transform = CGAffineTransform.identity
self.alpha = 1
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: { [weak self] in
guard let self = self else { return }
self.contentView?.transform = transform
self.contentView?.alpha = 0
}, completion: { [weak self] _ 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()
setupAutoLayount()
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.

근데 여기는 왜 setupUI(), setupAttributes() 이렇ㄱ ㅔ안하구.. 따로 네이밍하셧나용?!

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.

오타, 네이밍 같이 수정해놓겠습니다 👍 👍

}

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 setupAutoLayount() {
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