Установить пользовательский фрейм 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.

Вероятно, стоило сделать из этого небольшую статью о разработке.

Другие вопросы по тегам