💫 Custom alignment guides
Apple 공식문서
Apple Developer Documentation
developer.apple.com
private struct FirstThirdAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.height / 3
}
}
extension VerticalAlignment {
static let firstThird = VerticalAlignment(FirstThirdAlignment.self)
}
해당 뷰 높이의 3분의 1지점을 정렬하는 FirstThirdAlignment를 새로 만들고 VerticalAlignment에서 스태틱 변수로 추가해준다.
struct LayeredHorizontalStripes: View {
var body: some View {
ZStack(alignment: Alignment(horizontal: .center, vertical: .firstThird)) {
horizontalStripes(color: .blue)
.frame(width: 180, height: 90)
horizontalStripes(color: .green)
.frame(width: 70, height: 60)
}
}
private func horizontalStripes(color: Color) -> some View {
VStack(spacing: 1) {
ForEach(0..<3) { _ in color }
}
}
}
추가한 firstThird를 ZStack의 VerticalAlignment로 지정하면 이미지처럼 각각의 뷰의 3분의 1지점의 높이가 정렬이된다.
+) custom center alignment
https://useyourloaf.com/blog/swiftui-stack-custom-center-alignment/
💫 활용
CustomRadioButton
https://swiftui-lab.com/alignment-guides/
Alignment Guides in SwiftUI - The SwiftUI Lab
Alignment guides are a powerful tool that helps layout our SwiftUI views. They often save us from having to use more complex options (e.g, view preferences)
swiftui-lab.com
extension VerticalAlignment {
private struct CustomRadioButtonVerticalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[VerticalAlignment.center]
}
}
static let customRadioButtonVerticalAlignment = VerticalAlignment(CustomRadioButtonVerticalAlignment.self)
}
struct CustomRadioButton: View {
@State private var index = 0
private let weekdays = ["월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"]
var body: some View {
HStack(alignment: .customRadioButtonVerticalAlignment) {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.blue)
VStack(spacing: 8) {
ForEach(weekdays.indices, id: \.self) { index in
if self.index == index {
Text("\(weekdays[index])")
.alignmentGuide(.customRadioButtonVerticalAlignment) { dimensions in
dimensions[VerticalAlignment.center]
}
} else {
Text("\(weekdays[index])")
.onTapGesture {
withAnimation {
self.index = index
}
}
}
}
}
}
}
}
CustomGridTable
extension HorizontalAlignment {
private struct CustomGridTableHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[.leading]
}
}
static let customGridTableHorizontalAlignment = HorizontalAlignment(CustomGridTableHorizontalAlignment.self)
}
struct CustomGridTable: View {
var body: some View {
VStack(alignment: .customGridTableHorizontalAlignment) {
rowTable(title: "Name", content: "이기회")
rowTable(title: "Address", content: "Korea")
rowTable(title: "favorite", content: "🍎")
}
}
private func rowTable(title: String, content: String) -> some View {
HStack {
Text(title)
.fontWeight(.bold)
Text(content)
.alignmentGuide(.customGridTableHorizontalAlignment) { dimensions in
dimensions[.leading]
}
}
}
}
CustomGraphView
extension HorizontalAlignment {
private struct CustomGraphViewHorizontalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[HorizontalAlignment.center]
}
}
static let customGraphViewHorizontalAlignment = HorizontalAlignment(CustomGraphViewHorizontalAlignment.self)
}
extension VerticalAlignment {
private struct CustomGraphViewVerticalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[VerticalAlignment.center]
}
}
static let customGraphViewVerticalAlignment = VerticalAlignment(CustomGraphViewVerticalAlignment.self)
}
extension Alignment {
static let customGraphViewAlignment = Alignment(horizontal: .customGraphViewHorizontalAlignment, vertical: .customGraphViewVerticalAlignment)
}
CustomGraphViewHorizontalAlignment과 CustomGraphViewVerticalAlignment를 만들고 이를 조합한 customGraphViewAlignment 스태틱 변수를 사용한다.
struct CustomGraphView: View {
@State private var graphMaxHeight = CGFloat.zero
@State private var graphZeroHeight = CGFloat.zero
@State private var graphWidth = CGFloat.zero
private var barMaxHeight: CGFloat { graphZeroHeight - graphMaxHeight }
private let yAxisCount = 9
private let maxValue: Int = 100
private let weekdays = ["월", "화", "수", "목", "금", "토", "일"]
private let weekdata = [70, 100, 45, 79, 62, 20, 20]
var body: some View {
GeometryReader { geometry in
let size = geometry.size
ZStack(alignment: .customGraphViewAlignment) {
yAxisView(size: size)
xAxisView(size: size)
}
}
.frame(width: 300, height: 400)
}
private func yAxisView(size: CGSize) -> some View {
VStack {
HStack {
yAxisLabel("\(maxValue)", size: size)
yAxisBar()
.overlay(
GeometryReader { geometry in
let frame = geometry.frame(in: .global)
Color.clear
.onAppear {
graphWidth = frame.size.width
graphMaxHeight = frame.origin.y
}
}
)
}
Spacer()
ForEach(0..<yAxisCount, id: \.self) { i in
HStack {
let totalYAxisCount = yAxisCount + 1
let currentIndex = i + 1
let valueGap = maxValue - 0
yAxisLabel("\(maxValue - currentIndex * valueGap / totalYAxisCount)", size: size)
yAxisBar()
}
Spacer()
}
HStack {
yAxisLabel("0", size: size)
yAxisBar()
.alignmentGuide(.customGraphViewHorizontalAlignment) { $0[.trailing] }
.alignmentGuide(.customGraphViewVerticalAlignment) { $0[.top] }
.overlay(
GeometryReader { geometry in
let frame = geometry.frame(in: .global)
Color.clear
.onAppear {
graphZeroHeight = frame.origin.y
}
}
)
}
}
.frame(height: size.height)
}
private func yAxisLabel(_ label: String, size: CGSize) -> some View {
Text(label)
.frame(width: size.width / 10, alignment: .trailing)
}
private func yAxisBar() -> some View {
Rectangle()
.frame(height: 1)
.foregroundColor(.gray)
}
private func xAxisView(size: CGSize) -> some View {
HStack {
ForEach(weekdays.indices, id: \.self) { index in
let height = CGFloat(weekdata[index]) * barMaxHeight / 100
VStack {
Rectangle()
.frame(width: 15, height: height - 1)
.foregroundColor(.blue)
.frame(maxHeight: .infinity, alignment: .bottom)
.alignmentGuide(.customGraphViewVerticalAlignment) { $0[.bottom] }
Text("\(weekdays[index])")
}
if index != weekdays.count - 1 {
Spacer()
}
}
}
.padding(.horizontal)
.frame(width: graphWidth)
.alignmentGuide(.customGraphViewHorizontalAlignment) { $0[.trailing] }
}
}
'iOS > SwiftUI' 카테고리의 다른 글
SwiftUI에서 GoogleMapsAPI 적용해보기 - 심화 (0) | 2023.02.22 |
---|---|
SwiftUI에서 GoogleMapsAPI 적용해보기 - 기초 (0) | 2023.02.19 |
SwiftUI로 CheckBoxTreeView 만들기! (0) | 2022.07.01 |
SwiftUI로 Countdown Timer 만들어보기 (0) | 2022.06.17 |
SwiftUI 그라데이션 텍스트 구현하기 (0) | 2022.04.27 |