일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- SWIFT
- Alamofire
- test
- tuist #xcodecloud #ios #ci/cd #swiftlint #firebase
- IOS
- 모바일
- iOS 개발자
- concurrency
- xcode
- swiftdata
- UI
- uikit
- ios18
- regex
- 정규표현식
- navigationsplitview
- SWIFTUI
- Git
- composablearchitecture
- Tuist
- xcodecloud
- 개발
- combine
- TCA
- iOS 13.0+
- ObjC
- Firebase
- github
- Navigation
- network
- Today
- Total
iOS 개발 기록
SwiftUI - View의 크기를 구하는 방법 본문
앱을 만들면서 Text가 개행되는 것에 따라 라인을 그려줘야하는 View를 구현해야 했다.
Text 한 줄의 view의 높이를 구해서 전체 View를 나누면 라인의 수가 나올거라 생각해 그 방법으로 구현하려고 했다.
그런데 SwiftUI에서는 View의 크기를 구하는게 UIKit에 비해 까다로웠다.
여기서는 SwiftUI에서 View의 크기를 구하는 두가지 방법을 소개한다.
사실 두개 다 아이디어는 똑같기는 하다.
구하고자 하는 뷰와 똑같은 크기의 하위뷰를 만들어의 하위뷰의 크기를 전달하는 것이다.
1. PreferenceKey
Preference 는 Key-Value 로직으로 하위 뷰의 정보를 상위 뷰에 전달할 수 있다.
이를 위해 PreferenceKey 프로토콜을 따르는 Key를 정의해주어야 한다.
PreferenceKey를 통해 단순히 뷰의 크기 뿐만 아니라 ScrollView에서의 스크롤 위치 등 뷰와 관련된 정보들을 얻을 수 있다.
struct ViewSizeKey: PreferenceKey {
static var defaultValue = CGSize()
//reduce 메소드는 ViewSizeKey를 사용하는 뷰들의 값을 취합하는 역할을 한다.
static func reduce(value: inout CGSize, nextValue: () -> Value) {
value = nextValue()
}
}
사용
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
ForEach(0..<100) { num in
HStack {
Text("\(num)")
}
}///ForEach
.background(
GeometryReader { geo in
// 동일한 크기의 투병한 배경을 깔고 Geometry로 구한 View의 크기를 ViewSizeKey로 전달한다.
Color.clear.preference(key: ViewSizeKey.self, value: geo.frame(in: .local).size)
}
.onPreferenceChange(ViewSizeKey.self) {
print(geo)
})
}///body
}
구하고자 하는 상위 뷰(ScrollView)의 배경으로 투명한 배경(하위뷰)을 깔고, 배경의 Preference를 Key에 전달한다.
SwiftUI에서는 Color 또한 View 프로토콜을 따른다.
2. Combine
역시나 투명한 하위 뷰를 만들어서 Geometry로 크기를 구한다는 아이디어는 동일하다.
여기서는 View의 크기를 저장하기 위한 @State 변수 하나를 만들어 초기값이 변할때 Combine으로 감지해 값을 출력한다.
import SwiftUI
import Combine
struct ContentView: View {
///사용하는 메인뷰의 높이를 구하기위해 사용하는 변수
@State var writeViewSize : CGSize = .zero
var body: some View {
ScrollView(.vertical) {
ForEach(0..<100) { num in
HStack {
Text("\(num)")
}
}///ForEach
.background(
GeometryReader { geo in
Color.clear
.onAppear() { // 초기에 뷰가 생성될 때의 값
self.writeViewSize = proxy.size
print(self.writeViewSize)
}
.valueChanged(value: proxy.size) { value in // 뷰의 크기가 변한다면 이를 받을 값
self.writeViewSize = proxy.size
print(self.writeViewSize)
}
})
}///scrollView
}/// body
}
/// View Extension
extension View {
@ViewBuilder func valueChanged<T: Equatable>(value: T, onChange: @escaping (T) -> Void) -> some View {
if #available(iOS 14.0, *) {
self.onChange(of: value, perform: onChange)
} else {
self.onReceive(Just(value)) { (value) in
onChange(value)
}
}
}
}
결론
결국에는 같은 아이디어를 PreferenceKey로 구현하냐, Combine으로 구현하냐의 차이인 것 같다.
개인적으로는 첫번째 방법이 더 간편한 것 같다.
참고
https://stackoverflow.com/questions/66485411/dynamic-texteditor-overlapping-with-other-views
Dynamic TextEditor overlapping with other views
So I have made a successful dynamic TextEditor but when I try to combine views, it overlaps the other elements and no longer expands as you type. I am pretty sure this has to do with Geometry Reade...
stackoverflow.com
'SwiftUI' 카테고리의 다른 글
[iOS] WidgetKit (0) | 2023.02.06 |
---|---|
[iOS] swiftUI에서 UIKit 사용하기 (0) | 2023.02.06 |
SwiftUI - Realm (0) | 2022.04.26 |
SwiftUI - 애니메이션 (0) | 2022.04.21 |
SwiftUI의 4가지 원칙 (0) | 2022.04.13 |