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

android OS & iPadOS の記録。

【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でシェアする

【Swift P4.5.1】ScrollViewとZoom

2024-10-03 23:50:57 | Swift iPadOS用

前回の引用からスクロールとズームの両立は複雑だと分かる。
あえて、単純にスクロールとズームを組み合わせたらどうなるか確認をしておく。

で、下にスクロールビューにイメージズームを入れてみた。

結果、スクロールが優先的に作動してしまう。(1ポイント操作)
上手くすればピンチによるズーム。(2ポイント操作)

何かしらの工夫が必要である。

状況は、ズーム後のスクロールは動作する。
しかし、スクロール範囲はズーム後の画像全体ではない。
最初の画像サイズ領域に限られる。何らかの修正をする必要がある。

 

import SwiftUI

struct ContentView: View {
    @State private var currentScale: CGFloat = 1.0
    @State private var lastMagnificationValue: CGFloat = 1.0
    var body: some View {
        ScrollView([.horizontal,.vertical],showsIndicators: false)
        {
            Image(uiImage: UIImage(named: "Test")! ) 
                .resizable()
                .scaledToFill()
                .scaleEffect(currentScale)
                .gesture(
                    MagnifyGesture()
                        .onChanged { value in
                            let changeRate = value.magnification / lastMagnificationValue
                            currentScale *= changeRate
                            currentScale = max(1.0, currentScale )
                            lastMagnificationValue = value.magnification
                        }
                        .onEnded { value in
                            lastMagnificationValue = 1.0
                        }
                )
        }
        .scrollTargetLayout()
        .padding()
    }
}


スクロールとズームの混在は意図した通りの反応でないとストレスが溜まる。
モード切り替えを入れるなどワンクッションが入るとストレス軽減になるのかな?

【追記】
ズーム後にImageの.frame()でズーム後サイズを算出し設定してみたがスクロール範囲の変化なし。

 


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

【Swift P4.5.1】拡大縮小。ピンチジェスチャー

2024-10-02 20:34:46 | Swift iPadOS用

ピンチジェスチャーでのズーム倍率は必ず(1.0)から始まるらしい。
参考書通りに書くとピンチ始まりで必ず原画サイズに戻ってからの拡大になる。
倍率を保存して、そこから再開すれば指を離しても継続できる。とは分かるけど上手くいかない…。

検索に少々掛ったけど何とか見つけた。

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

コピペで検索してください。
記事ではズームとスクロールを両立していました。

ここではズームの継続のみ切り出しています。


import SwiftUI

struct ContentView: View {
    @State private var currentScale: CGFloat = 1.0
    @State private var lastMagnificationValue: CGFloat = 1.0
    var body: some View {
                
        Image(uiImage: UIImage(named: "Test")! ) 
            .resizable()
            .scaledToFill()
            .scaleEffect(currentScale)
            .gesture(
                MagnifyGesture()
                    .onChanged { value in
                        let changeRate = value.magnification / lastMagnificationValue
                        currentScale *= changeRate
                        currentScale = max(1.0, currentScale )
                        lastMagnificationValue = value.magnification
                    }
                    .onEnded { value in
                        lastMagnificationValue = 1.0
                    }
             )
    }
}

数値の計算と設定方法で自分の工夫以上に簡単な記述となった^^。

このコードだと倍率1.0以下になるとエラーになるので
currentScale = max(1.0, currentScale )
倍率1.0以上をセットしている。

frameをきちんと設定すれば良いのかもしれない。

 


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

【Swift P4.5.1】Scrollview

2024-09-28 00:47:43 | Swift iPadOS用

画像と言ったらスクロール!
androidのスクロールはメンドクサ!と思えるくらいあっさりスッキリ。

実際は、グリッドにサムネイル画像を並べてリンク張ってタップされたらスクロールビューを開く感じ。
チュートリアルを見ても簡単でした。


import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack () {
            ScrollView([.horizontal,.vertical], showsIndicators: false) {
                Image(uiImage: UIImage(named: "Test")! ) // アセットにTestファイルを用意
                    .resizable()
                    .scrollTargetLayout()
            }
            .scrollTargetBehavior(.viewAligned)
            .padding()
        }
    }
}

ただ、Test画像は、2300x1600ドットくらい。(uiimage.size調べ)
なのにスクロール範囲が4000x3000(倍のサイズ)くらいで表示されてる。
よく分からない^^;

 

 


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

【Swift P4.5.1】座標系がややこしいのと難解案件

2024-09-26 23:51:10 | Swift iPadOS用

WindowsやAndroidは、左上が(0,0)。
画面の座標、画像の座標、0,0に合わせれば、見えている画面の左上に画像の左上が重なる。

Swiftは、そんな簡単ではない^^;
画像アスペクト比1:1でスケールを1にしたとき、画面より画像が大きいとセンター合わせにされてしまう。
alignment: .leading や alignment: .top でも。

原点とは別に基準点?が画像中央にあり、補正メソッドを組み込まないと面倒。

 

不思議なのは、GeometryReader { geometry in  }で括ると画面左上と画像左上が合わさる。なぜ?

【ContentView.swift】

import SwiftUI

struct ContentView: View {   // タイプA 左上合致
    var body: some View {
        GeometryReader { geometry in // 画面情報取得用
            VStack(alignment: .leading) {
                let uii = UIImage(named: "Test") // "Test"はアセットに大きい画像を用意した
                Image(uiImage: uii!)
                    .resizable()
                    .aspectRatio((uii!.size.width/uii!.size.height), contentMode: .fill)
                
                Text("width: \(uii!.size.width)")  // ”\” は、バックスラッシュ
                Text("height: \(uii!.size.height)")
            }
        }
    }
}


struct ContentView: View {   // タイプB 画像が小さければ左上合致
    var body: some View {
        VStack(alignment: .leading) {
                let uii = UIImage(named: "Test")
                Image(uiImage: uii!)
                    .resizable()
                    .aspectRatio((uii!.size.width/uii!.size.height), contentMode: .fill)
                
                Text("width: \(uii!.size.width)")
                Text("height: \(uii!.size.height)")
        }
    }
}

タイプAは、画面横幅より画像の横幅が大きくてもalignment: .leadingが効く。
タイプBは、画面横幅より画像の横幅が大きいと画面の中央と画像の中央が合わさるようだ。

シミュレーションの状態の話なので実機でどうなっているかは未確認。

 

 


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