오랜만입니다!!!
지금까지 취준 + 회사일로 정신이 없었는데, 조금씩 적응도 되어가고 있고
남는 시간에 기록과 공부를 하기 위해 블로그를 다시 써보려고 합니다!
아마도 지금 회사에서 코딩을 하면서 다루는 SwiftUI와 TCA에 대해 앞으로 포스팅을 많이 할 것 같은데
오늘은 오랜만에 돌아온 기념으로 간단한 것부터 포스팅해보려고 합니다. (포스팅 내에서는 편한 말투로!)
많은 앱에서 어떤 버튼 혹은 뷰에 대한 설명을 해주기 위해 툴팁뷰 혹은 툴킷을 보여주는 것을 심심치 않게 볼 수 있다. 오늘은 이 부분을 구현해보려고 한다.
1. Tool tip이란?

"사람들이 앱의 기능을 발견하는 데 도움이 되는 팁을 표시합니다."
iOS 17부터 공식적으로 생긴 TipKit이라는 프레임워크를 보면 위와 같은 설명이 적혀있다.
즉, 어디까지나 도움을 주는 역할이며, 앱의 핵심 기능을 사용하는 데 필수적이어서는 안되고,
사용자가 앱의 숨겨진 기능이나 덜 알려진 기능을 발견할 수 있도록 돕는 도구이다.

하지만 TipKit은 최소버전이 17이다.
즉, 17보다 최소버전이 낮은 앱에는 적용이 불가능하다.
또한 간단한 텍스트만 담을 툴팁이 필요한 경우가 있을 수 있다.
이런 경우를 위해 커스텀 툴팁을 만들어보려고 한다.
2. 구현 아이디어

구현 아이디어는 간단하다.
삼각형을 그리고, 툴팁에 들어갈 Text의 백그라운드 색을 적용한 뒤
라운드한 사각형처럼 보이게 하기 위해서 cornerRadius를 적용한 것이다.
이때 조금 더 유연하게 위치를 시킬 수 있도록 삼각형이 거꾸로 있는 경우와
삼각형의 위치를 6개로 나누어서 구현을 하였다.
3. 코드
1. 삼각형 만들기
검색을 해보면 삼각형을 만드는 방법이 나오는데
그 방법을 활용해서 삼각형을 만들었다.
하지만 위에서 언급했듯
삼각형이 거꾸로 있는 경우를 고려하여 만들었다.
struct Triangle: Shape {
var pointingUp: Bool
func path(in rect: CGRect) -> Path {
var path = Path()
if pointingUp {
path.move(to: CGPoint(x: rect.midX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
} else {
path.move(to: CGPoint(x: rect.midX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
}
path.closeSubpath()
return path
}
}
2. 삼각형 위치 고려하기
그리고 또 하나 고려했다는 포인트가 바로
삼각형의 위치이다.
enum ToolTipAlignment {
case topLeft
case topRight
case topCenter
case bottomLeft
case bottomRight
case bottomCenter
}
열거형을 사용해서
6가지 경우로 나누었고
이를 활용해서 위치를 조정할 예정이다.
3. Custom Tool Tip View
이제 위의 열거형과 삼각형을 가지고
툴팁뷰를 만들어보자.
struct ToolTipView: View {
let text: String
let color: Color
let alignment: ToolTipAlignment
init(_ text: String, color: Color = .white, alignment: ToolTipAlignment = .topLeft) {
self.text = text
self.color = color
self.alignment = alignment
}
var body: some View {
VStack(alignment: horizontalAlignment, spacing: -6) {
// 삼각형이 위에 있을지, 아래에 있을지 나눈 코드
if alignment == .bottomLeft || alignment == .bottomRight || alignment == .bottomCenter {
tooltipContent
tooltipTriangle
} else {
tooltipTriangle
tooltipContent
}
}
}
// 삼각형이 앞쪽에 있을지, 뒤쪽에 있을지, 가운데에 있을지 결정하는 코드
private var horizontalAlignment: HorizontalAlignment {
switch alignment {
case .topLeft, .bottomLeft:
return .leading
case .topRight, .bottomRight:
return .trailing
case .topCenter, .bottomCenter:
return .center
}
}
// 툴팁에 들어갈 text와 백그라운드 뷰에 관한 코드
private var tooltipContent: some View {
Text(text)
.foregroundColor(.black)
.font(.system(size: 14))
.fontWeight(.semibold)
.padding(8)
.background(color)
.cornerRadius(8)
}
// 일반적인 삼각형과 역삼각형 중 어떤 거를 쓸지 고려하는 코드
private var tooltipTriangle: some View {
Triangle(pointingUp: alignment == .topLeft || alignment == .topRight || alignment == .topCenter)
.fill(color)
.frame(width: 20, height: 15)
.padding(.leading, horizontalAlignment == .leading ? 10 : 0)
.padding(.trailing, horizontalAlignment == .trailing ? 10 : 0)
}
}
우선,
var body부터 보자면 열거형에 따라
삼각형의 위치를 아래에 둘 건지, 위에 둘건지 나눴다.
그리고 연산 프러퍼티인 tooltipTriangle에서
case에 따라 일반 삼각형 혹은 역삼각형이 그려지게 만들었다.
또 tooltipContent에서는 툴팁 안에 들어갈
string 값을 넣어주고 색을 채워주었고, 코너도 동그랗게 만들었다.
4. 사용 예시
구현 아이디어에서 보여준 이미지처럼
쓰고 싶은 곳에 ToolTipView를 호출하여 사용하면 끝!!이다.
struct TestToolTipView: View {
var body: some View {
ZStack {
Color.green.ignoresSafeArea()
VStack {
HStack {
ToolTipView("Top Left", alignment: .topLeft)
Spacer()
ToolTipView("Top Right", alignment: .topRight)
}
.padding()
ToolTipView("Top Center", alignment: .topCenter)
Spacer()
ToolTipView("Bottom Center", alignment: .bottomCenter)
HStack {
ToolTipView("Bottom Left", alignment: .bottomLeft)
Spacer()
ToolTipView("Bottom Right", alignment: .bottomRight)
}
.padding()
}
}
}
}
4. 부족한 부분
아직 해당 코드만 만들어놓고
앱에 적용하지 않았다.
그렇기 때문에 anchor와 같은 것은 미처 생각하지 못했다.
즉 실제로 툴팁뷰를 뷰에 적용한다고 하였을 때,
어떤 위치에 어떤 방식으로 적용시켜야 할지
지금 코드로는 어려울 수 있다.
이 부분이 해결된다면 더 보완해서 코드를 올려보겠다.