タブレット用プログラムの書き止め

android OS & iPadOS の記録。

【Swift P4.5.1】Retinaディスプレイについて

2024-10-18 23:03:43 | Swift iPadOS用

単純に画像を表示するのは綺麗。
ピンチで拡大したら、綺麗。

アプリユーザーは座標を細かく意識しなくていいので綺麗な画面はとても良い。

 

ただ、画像を編集しようとプログラミングする時に…。え?どういう事!

.Fit や .Fillはスクリーンショットに近いイメージなんだけど、
画像ファイルを読み込みそのまま表示させた時に違和感が出る。

Retinaの構造上?の事から起こるものらしい。

 

作業にてiPadでスクリーンショットを撮った画像ファイルをUIImageで読み込みスケール1でそのまま表示する。
画像widthはデバイス横幅と同じ? 画像ファイルは 2,360x1,640サイズ。(iPadのRetina公称同じ)
なのに表示されている画像はデバイス画面の中に入りきらず画像面積1/4(左上)部分になっている。

そして、自作スクロールと画像オフセットで画像自体の座標を取得すると...。
画像右下スクロール座標取得で画像MaxWidth、MaxHeightで画像座標はほぼ正常だった。

 


Swift iPad用プログラミングを初めてする者には戸惑う事柄。

ピクセルには物理ピクセルと論理ピクセルがある。
物理ピクセルは、1ドット=1ピクセル
論理ピクセルは、1ドット=複数ピクセル(2x2等)

スクリーンショットして画像ファイルに保存すると、2,360x1,640サイズになる。
プログラミングで画像ファイルから読み込みプレーン表示すると、1,180x820ドット部分しかデバイスに表示されない。

 

日記を書くにあたり、フルスクリーンの時のViewサイズを調べてみた。(今更だけど)
親viewの幅は 1,180、高さ 820 …。ふ~む^^;

今まで確認してなかった(笑)
確かに論理ピクセル(2x2)でした。

 

画像とデバイスの座標相関を留意しつつプログラミングする事が大切だと分かった。
C++脳には理解に時間がかかる(´;ω;`)

 

 

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

【Swift P4.5.1】Dragに二つの処理を割り当てる

2024-10-15 22:35:09 | Swift iPadOS用

ダブルタップで、isModeを true <-> false と切り替える。(ボタンで切り替えるのも良いかな)
true の時にアイコンを表示して画像をドラッグでスクロール。
falseの時にアイコンを非表示にしてドラッグ座標値を処理する。(切り抜きとか)

struct SubView: View {
    
    // 省略
    @State private var handLocation = CGPoint.zero

    func drag_scroll(size: CGSize) {
        // 変更無しなので省略
    }
    
    var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                if isMode {
                    drag_scroll(size: value.translation)
                    handLocation = value.location // 指し手アイコンを表示する場所
                }
                else {
                    var point = value.location
                    point = convertImagePosition(viewPoint: point, imageOffset: lastOffset)
                    print("x: \(point.x) y: \(point.y)" )  // 画像上の座標
                }
            }
            .onEnded {_ in 
                if isMode {
                    lastOffset = offset
                }
                else {
                    // 最終処理
                }
            }
    }
    
    func convertImagePosition( viewPoint:CGPoint, imageOffset: CGSize ) -> CGPoint {
        var point = viewPoint
        point.x = ( point.x  - imageOffset.width ) / currentScale
        point.y = ( point.y  - imageOffset.height ) / currentScale
        return point
    }
    
    var body: some View {
        ZStack(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)
                    .gesture(TapGesture(count: 2) // ダブルタップでドラッグでの処理を切り替える
                        .onEnded() {_ in
                            isMode = !isMode
                        }
                    )

                Image(systemName: "hand.point.up") // 指し手アイコン、スクロールモードの時に表示する
                    .font(.largeTitle)
                    .foregroundColor(.white)
                    .position(handLocation)
                    .opacity(isMode ? 1: 0) // 透明度を変化して表示、非表示を切り替える
            }
        }
    }


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

【Swift P4.5.1】自作スクロールの画像座標の取得確認。

2024-10-14 18:25:26 | Swift iPadOS用

ズームの拡大率を変えて試したが画像の座標と大体合っている。


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
    // 中略
    
    var body: some View {
        VStack(alignment: .leading) {
            GeometryReader { geometry in 
                Image(uiImage: image!)
                    .resizable()

                    //中略

                    .onTapGesture() { value in
                        let point = convertImagePosition(viewPoint: value, imageOffset: lastOffset)
                        
                        print("x: \(point.x) y: \(point.y)" ) // 座標値を表示。確認後は消去
                    }
            }
        }
       // 省略
    }

    func convertImagePosition( viewPoint: CGPoint, imageOffset: CGSize ) -> CGPoint {
        var point = viewPoint
        point.x = ( point.x  - imageOffset.width ) / currentScale // 本当は、currentScaleも引数で渡したい。(手抜き)
        point.y = ( point.y  - imageOffset.height ) / currentScale
        return point
    }

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

【Swift P4.5.1】Scrollを作る

2024-10-13 00:01:41 | Swift iPadOS用

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

ドラッグは、スクロール、座標取得、移動など同時利用になるのでモード切り替えで使用となる。
あとで拡大時の処理と座標取得の確認。

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

【Swift P4.5.1】ScrollViewとZoom(2)

2024-10-06 18:30:55 | Swift iPadOS用

単純にScrollView()とMagnifyGesture()を利用すると画像の拡大状態でスクロールさせても元の画像の一部分しか見られず、端までスクロールできない。


note.comさんの
【SwiftUIでImageをピンチイン・ピンチアウト・ダブルタップでズームさせる】


このコードをそのまま使わせてもらうと拡大時のスクロールで幅は元の画像の端までスクロールして全部見られる。
しかし、高さは元の画像の端までスクロールできない。

ビューの横幅、アスペクト比を渡している事から、アスペクト比の求め方辺りに何かあると思えた。
なので、色々試した結果。元の画像のアスペクト比を算出して渡してみたら横も高さも端までスクロールできた。

UIImageを用意してCGSizeを取り出す。
アスペクト比を求めた場所で元の画像のアスペクト比を求める。

他はそのまま使えば拡大された元画像の完全スクロールができる。

対処療法的な対応はできたけど、ScrollView()のスクロール範囲についての理解は不明瞭のまま。
それは後回しにして他の基礎習得に移る。


import SwiftUI

struct ContentView: View {
    @State private var aspectRatio: CGFloat = 1.0

    // 追加
    @State private var image = UIImage(named: "Test") // アセットフォルダに Test.jpg を入れて利用しています
    // 

    var body: some View {
        GeometryReader { proxy in
            Image(uiImage: image!)
                .resizable()
                .scaledToFit()
                .frame(width: proxy.size.width)
             // backgroundでGeometryReaderを使うことで、対象のViewのサイズを取得できる
                .background(GeometryReader { imageGeometry in
                    Color.clear
                        .onAppear {
                            // 【追加】画像のアスペクト比を算出する。置き換え部分
                            aspectRatio = image!.size.width / image!.size.height
                    
                            /* ビューのアスペクト比を算出していた。元部分
                            aspectRatio = imageGeometry.size.width / imageGeometry.size.height
                            */
                        }
                })
                .modifier(ImageMagnificationModifier(contentSize: proxy.size, aspectRatio: aspectRatio))
                .background(.black)
                .ignoresSafeArea()
        }
    }
}

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする