#swiftui
#swiftui
Вопрос:
Как создать страницу, которая работает точно так же, как приложения для фотографий на iOS, которые могут увеличивать фотографию с помощью MagnificationGesture () и могут перемещаться после увеличения с помощью чистого SwiftUI?
Я пытался искать решения на форуме, но ни у одного вопроса пока нет решения. Есть какие-либо рекомендации?
Вот мой код:
let magnificationGesture = MagnificationGesture()
.onChanged { amount in
self.currentAmount = amount - 1
}
.onEnded { amount in
self.finalAmount = self.currentAmount
self.currentAmount = 0
}
let tapGesture = TapGesture()
.onEnded {
self.currentAmount = 0
self.finalAmount = 1
}
Image("Cat")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:UIScreen.main.bounds.width,height: UIScreen.main.bounds.height)
.scaleEffect(finalAmount currentAmount)
.simultaneousGesture(magnificationGesture)
.simultaneousGesture(tapGesture)
Первоначально я пытался добавить еще 1 simultaneousGesture, DragGesture(), которые регулируют смещение, но это не сработало.
Текущий код хорошо увеличивает изображение, но после увеличения я хочу, чтобы ему разрешалось панорамирование. Я попытался добавить UIScrollView, но это также не удалось.
Вот моя мысль:
let dragGesture = DragGesture()
.onChanged { value in self.offset = value.translation }
и добавить .offset() к изображению.
Однако он не работает, и у симулятора не хватает памяти.
Есть какие-либо рекомендации?
Комментарии:
1. Попробуйте использовать LazyVGrid и отрегулируйте количество столбцов в зависимости от MagnificationGesture()
2. Я не ищу вид сетки, а жест масштабирования и панорамирования в swiftui
Ответ №1:
Используйте DragGesture()
с position
. Протестирован Xcode 12.1 с iOS 14.2 (проблем с памятью нет.)
struct ContentView: View {
@State private var currentAmount: CGFloat = 0
@State private var finalAmount: CGFloat = 1
@State private var location: CGPoint = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2)
@GestureState private var startLocation: CGPoint? = nil
var body: some View {
let magnificationGesture = MagnificationGesture()
.onChanged { amount in
self.currentAmount = amount - 1
}
.onEnded { amount in
self.finalAmount = self.currentAmount
self.currentAmount = 0
}
// Here is create DragGesture and handel jump when you again start the dragging/
let dragGesture = DragGesture()
.onChanged { value in
var newLocation = startLocation ?? location
newLocation.x = value.translation.width
newLocation.y = value.translation.height
self.location = newLocation
}.updating($startLocation) { (value, startLocation, transaction) in
startLocation = startLocation ?? location
}
return Image("temp_1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width:UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.scaleEffect(finalAmount currentAmount)
.position(location)
.gesture(
dragGesture.simultaneously(with: magnificationGesture)
)
}
}
Комментарии:
1. Можно ли запретить пользователю перемещать изображение с пользовательского экрана, используя ваш метод?
2. Код не работает, когда изображение находится за пределами экрана или когда масштаб пользователя слишком мал. центральное положение изображения может измениться, и изображение исчезнет
3. Я только что добавил решения, основанные на вашем вопросе. Почему одновременные жесты не работают. Для дальнейшего и на основе ваших требований вы должны изменить этот код. И вы должны использовать просмотр прокрутки и настраивать все
4. Пожалуйста, добавьте минимальные ограничения по масштабированию и управляйте всеми сценариями. Мы не выполняем здесь всю функциональность
Ответ №2:
Мне наконец удалось решить проблему с UIViewRepresentable.
struct ImageScrollView: UIViewRepresentable {
private var contentSizeWidth: CGFloat = 0
private var contentSizeHeight: CGFloat = 0
private var imageView = UIImageView()
private var scrollView = UIScrollView()
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: UIViewRepresentableContext<ImageScrollView>) -> UIScrollView {
let image = UIImage(named: "Dummy")
let width = image?.size.width
let height = image?.size.height
imageView.image = image
imageView.frame = CGRect(x: 0, y: 0, width: width ?? 0, height: height ?? 0)
imageView.contentMode = UIView.ContentMode.scaleAspectFit
imageView.isUserInteractionEnabled = true
scrollView.delegate = context.coordinator
scrollView.isScrollEnabled = true
scrollView.clipsToBounds = true
scrollView.bouncesZoom = true
scrollView.isUserInteractionEnabled = true
scrollView.minimumZoomScale = 0.5 //scrollView.frame.size.width / (width ?? 1)
scrollView.maximumZoomScale = 2
scrollView.zoomScale = 1
scrollView.contentSize = imageView.frame.size
scrollView.addSubview(imageView)
let doubleTapGestureRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(sender:)))
doubleTapGestureRecognizer.numberOfTapsRequired = 2;
doubleTapGestureRecognizer.numberOfTouchesRequired=1;
scrollView.addGestureRecognizer(doubleTapGestureRecognizer)
imageView.addGestureRecognizer(doubleTapGestureRecognizer)
return scrollView
}
func updateUIView(_ uiView: UIScrollView,
context: UIViewRepresentableContext<ImageScrollView>) {
}
class Coordinator: NSObject, UIScrollViewDelegate {
var control: ImageScrollView
init(_ control: ImageScrollView) {
self.control = control
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("scrollViewDidScroll")
centerImage()
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView,
with view: UIView?,
atScale scale: CGFloat) {
print("scrollViewDidEndZooming")
print(scale, scrollView.minimumZoomScale, scrollView.maximumZoomScale)
scrollView.setZoomScale(scale, animated: true)
centerImage()
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return self.control.imageView
}
func centerImage() {
let boundSize = self.control.scrollView.frame.size
var frameToCenter = self.control.imageView.frame
frameToCenter.origin.x = 0
frameToCenter.origin.y = 0
if (frameToCenter.size.width<boundSize.width) {
frameToCenter.origin.x = (boundSize.width-frameToCenter.size.width)/2;
}
if (frameToCenter.size.height<boundSize.height) {
frameToCenter.origin.y = (boundSize.height-frameToCenter.size.height)/2;
}
self.control.imageView.frame = frameToCenter
}
@objc func handleTap(sender: UITapGestureRecognizer) {
if (self.control.scrollView.zoomScale==self.control.scrollView.minimumZoomScale) {
self.control.scrollView.zoomScale = self.control.scrollView.maximumZoomScale/2;
} else {
self.control.scrollView.zoomScale = self.control.scrollView.minimumZoomScale;
}
print("tap")
}
}
}