Mask
SwiftUI
에서는 mask()
수정자를 사용하여 이미지 또는 텍스트를 마스크 할 수 있습니다.
간단한 예제를 보면서 어떻게 사용할 수 있는지 알아보도록 할게요.
VStack {
Image("sample2")
.resizable()
.aspectRatio(contentMode: .fit)
.mask(
ZStack {
Circle()
.frame(width: 208, height: 208)
Circle()
.frame(width: 90, height: 90)
.offset(x: 80, y: 55)
}
)
.frame(maxHeight: .infinity)
}
이제 mask
를 활용해서 평점을 줄 수 있는 버튼을 하나 만들어 줄까 해요.
우선 별이 5개인 뷰를 만들어줍니다.
import SwiftUI
struct maskView: View {
var body: some View {
ZStack {
starsView
}
}
private var starsView: some View {
HStack {
ForEach(1..<6) { star in
Image(systemName: "star.fill")
.foregroundColor(.gray)
}
}
}
}
그리고 starsView
에 overlay
와 mask
를 사용하여 회색인 별을 노란색으로 채워주겠습니다.
ZStack {
starsView
.overlay(
GeometryReader { geo in
Rectangle()
.fill(Color.yellow)
.mask(starsView)
}
)
}
이제 frame
을 적용해서 숫자가 올라갈수록 rectangle
의 frame
넓이도 바뀔 수 있도록 구현해줘야 합니다.
@State
속성을 사용하여 rating
을 0으로 설정해줍니다.
struct maskView: View {
@State var rating: Int = 1
var body: some View {
ZStack {
starsView
.overlay(
GeometryReader { geo in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
)
}
}
@State
의 rating
값을 하나씩 올려보면 별의 색도 채워지는 것을 확인할 수 있습니다. 그리고 overlay
뷰를 생성하고 위에 있는 코드를 옮겨줍니다.
var body: some View {
ZStack {
starsView
.overlay(
overlayView
)
}
}
private var overlayView: some View {
GeometryReader { geo in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
}
overlayView
아래 mask
를 적용해볼게요.
.overlay(
overlayView
.mask(starsView)
)
자, 그럼 starsView
로 돌아가서 버튼을 눌렀을 때마다 별이 채워질 수 있도록해줘야겠죠? foregroundColor
아래 onTapGesture
을 추가해줍니다.
private var starsView: some View {
HStack {
ForEach(1..<6) { star in
Image(systemName: "star.fill")
.foregroundColor(.gray)
.onTapGesture {
rating = star
}
}
}
}
withAnimation
을 사용하면 더 보기 좋아집니다.
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)) {
rating = star
}
}
그런데 한 가지 문제가 있습니다.
별 한 개 에서 -> 다섯 개로 높아질 때는 정상적으로 클릭이 가능한데, 5에서 4로 내리면 클릭이 되지를 않습니다.
이유는 우리는 지금 overlayView
를 클릭하고 있기 때문입니다.
onTapGesture
은 starsView
에 줬는데 말이죠. 이럴 때는 overlayView
에 .allowsHitTesting(false)
를 할당해주면 완벽히 해결됩니다.
private var overlayView: some View {
GeometryReader { geo in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
.allowsHitTesting(false)
}
이제 앱을 닫았을 때도 평점이 저장될 수 있도록 UserDefaults
를 한번 사용해볼게요.
starsView
의 onTapGestire
아래에 다음과 같이 userDefaults
를 추가합니다.
private var starsView: some View {
HStack {
ForEach(1..<6) { star in
Image(systemName: "star.fill")
.foregroundColor(.gray)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)) {
rating = star
UserDefaults.standard.setValue(rating, forKey: "rating_key")
}
}
}
}
}
그리고 @State
도 아래와 같이 수정합니다.
@State var rating: Int = UserDefaults.standard.integer(forKey: "rating_key")
자 이렇게 앱을 종료 후 재 시작을 해도 부여해줬던 평점이 그대로 저장된 것을 확인할 수 있습니다.
import SwiftUI
struct maskView: View {
@State var rating: Int = UserDefaults.standard.integer(forKey: "rating_key")
var body: some View {
ZStack {
starsView.overlay(overlayView.mask(starsView))
}
}
private var overlayView: some View {
GeometryReader { geo in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
.allowsHitTesting(false)
}
private var starsView: some View {
HStack {
ForEach(1..<6) { star in
Image(systemName: "star.fill")
.foregroundColor(.gray)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)) {
rating = star
UserDefaults.standard.setValue(rating, forKey: "rating_key")
}
}
}
}
}
}
이미지도 추가해볼게요.
전체 코드
<hide/>
import SwiftUI
struct maskView: View {
@State var rating: Int = UserDefaults.standard.integer(forKey: "rating_key")
var body: some View {
VStack(spacing: 20) {
starsView.overlay(overlayView.mask(starsView))
Image("sample1")
.resizable()
.scaledToFit()
.mask(
ZStack {
Circle()
.frame(width: 300, height: 300)
Circle()
.frame(width: 150, height: 150)
.offset(x: 100, y: 70)
}
)
}
}
private var overlayView: some View {
GeometryReader { geo in
ZStack(alignment: .leading) {
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
.allowsHitTesting(false)
}
private var starsView: some View {
HStack {
ForEach(1..<6) { star in
Image(systemName: "star.fill")
.font(.title)
.foregroundColor(.gray)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.3)) {
rating = star
UserDefaults.standard.setValue(rating, forKey: "rating_key")
}
}
}
}
}
}
읽어주셔서 감사합니다🤟
본 게시글의 전체 코드 GitHub 👇🏻
'SWIFTUI > Others' 카테고리의 다른 글
SwiftUI : Background Threads / Queues (0) | 2021.06.07 |
---|---|
SwiftUI : @FetchRequest 속성 래퍼 [Core Data #1] (1) | 2021.06.05 |
SwiftUI : Gesture (0) | 2021.05.29 |
SwiftUI : Markups과 Documentation을 추가하는 방법 (0) | 2021.05.13 |
SwiftUI : ActionSheet (0) | 2021.05.12 |