#ios #swift #swiftui #uikit
Вопрос:
Я пытаюсь создать UIScrollView, который может масштабироваться за пределы исходных кадров вида, который он размещает. Вот мой код:
import SwiftUI
import UIKit
struct MapWrapperView<Content: View>: UIViewRepresentable {
private var content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.minimumZoomScale = 0
scrollView.maximumZoomScale = 100
scrollView.bouncesZoom = false
scrollView.bounces = false
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false
let hostedView = context.coordinator.hostingController.view!
hostedView.translatesAutoresizingMaskIntoConstraints = true
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostedView.frame = scrollView.bounds
scrollView.addSubview(hostedView)
return scrollView
}
func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: self.content))
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
context.coordinator.hostingController.rootView = self.content
assert(context.coordinator.hostingController.view.superview == uiView)
}
// MARK: - Coordinator
class Coordinator: NSObject, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>
init(hostingController: UIHostingController<Content>) {
self.hostingController = hostingController
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return hostingController.view
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
if(scale < scrollView.minimumZoomScale){
scrollView.minimumZoomScale = scale
}
if(scale > scrollView.maximumZoomScale){
scrollView.maximumZoomScale = scale
}
}
}
}
Я могу успешно уменьшить масштаб дальше, чем границы подвид, но когда это происходит, подвид уменьшается с верхнего левого края экрана и начинает вести себя странно. Есть ли какой-либо способ: а) уменьшить масштаб подвида от центра экрана, а затем расширить его до новых границ, или б) возможно, вначале установить масштаб подвида на меньшее значение, а затем установить разумный минимальный масштаб?
Ответ №1:
Хорошо, я нашел ответ. В принципе, согласно другим членам stackoverflow, переменная contentOffset и contentInset должны динамически изменяться при каждом сжатии, чтобы управлять тем, где содержимое привязывает масштаб. Вот измененный код, чтобы это сработало:
//
// MapWrapperView.swift
// Saturday
//
// Created by Bruce Jagid on 6/22/21.
//
import SwiftUI
import UIKit
struct MapWrapperView<Content: View>: UIViewRepresentable {
private var content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.minimumZoomScale = 0
scrollView.maximumZoomScale = 100
scrollView.bouncesZoom = false
scrollView.bounces = false
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false
let hostedView = context.coordinator.hostingController.view!
hostedView.translatesAutoresizingMaskIntoConstraints = true
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
scrollView.bounds = hostedView.frame
scrollView.addSubview(hostedView)
let leftMargin: CGFloat = (scrollView.frame.size.width - hostedView.bounds.width)*0.5
let topMargin: CGFloat = (scrollView.frame.size.height - hostedView.bounds.height)*0.5
scrollView.contentOffset = CGPoint(x: max(0,-leftMargin), y: max(0,-topMargin));
scrollView.contentSize = CGSize(width: max(hostedView.bounds.width, hostedView.bounds.width 1), height: max(hostedView.bounds.height, hostedView.bounds.height 1))
return scrollView
}
func makeCoordinator() -> Coordinator {
return Coordinator(hostingController: UIHostingController(rootView: self.content))
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
context.coordinator.hostingController.rootView = self.content
assert(context.coordinator.hostingController.view.superview == uiView)
}
// MARK: - Coordinator
class Coordinator: NSObject, UIScrollViewDelegate {
var hostingController: UIHostingController<Content>
init(hostingController: UIHostingController<Content>) {
self.hostingController = hostingController
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return hostingController.view
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
if(scale < scrollView.minimumZoomScale){
scrollView.minimumZoomScale = scale
}
if(scale > scrollView.maximumZoomScale){
scrollView.maximumZoomScale = scale
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
if(scrollView.zoomScale < 1){
let leftMargin: CGFloat = (scrollView.frame.size.width - hostingController.view!.frame.width)*0.5
let topMargin: CGFloat = (scrollView.frame.size.height - hostingController.view!.frame.height)*0.5
scrollView.contentInset = UIEdgeInsets(top: max(0, topMargin), left: max(0,leftMargin), bottom: 0, right: 0)
}
}
}
}
Всякий раз, когда вид прокрутки приближается к границам представления содержимого, масштабирование будет центрироваться в соответствии с логикой функции делегирования scrollViewDidZoom.
Комментарии:
1. Я предполагаю, что мой следующий вопрос заключается в том, как сделать так, чтобы этот вид все еще позволял мне увеличивать определенные области за пределами подвида?
2. Неважно, я тоже это понял, просто сделал начальный масштаб подвида небольшим, поэтому он начинает уменьшаться. Вид прокрутки автоматически отображает это в масштабе 1, поэтому увеличение и уменьшение масштаба ничего не меняет