#swift #swiftui #gesture
Вопрос:
Я работаю над некоторым кодом, в котором пользователям должно быть разрешено использовать различные жесты на изображении приложения. Я хотел бы понять, как я могу разрешить пользователям нажимать или дважды нажимать на определенное место на изображении. Прямо сейчас они могут масштабироваться только от центра, иначе я получаю неожиданные результаты с увеличением. Я выбрал этот путь, так как для этого используется как SwiftUI, так и функция перетаскивания для отключения. Спасибо!
import SwiftUI
public struct SampleZoom {
@ObservedObject
private var viewModel: ViewModel
@State private var currentZoom: CGFloat = 0
@State private var endingZoom: CGFloat = 1
@State private var isZoomed = false
@State private var pointTapped: CGPoint = .zero
@GestureState var swipeOffset: CGSize = .zero
public init(viewModel: ViewModel) {
self.viewModel = viewModel
}
}
extension SampleZoom: View {
public var body: some View {
if viewModel.isVisible {
ZStack {
Color.black
.opacity(viewModel.bgOpacity)
.edgesIgnoringSafeArea(.all)
image(imageData: viewModel.image)
}
.onAppear {
endingZoom = 1
isZoomed = false
}
.overlay(
/// Close Button
Button(action: {
viewModel.isVisible.toggle()
}, label: {
Image(systemName: "xmark")
.foregroundColor(.white)
.padding()
.background(Color.white.opacity(0.33))
.clipShape(Circle())
})
.padding(Spacing.standard), alignment: .topLeading
)
} else {
Spacer()
}
}
}
private extension SampleZoom {
/// Creates card Image
func image(imageData: Data) -> some View {
GeometryReader { reader in
Image("Your Image Here!")
.resizable()
.aspectRatio(contentMode: .fit)
.offset(y: viewModel.imageOffset.height)
.animation(.default)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.scaleEffect(endingZoom currentZoom > 1 ? endingZoom currentZoom : 1, anchor: UnitPoint(
x: pointTapped.x / reader.frame(in: .global).maxX,
y: pointTapped.y / reader.frame(in: .global).maxY
))
/// Double tap to zoom
.gesture(
TapGesture(count: 2).onEnded {
withAnimation {
isZoomed.toggle()
endingZoom = endingZoom > 1 ? 1 : 2
}
}
.simultaneously(
with: DragGesture(minimumDistance: 0, coordinateSpace: .global).onChanged { value in
if !isZoomed {
pointTapped = value.startLocation
}
}
/// Swipe amp; close interactions
.updating($swipeOffset) { value, outValue, _ in
outValue = value.translation
viewModel.onChange(imagePosition: swipeOffset)
}
.onEnded(viewModel.onEnd(swipeDistance:))
)
)
/// Pinch amp; zoom interactions
.gesture(
MagnificationGesture()
.onChanged { amount in
currentZoom = amount - 1
}
.onEnded { _ in
endingZoom = currentZoom
currentZoom = 0
isZoomed = endingZoom currentZoom > 1 ? true : false
}
)
}
}
}
public extension SampleZoom {
final class ViewModel: ObservableObject {
let image: Data
@Published
var isVisible: Bool
@Published
var imageOffset: CGSize = .zero
@Published
var bgOpacity: Double = 1
func onChange(imagePosition: CGSize) {
imageOffset = imagePosition
let halfScreenHeight = UIScreen.main.bounds.height / 2
let progress = imagePosition.height / halfScreenHeight
withAnimation(.default) {
bgOpacity = Double(1 - (progress < 0 ? -progress : progress))
}
}
func onEnd(swipeDistance: DragGesture.Value) {
withAnimation(.easeOut) {
var translation = swipeDistance.translation.height
if translation < Spacing.none {
translation = -translation
}
if translation < Spacing.doubleExtraLarge * 3 {
imageOffset.height = Spacing.none
bgOpacity = 1
} else {
isVisible.toggle()
imageOffset.height = Spacing.none
bgOpacity = 1
}
}
}
init(image: Data, isVisible: Bool) {
self.image = image
self.isVisible = isVisible
}
}
}
Ответ №1:
Вы можете очень легко щелкнуть, чтобы увеличить изображение с помощью UIScrollView. Добавьте UIScrollView и внутри добавьте изображение. Затем реализуйте UIScrollViewDelegate и функцию viewForZooming
override func viewDidLoad() {
super.viewDidLoad()
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 10.0
scrollView.delegate = self
}
extension YourVC: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return image
}
}