Создание страницы, на которой отображается фото, похожее на приложение для фотографий (масштабирование и панорамирование)

#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")
        
    }
}
 

}