Установить пользовательский фрейм UIView в UIViewRepresentable SwiftUI
Я пытаюсь использовать свой собственный UIView в SwiftUI с помощью UIViewRepresentable, и я хочу, чтобы мой UIView имел тот же размер, что и в.frame(), чтобы я мог использовать его следующим образом:
MyViewRepresentable()
.frame(width: 400, height: 250, alignment: .center)
Например, я могу установить фрейм как свойство:
struct MyViewRepresentable: UIViewRepresentable {
var frame: CGRect
func makeUIView(context: Context) -> UIView {
let myView = MyView(frame: frame)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
Применение:
struct ContentView: View {
var body: some View {
MyViewRepresentable(frame: CGRect(x: 0, y: 0, width: 400, height: 250))
.frame(width: 400, height: 250, alignment: .center)
}
}
Это не решение, и мне интересно, как это исправить.
2 ответа
Если MyView
имеет правильную внутреннюю компоновку (которая зависит только от собственных границ), то во внешних дополнительных ограничениях нет необходимости, т.е.
struct MyViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> UIView {
return MyView(frame: .zero)
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
будет точно по размеру ниже, имея рамку 400x250
struct ContentView: View {
var body: some View {
MyViewRepresentable()
.frame(width: 400, height: 250, alignment: .center)
}
}
если это не так, то внутренний макет MyView имеет дефекты.
Если ответ Asperi у вас не удался, то, наверное, как они сказали: внутренняя раскладка MyView имеет дефекты.
Чтобы решить эту проблему, у вас есть несколько вариантов:
Вариант A. Используйте ограничения AutoLayout внутри
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// 1. View Hierarchy
self.addChild(self.mySubview)
self.view.addSubview(self.mySubview.view)
self.mySubview.didMove(toParent: self)
// 2. View AutoLayout Constraints
self.mySubview.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: self.mySubview.view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: self.mySubview.view.trailingAnchor),
view.topAnchor.constraint(equalTo: self.mySubview.view.topAnchor),
view.bottomAnchor.constraint(equalTo: self.mySubview.view.bottomAnchor)
])
}
Вариант Б. Установить рамку вручную в пределах
Просто в пределах вашего
UIViewController
, установить кадры подвидов в
viewDidLayoutSubviews
.
override func viewDidLoad() {
super.viewDidLoad()
// 1. Add your subviews once in `viewDidLoad`
self.addChild(self.mySubview)
self.view.addSubview(self.mySubview.view)
self.mySubview.didMove(toParent: self)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 2. Layout your subviews `viewDidLayoutSubviews`
// Update subview frame size
// Must be done in `viewDidLayoutSubviews`
// Otherwise in `viewDidLoad` the bounds equal `UIScreen.main.bounds`, even if you used explicit frame within SwiftUI and used GeometryReader to pass the `CGSize` yourself to this UIViewController!
let mySubviewFrame = self.view.bounds
self.mySubview.view.frame = mySubviewFrame
}
Дополнительные ресурсы
- В основном у вас есть несколько методов макета в iOS. Здесь они упорядочены от самых старых/худших до самых новых/лучших. Этот порядок, конечно, самоуверенный:
- Макет на основе фреймов . Ручное управление
frame
а такжеbounds
свойства в UIView. - Маски автоматического изменения размера . Под капотом используются архаичные пружины и стойки. Видеть
autoResizingMask
, этот ответ и эта статья . - Ограничения автомакета .
- Автомакет StackView.
- SwiftUI
VStack
а такжеHStack
! Это только для SwiftUI, и все вышеперечисленное относится только к UIKit.
- Макет на основе фреймов . Ручное управление
Вероятно, стоило сделать из этого небольшую статью о разработке.