ScrollView()は簡単で使い勝手が良いのだが画像編集ではタップポイントの座標とか取得するのに少々難がある。
画像を拡大してタップ、座標取得して加工するにはスクロールメソッドを作る。
座標は基本的に左上が(0,0)である。親ビューが基準というところがミソ。
Imageを親ビューの左上と合わせると同じ座標になる。
Imageが親ビューのサイズより大きい場合はImageをスクロールさせる必要がある。
また、Imageを拡大した場合もスクロールが必要になることも。
スクロールを一方向で簡易化して考えると導きやすい。
Imageは親ビューから逸脱しない。Imageの部分が親ビュー全体に表示されImegeの範囲外が映し出されることはない。
まずは画用紙にカッターで四角い穴を切り抜く。
後ろに画像を置き画用紙をスライドさせて画像の端から端を見る。
という想像。
画像の座標(0,0~imageWidth, imageHeight)
画用紙の穴の左上を(0,0)とすると幅、高さを(windowWidth, windowHeight)
画用紙の穴を画像の上でスライドさせる。想像しやすいので。
この考えだと、穴をオフセット(0~(imageWidth-windowWidth), 0~(imageHeight-windowHeight))で表現できる。
Swiftでは設定方法が違うので変換しなければならない。
穴の方が固定で画像を動かす。そして、穴(親ビュー)の座標が基準なので、
画像のオフセットは( -(imageWidth-windowWidth)~0, -(imageHeight-windowHeight)~0 )になる。
座標が決まったので落とし込み。
import SwiftUI
struct SublView: View {
private var image = UIImage(named: "Test3") // アセットフォルダに用意JPG
private var currentScale: CGFloat = 1.0 // リザーブ。今後使用
@State private var viewSize: CGSize = CGSize.zero
@State private var offset = CGSize.zero
@State private var lastOffset = CGSize.zero // ドラッグ後の座標を記憶
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
var offsetX = 0.0
if image!.size.width > viewSize.width {
let w = image!.size.width * currentScale - viewSize.width
offsetX = lastOffset.width + value.translation.width // 前の座標にドラッグ量を加算
if offsetX > 0.0 { offsetX = 0.0 } // 移動量を制限
else if offsetX < -w { offsetX = -w }
}
var offsetY = 0.0
if image!.size.height > viewSize.height {
let h = image!.size.height * currentScale - viewSize.height + 74 // +74はナビを使っていると上部にタイトル部分が入るので
offsetY = lastOffset.height + value.translation.height
if offsetY > 0.0 { offsetY = 0.0 }
else if offsetY < -h { offsetY = -h }
}
offset = CGSize(width: offsetX, height: offsetY)
}
.onEnded {_ in
lastOffset = offset // 記憶
}
}
var body: some View {
VStack(alignment: .leading) {
GeometryReader { geometry in
Image(uiImage: image!)
.resizable()
.frame( width: image!.size.width * currentScale, height: image!.size.height * currentScale )
.background(GeometryReader {value in
Color.clear
.onAppear{
viewSize = geometry.size // 親ビューサイズを取得
}
})
.offset(offset)
.gesture(dragGesture)
}
}
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Image(systemName: "doc")
}
}
}
}
ドラッグは、スクロール、座標取得、移動など同時利用になるのでモード切り替えで使用となる。
あとで拡大時の処理と座標取得の確認。