Slider label position
โฑ๐ง under construction
Last updated
Was this helpful?
โฑ๐ง under construction
Last updated
Was this helpful?
โฉ โฉ โฉ label position
่จญๅฎ ๆธๅผๆจ็ฑคๅฐๆปๆกฟ่ชฟ้็ไธๆนใ
ๆนๆณ ๏ผ๏ผไฝฟ็จๅบๅฎๅฏฌๅบฆ็ ใ
ๅช้ป๏ผๅฎนๆ่จ็ฎๆธๅผๆจ็ฑค็ๅ็งป้ใ
็ผบ้ป๏ผๅฏฌๅบฆๅบๅฎ๏ผ้ฉๆๆงๅทฎใ
่ฒ ่ฒฌๅฐๅญ่ฆๅ () ็ฝฎไธญๅฐ้ฝ (Text
็ไธญๅฟ้ปๆพๅจ่ๆฏๆญฃไธญๅคฎ)ใ
่จ็ฎ Text
็ๅ็งป้ๆ๏ผ้ ไปฅไธญๅคฎ็บๅบๆบใ
็จ ๅ็งป Text
ใ
// view properties
@State var t: CGFloat = 0.5
let sliderWidth: CGFloat = 300 // โญ๏ธ ๆปๆกฟๅฏฌๅบฆ
// view body
// slider(fixed width) > background > text
Slider(value: $t)
// โญ๏ธ 1. `.background` ่ฒ ่ฒฌๅฐๅญ่ฆๅใ็ฝฎไธญๅฐ้ฝใ
.background {
let knobWidth: CGFloat = 28 // ๆปๆกฟ่ชฟ้ๅฏฌๅบฆ
let knobRange = sliderWidth - knobWidth // ่ชฟ้ๆดปๅ็ฏๅๅฏฌๅบฆ
// โญ๏ธ 2. ๅ ็บ Text ๆ่ขซ .background ็ฝฎไธญๅฐ้ฝ
// ๆไปฅ่จ็ฎๅ็งป้ๆ๏ผ้ ไปฅไธญๅคฎ็บๅบๆบใ
let xOffset = (t-0.5) * knobRange // ๆธๅผๆจ็ฑคๅ็งป้
// โญ๏ธ 3. ็จ .offset() ๅ็งป Textใ
Text("\(t, specifier: "%.2f")")
.offset(x: xOffset, y: -30)
}// background
.frame(width: sliderWidth) // โญ๏ธ ๅฏฌๅบฆๅบๅฎ
ๅช้ป๏ผๅฏ้ฉๆๆฏ่ฆๅ็็ฐๅขใๅคงๅฐๅฏ่ชๅ่ชฟๆดใ
็ผบ้ป๏ผๅฎนๆๅฟฝ็ฅ GeometryReader
ไปฅๅทฆไธ่ง็บๅฐ้ฝๆนๅผ๏ผๅฐ่ดๅฎไฝ้ฏ่ชคใ
้็ถ .background
่ฒ ่ฒฌๅฐๅญ่ฆๅ็ฝฎไธญๅฐ้ฝ๏ผไฝ GeometryReader
ๆฏๅฎๅฏไธ็ๅญ่ฆๅ๏ผ่ไธ GeometryReader
ๆ็จๆๆฏ่ฆๅ็ๆๆ็ฉบ้๏ผๅ ๆญค .background
็ๅฐ้ฝ็ญๆผๆฒๆๆ็จโ๏ธ๏ผๅฐ้ฝไธป่ฆๆฏ็ฑ GeometryReader
ไพๆฅๆๆๆง๏ผ
ๆณจๆ๏ผGeometryReader
็ๅฐ้ฝๆนๅผ่ .background
ไธๅ๏ผๅฎ่ฒ ่ฒฌๅฐๅญ่ฆๅๅฐ้ฝใๅทฆไธ่งใ๏ผๅ ๆญคๅจ GeometryReader
ๅ
ง้จๆพ็ฝฎๅญ่ฆๅ็้่ผฏ่ๅจ .background
ๅ
งไธๅโ๏ธ
่จ็ฎๆจ็ฑคๅ็งป้ๆ๏ผ่ฆไปฅ GeometryReader
ๅทฆไธ่ง่้๏ผๅๆ้่ฆ่้ๅฐ่ชฟ้ๆฌ่บซๆไธๅฎๅฏฌๅบฆ (28)๏ผๅ ๆญค่ชฟ้็ไธญๅฟ้ป็กๆณๆฅ่งธๅฐๆปๆกฟๆๅทฆๅด๏ผ่ๆฏ่ชฟ้็ๅทฆๅดๆฅ่งธๅฐๆปๆกฟ็ๆๅทฆๅด๏ผๆไปฅ่จ็ฎๆจ็ฑคๅ็งป้ๆ๏ผ้่ฆๅ
ๅ ไธ่ชฟ้ๅฏฌๅบฆ็ไธๅใ
็จ .position()
็ดๆฅ่จญๅฎ Text ไธญๅฟ้ปไฝ็ฝฎใ
ๅช้ป๏ผไธๆๅ ็บๅญ้ซๅฏฌๅบฆ่ฎๅ่ๅฐ่ดๆจ็ฑคไฝ็ฝฎ่่ชฟ้็กๆณๅฐ้ฝใ
// view's properties
@State var s: CGFloat = 100 // ๆปๆกฟๆธๅผ
let range: ClosedRange<CGFloat> = 100...300 // ๆธๅผ็ฏๅ
let min = range.lowerBound // ๆๅฐๅผ
let max = range.upperBound // ๆๅคงๅผ
// slider > background > GeometryReader > Text
Slider(value: $s, in: range)
// โญ๏ธ 1. ็จ .background ๆญ้
GeometryReader ไพๅๅพ Slider ็ๅฐบๅฏธ
// - ้็ถ background ่ฒ ่ฒฌๅฐๅญ่ฆๅใ็ฝฎไธญๅฐ้ฝใ๏ผไฝ GeometryReader
// ๆฏๅฎๅฏไธ็ๅญ่ฆๅ๏ผ่ไธ GeometryReader ๆ็จๆ่ๆฏ็ๆๆ็ฉบ้๏ผ
// ๅ ๆญค background ็ๅฐ้ฝ็ญๆผๆฒๆๆ็จโ๏ธ
.background {
// โญ๏ธ 2. ๆณจๆ๏ผGeometryReader ็ๅฐ้ฝๆนๅผ่ background ไธๅ๏ผ
// ๅฎ่ฒ ่ฒฌๅฐๅญ่ฆๅๅฐ้ฝใๅทฆไธ่งใ๏ผๅ ๆญคๅจ GeometryReader ๅ
ง้จ
// ๆพ็ฝฎๅญ่ฆๅ็้่ผฏ่ๅจ background ๅ
งไธๅโ๏ธ
GeometryReader { geo in
let s2 = (s - min)/(max - min) // s2: 0...1
let knobWidth: CGFloat = 28 // ๆปๆกฟ่ชฟ้ๅฏฌๅบฆ
let sliderWidth = geo.size.width // ๆปๆกฟๅฏฌๅบฆ
let knobRange = sliderWidth - knobWidth // ่ชฟ้็ๆดปๅๅฏฌๅบฆ
// โญ๏ธ 3. ่จ็ฎๆจ็ฑคๅ็งป้ๆ๏ผ่ฆไปฅ GeometryReaderใๅทฆไธ่งใ่้๏ผ
// ๅๆ้่ฆ่้ๅฐ่ชฟ้ๅฏฌๅบฆใๅ ็บ่ชฟ้ๆฌ่บซๆไธๅฎๅฏฌๅบฆ (28)๏ผ
// ๅ ๆญค่ชฟ้็ไธญๅฟ้ป็กๆณๆฅ่งธๅฐๆปๆกฟ็ๆๅทฆๅด๏ผ่ๆฏ่ชฟ้็ใๅทฆๅดใ
// ๆฅ่งธๅฐๆปๆกฟ็ๆๅทฆๅด๏ผๆไปฅ่จ็ฎๆจ็ฑคๅ็งป้ๆ๏ผ้่ฆๅ
ๅ ไธ่ชฟ้ๅฏฌๅบฆ็ไธๅใ
let xOffset = (s2) * knobRange + knobWidth/2
// โญ๏ธ 4. ็จ .position() ็ดๆฅ่จญๅฎ Text ไธญๅฟ้ปไฝ็ฝฎ
// ๅช้ป๏ผไธๆๅ ็บๅญ้ซๅฏฌๅบฆ่ฎๅ่ๅฐ่ดๆจ็ฑคไฝ็ฝฎ่่ชฟ้็กๆณๅฐ้ฝใ
Text("\(s, specifier: "%.0f")")
.position(x: xOffset, y: -16)
// โ๏ธ ็จ .offset() ็็ผบ้ป๏ผ
// ่จ็ฎ Text ๅ็งป้ๆ้่ฆ่ๆ
ฎๅฐๅญ้ซๅฏฌๅบฆ่ฎๅ๏ผๅพๅข่จ็ฎๅฐ้ฃใ
// .offset(x: xOffset, y: -30)
}// GeometryReader
}// background
}// slider2-1
ๅช้ป๏ผ
ๅฏ้ฉๆๆฏ่ฆๅใๅคงๅฐๅฏ่ชๅ่ชฟๆดใ
ZStack
ๅฏๅนซๅฟ็ฝฎไธญๅญ่ฆๅใ
็ผบ้ป๏ผ
ๅฎนๆๅฟ่จๅฐ ZStack
็ๅฐบๅฏธๆพๅฐ่ GeometryReader
ไธๆจฃๅคง๏ผๅฐ่ด GeometryReader
ไพ็ถๅ
ๅฐๆดๅ ZStack
ๆพๅฐๅทฆไธ่ง๏ผ็ถๅพ ZStack
ๆๅจๅฎ็ๅ
ง้จๅ็ฝฎไธญๅฐ้ฝ๏ผ้ๆ็็ฝฎไธญๅฐ้ฝๅฐฑๆฏ็กๆ็๏ผๅ ็บๆดๅ ZStack
้ฝๅจๅทฆไธ่งโ๏ธ ๏ผ็ไธๅ slider 2-2๏ผ
ๅฟ่จๅฐ ZStack
็ๅฐบๅฏธๆพๅฐ่ GeometryReader
ไธๆจฃๅคงโ๏ธ
GeometryReader
ไพ็ถๅ
ๅฐๆดๅ ZStack
ๆพๅฐๅทฆไธ่ง๏ผ็ถๅพ ZStack
ๆๅจๅฎ็ๅ
ง้จๅ็ฝฎไธญๅฐ้ฝโ๏ธ
่จ็ฎๅ็งป้ๆ๏ผไปไปฅใไธญๅฟ้ปใ็บ่้๏ผๅฐ่ดๆจ็ฑคไฝ็ฝฎ้ฏ่ชคโ๏ธ
// view's properties
@State var s: CGFloat = 100 // ๆปๆกฟๆธๅผ
// view body
// 2-2. slider > overlay > GeometryReader > ZStack > Text
Slider(value: $s, in: 100...300)
.background {
// โญ๏ธ 2. GeometryReader ไพ็ถๅ
ๅฐๆดๅ ZStack ๆพๅฐๅทฆไธ่ง๏ผ
// ็ถๅพ ZStack ๆๅจๅฎ็ๅ
ง้จๅ็ฝฎไธญๅฐ้ฝโ๏ธ
GeometryReader { geo in
let s2 = (s - 100)/(300 - 100) // s2: 0...1
let width = geo.size.width - 28 // ่ชฟ้็ๆดปๅๅฏฌๅบฆ
// โญ๏ธ 3. ่จ็ฎๅ็งป้ๆ๏ผไปไปฅใไธญๅฟ้ปใ็บ่้๏ผๅฐ่ดๆจ็ฑคไฝ็ฝฎ้ฏ่ชคโ๏ธ
let xOffset: CGFloat = (s2 - 0.5) * width
// โญ๏ธ 1. ๅฟ่จๅฐ ZStack ๅฐบๅฏธๆพๅฐ่ GeometryReader ไธๆจฃๅคง
ZStack {
Text("\(s, specifier: "%.0f")")
.offset(x: xOffset, y: -30)
}// ZStack
}// GeometryReader
}// background
ๆนๆณ๏ผ-๏ผ๏ผๅๆนๆณ๏ผ-๏ผ๏ผไฝไฟฎๆญฃไบๅฟ่จๅฐ ZStack
็ๅฐบๅฏธๆพๅฐ่ GeometryReader
ไธๆจฃๅคง็ๅ้ก๏ผๅฆๆญค ZStack
ๆ่ฝ็ๆญฃๆฅๆๅนซๅฟ็ฝฎไธญๅฐ้ฝๅญ่ฆๅใ
ๅ
ๆฃ้ค GeometryReader
ๅทฆๅณๅไธๅๆปๆกฟ่ชฟ้็ๅฏฌๅบฆใ
ๅฉ็จ .frame(maxWidth: .infinity, maxHeight: .infinity)
ๅฐ ZStack
็ๅฐบๅฏธๆพๅฐๆๅคง (GeometryReader
ๆฃๆ่ชฟ้ๅฏฌๅบฆ)
ๆญคๆ ZStack
ๅฐฑ่ฝๆญฃๅธธๅนซๅฟ็ฝฎไธญๅฐ้ฝ Text
ใ
็จ .position()
็ดๆฅ่จญๅฎๆจ็ฑคไธญๅฟ้ปไฝ็ฝฎ (ๆญคๆๆจ็ฑคๆ็ๅจ่ชฟ้ๆญฃไธๆน)ใ
็จ .offset()
ๅฐๆจ็ฑคๅพไธ่ชฟใ
// view's properties
let bounds: ClosedRange<CGFloat> = 100...300
let min = bounds.lowerBound
let max = bounds.upperBound
// view body
// 2-3. slider > overlay > GeometryReader > ZStack(maximized) > Text
Slider(value: $s, in: bounds)
.overlay {
// โญ๏ธ 1. overlay + GeometryReader ๅๅพ Slider ๅฐบๅฏธ (geo.size)
GeometryReader { geo in
let s2 = (s - min)/(max - min) // s2: 0...1
let knobWidth: CGFloat = 28
let yOffset: CGFloat = -16
// โญ๏ธ 5. ่จ็ฎ ZStack ็ๅคงๅฐ
let zstackSize = geo.size - CGSize(knobWidth, 0) // GeometryKit
// โญ๏ธ 4. ZStack ๅนซๅฟ็ฝฎไธญๅฐ้ฝ Text
ZStack(alignment: .center) {
Text("\(s, specifier: "%.0f")")
// โญ๏ธ 6. ็จ .position() ็ดๆฅ่จญๅฎๆจ็ฑคไธญๅฟ้ปไฝ็ฝฎ๏ผ
// ๆญคๆๆจ็ฑคๆ็ๅจ่ชฟ้ๆญฃไธๆนใ
.position(zstackSize[s2, 0]) // GeometryKit
// โญ๏ธ 7. ๅฐๆจ็ฑคๅพไธ่ชฟใ
.offset(y: yOffset)
}// ZStack
// โญ๏ธ 3. ๅฐ ZStack ็ๅฐบๅฏธๆพๅฐๆๅคง
.frame(maxWidth: .infinity, maxHeight: .infinity)
// โญ๏ธ 2. ๅ
ๆฃ้ค GeometryReader ๅทฆๅณๅไธๅๆปๆกฟ่ชฟ้็ๅฏฌๅบฆ
.padding(.horizontal, knobWidth/2) // knob half width
}//GeometryReader
.allowsHitTesting(false) // ้ฒๆญข overlay ๆไฝ slider
}// overlay
ๆนๆณ๏ผ-๏ผ๏ผๅๆนๆณ๏ผ-๏ผใ็ผๅฐ็ไบบๆ่ฉฒๆๅฏ่ฆบๅฐ๏ผ้็ถๆนๆณ๏ผ-๏ผไธญๆๅๆ็จ ZStack
ไพๅนซๅฟ็ฝฎไธญๅญ่ฆๅ๏ผๅฎๅฏไธ็ๅญ่ฆๅ Text
ๅป้ธๆไบ .position()
ไพ็ดๆฅ่จญๅฎ่ชๅทฑ็ไธญๅฟ้ปไฝ็ฝฎใๆๅฅ่ฉฑ่ชช๏ผZStack
ๆ นๆฌๆฒ่ตทไปปไฝไฝ็จ๏ผๅ ๆญคๅฏไปฅ็ดๆฅ็งป้ค ZStack
โ๏ธ
๐ง
import SwiftUI
import GeometryKit // CGSize as Vector2D
struct SliderValuePosition: View {
@State var t: CGFloat = 0.5
@State var s: CGFloat = 100
// slider range
let range: ClosedRange<CGFloat> = 100...300
var min: CGFloat { range.lowerBound }
var max: CGFloat { range.upperBound }
var s2: CGFloat { (s - min)/(max - min) } // s2: 0...1
let knobWidth: CGFloat = 28 // slider knob width
let yOffset: CGFloat = -16 // label y-offset
var body: some View {
ScrollView {
VStack(spacing: 100) {
Spacer()
slider1
HStack {
slider2_1
slider2_2
}
slider2_3
slider2_4
Spacer()
}// VSTack
.padding()
.frame(maxWidth: 900)
}// ScrollView
}// body
// 1. slider(fixed width) > background > text
var slider1: some View {
let sliderWidth: CGFloat = 300 // โญ๏ธ fixed slider width
let knobRange = sliderWidth - knobWidth
let xOffset = (t-0.5) * knobRange // -0.5...0.5 * knobRange
return Slider(value: $t).background {
Text("\(t, specifier: "%.2f")")
.offset(x: xOffset, y: -30)
}// slider.background
.bgColor(.green)
.frame(width: sliderWidth)
.borderAndTitle("slider 1 (โญ๏ธ fixed width โญ๏ธ)")
}// slider1
// 2-1. slider > background > GeometryReader > Text
var slider2_1: some View {
Slider(value: $s, in: range)
.background {
GeometryReader { geo in
let sliderWidth = geo.size.width // slider width
let knobRange = sliderWidth - knobWidth // knob ็ๆดปๅๅฏฌๅบฆ
let xOffset = s2 * knobRange + knobWidth/2 // label x-offset
// โญ๏ธ ็จ .position() ็ๅช้ป๏ผ
// ็ดๆฅ่จญๅฎไธญๅฟ้ปไฝ็ฝฎ๏ผไธๆๅ ็บๅญ้ซๅฏฌๅบฆ่ฎๅ่ๅฐ่ดไฝ็ฝฎๅ็งป
Text("\(s, specifier: "%.0f")")
.position(x: xOffset, y: -16)
// โ๏ธ ็จ .offset() ็็ผบ้ป๏ผ
// ๅจ GeometryReader ๅ
ง้จ๏ผๅญ่ฆๅๆๅฐ้ฝๅทฆไธ่ง๏ผ
// ๅ ๆญค่จ็ฎ Text ็ๅ็งป้ๆ้่ฆ่ๆ
ฎๅฐๅญ้ซๅฏฌๅบฆ่ฎๅ๏ผ
// ๅฐ่ด่จ็ฎไธ็ๅฐ้ฃใ
// .offset(x: xOffset, y: -30)
}// GeometryReader
.bgColor(.yellow).allowsHitTesting(false)
}// background
.borderAndTitle("slider 2-1")
}// slider2-1
// 2-2. slider > background > GeometryReader > ZStack > Text
var slider2_2: some View {
Slider(value: $s, in: range)
.background {
GeometryReader { geo in
let sliderWidth = geo.size.width
let knobRangeWidth = sliderWidth - knobWidth
let xOffset = (s2 - 0.5) * knobRangeWidth
ZStack {
Text("\(s, specifier: "%.0f")")
.offset(x: xOffset, y: -30) // move upward
}// ZStack
.bgColor(.red, opacity: 0.9)
}// GeometryReader
.bgColor(.yellow)
.allowsHitTesting(false)
}// slider.background
.borderAndTitle("slider 2-2")
}// slider2_2
// 2-3. slider > overlay > GeometryReader > ZStack(maximized) > Text
var slider2_3: some View {
Slider(value: $s, in: range)
.overlay {
GeometryReader { geo in
let zstackSize = geo.size - CGSize(knobWidth, 0) // GeometryKit
ZStack {
Text("\(s, specifier: "%.0f")")
// โญ๏ธ position label directly
.position(zstackSize[s2, 0]) // GeometryKit
.offset(y: yOffset) // move upward
}// ZStack
// โญ๏ธ maximize ZStack
.frame(maxWidth: .infinity, maxHeight: .infinity)
.bgColor(.red)
.padding(.horizontal, knobWidth/2) // knob half width
}//GeometryReader
.allowsHitTesting(false)
}// slider.overlay
.borderAndTitle("slider 2-3")
}// slider2_3
// 2-4. slider > background > GeometryReader > Text
var slider2_4: some View {
Slider(value: $s, in: range)
.background {
GeometryReader { geo in
Text("\(s, specifier: "%.0f")")
// โญ๏ธ position label directly
.position(geo.size[s2, 0]) // GeometryKit
.offset(y: yOffset) // move upward
}//GeometryReader
.padding(.horizontal, knobWidth/2) // โญ๏ธ knob half width
.bgColor(.yellow)
.allowsHitTesting(false)
}// background
.borderAndTitle("slider 2-4")
}// slider2_4
}// SliderValuePosition
// view extension
private extension View {
/// `view.borderAndTitle("name")`
func borderAndTitle(_ title: String) -> some View {
self
.border(.secondary)
.padding()
.border(.secondary)
.overlay {
Text(title)
.offset(y: 50)
.foregroundStyle(Color.secondary)
.multilineTextAlignment(.center)
}
}
/// `view.bgColor(.red)`
func bgColor(_ color: Color, opacity: Double = 0.5) -> some View {
self.background(color.opacity(opacity))
}
}
GeometryReader๏ผ็จๆผๅๅพ Slider ็ๅฏฌๅบฆใ
GeometryKit๏ผcustom package
ๆนๆณ๏ผ-๏ผ๏ผ็จ ๏ผ ๏ผ ๅฐบๅฏธ
ๆนๆณ๏ผ-๏ผ๏ผ็จ ๏ผ ๏ผ ๅนซๅฟ็ฝฎไธญ
ไฝฟ็จ .overlay
+ ๅๅพ ๅฐบๅฏธ (geo.size
)
่จป๏ผ็ถไธๅ view ็จๅฐ ๆ๏ผๆฏๅญ่ฆๅ้ไฟ่ฆๅ้ไพ็๏ผไพๅฆ๏ผchild.parent().grandParent()
๏ผๅ้ข็ๆฏๅญ่ฆๅใๅพ้ข็ๆฏๆฏ่ฆๅโ๏ธ
ๅฉ็จ GeometryReader
็่ณ่จ (geo.size
) ่จ็ฎ ZStack
็ๅฐบๅฏธใ
(่จป๏ผ้่ฃกๆ็จๅฐ GeometryKit ้ๅ )
โฉ โฉ
โฑ