月別アーカイブ: 2018年7月

【Swift】ちょっと珍しいエラー?【libclosured.dylib】

先日、アプリをアップデートして課金機能を実装しました。
ユーザーから課金しても良いので広告を外してほしいと熱望されていたのです。
しかし、ただでさえプログラミングに慣れていないのに、課金はかなり理解し難く、たいへん手を焼きました。
おかげで途中数ヶ月放置なんてこともあり、実装できるまでに1年近くかかりました。

で、Xcodeのシミュレータでも、実機でもsandBoxを使って問題なく課金が出来ることを確認して、Appleの審査に提出しました。
アプリの説明にも堂々と「課金で広告が外せるようになりました」と書いて。
最近は審査の時間がどんどん短くなってるようで、今回はびっくりの24時間切り、18時間位で無事リリースされました。

が、そこで問題が起こりました。
設定の画面から課金の画面に行くと、フリーズしてクラッシュするのです。
何度試しても同じ、実験機でも、嫁のiPhoneでも同様でした。
もう一度、Xcodeからビルドして動作確認をしてもこちらは問題なし。
appSotreからダウンロードし直してみるとやっぱりクラッシュ。
こんなんどうやって、デバグしたらいいのと、血の引く思いでした。
毎度、Appleに審査出して通ってから実地確認ってわけにも行かないしね。^^;
あんなに堂々と課金できますよって書いちゃったのに。/(^o^)\

で、まあ、とりあえずいろいろ検索して
下記サイト見つけエラーログが調べられることがわかりました。
swift – 原因のわからないバグの特定方法について – スタック・オーバーフロー

エラーログを取ってみるとズラズラとログが表示されて、

Incident Identifier: 93F4A809-916A-43F3-9035-FC07F83A1D27
CrashReporter Key:   b5ff785017c05598ffb764559b85137d5acda044
Hardware Model:      iPhone10,3

〜〜中略〜〜

0x1a93b1000 - 0x1a93b4fff InternationalSupport arm64  <fd334ec64cdc3287a1c03a90d37338aa> /System/Library/PrivateFrameworks/InternationalSupport.framework/InternationalSupport
0x1acdcc000 - 0x1acdcdfff DCIMServices arm64  <267ae9cb3c873eaa89fd7d48c1e644ae> /System/Library/AccessibilityBundles/DCIMServices.axbundle/DCIMServices
0x1acdce000 - 0x1ace8dfff AGXMetalA11 arm64  <d49bd1c17d5e33d6983ca1307d81e911> /System/Library/Extensions/AGXMetalA11.bundle/AGXMetalA11
0x1ad172000 - 0x1ad18dfff OnBoardingKit arm64  <b37b2003c4db3fe89c6117f83823f0c3> /System/Library/PrivateFrameworks/OnBoardingKit.framework/OnBoardingKit
0x1ad1b3000 - 0x1ad1b5fff ShortcutUIKit arm64  <fc87d5f042363da4bb448acf39be8d95> /System/Library/PrivateFrameworks/ShortcutUIKit.framework/ShortcutUIKit
0x1ad277000 - 0x1ad2a7fff libclosured.dylib arm64  <e61ffac51cae3e1fb9eb6a6e2801777b> /usr/lib/closure/libclosured.dylib

EOF

という感じで、最後は
0x1ad277000 – 0x1ad2a7fff libclosured.dylib arm64 /usr/lib/closure/libclosured.dylib
で終わっていました。

よくわからんけど、/usr/lib/closure/libclosured.dylib このあたりが怪しいかと検索しまくり。

日本語のページでは全く引っからなかったけど、海外のサイトではcrashという文字とともにいくつか引っかかりました。
CFNetwork crash with iOS 11.3.1 |Apple Developer Forums
My app crash with Exception Type:  EXC_CRASH (S… |Apple Developer Forums
Crash in iOS 11.1 | GeoNet

などなど…。

英語はよくわからんが、総じて見ると、原因不明なんだけどOSの問題ちゃうん?って感じでした。

で、AppleDeveloperに電話で聞いても見たのですが、症状の再現はされるけれど、原因はわからんと。
こっちでは無理なので、テクニカルサポートかフォーラムで聞いてみてくれと。
どっちも英語やん。\(^o^)/オワタ

仕方なしに、コードを見直してみたり、App Store Connectを見直してみたり、
、、、してたら、App Store Connect のApp内課金のページですっごい違和感が!

あれ?課金コンテンツって審査用のスクリーンショットで必須だよね?/(^o^)\
でも、設定されてないよね。/(^o^)\

スクリーンショット 2018-07-24 16.51.33

メタデータが不足って書いてあると審査用のスクリーンショットがなかったりします。

よくよくアプリの設定を見たら、Xcodeでもin-App purchaceもオンになってないし、全然課金が動作する状態じゃなかった。/(^o^)\
Xcodeのin-App purchaceがオンになっててスクリーンショットがなかったりしたらリジェクトもされたんだろうけど、すべて後回しにして数ヶ月放置している間にそういうこともすっかり忘れていたようです。
そりゃ課金の設定無いのに、プロダクト情報取りに行ったらフリーズもするわ。^^;
しかし、よくアプリの審査通ったな。/(^o^)\

で、スクリーンショットを撮って、App内課金の設定もして、速攻で再度審査に提出しました。

まあはよ気づいてよかったよ。
こんなこともあるんだね。
うじゃうじゃ。

【Swift4】Google Analytics と eコマース?

SwiftでGoogle Analytics で eコマースを利用しようとしたが、ワケワカラン。
そもそもeコマースがわかってないんだが。
とりあえず課金があったらその情報を受け取れるようにしてみたい。

とりあえず、まずここの説明がSwiftに対応していない。(# ゚Д゚)
e コマース トラッキング – iOS SDK  |  iOS 向けアナリティクス  |  Google Developers

コピペだが、このコードがSwift化できれば良いんだと思う。

/*
 * Called when a purchase is processed and verified.
 */
- (void)onPurchaseCompleted {

  // Assumes a tracker has already been initialized with a property ID, otherwise
  // this call returns null.
  id tracker = [[GAI sharedInstance] defaultTracker];

  [tracker send:[[GAIDictionaryBuilder createTransactionWithId:@"0_123456"             // (NSString) Transaction ID
                                                   affiliation:@"In-app Store"         // (NSString) Affiliation
                                                       revenue:@2.16F                  // (NSNumber) Order revenue (including tax and shipping)
                                                           tax:@0.17F                  // (NSNumber) Tax
                                                      shipping:@0                      // (NSNumber) Shipping
                                                  currencyCode:@"USD"] build]];        // (NSString) Currency code

  [tracker send:[[GAIDictionaryBuilder createItemWithTransactionId:@"0_123456"         // (NSString) Transaction ID
                                                              name:@"Space Expansion"  // (NSString) Product Name
                                                               sku:@"L_789"            // (NSString) Product SKU
                                                          category:@"Game expansions"  // (NSString) Product category
                                                             price:@1.9F               // (NSNumber) Product price
                                                          quantity:@1                  // (NSInteger) Product quantity
                                                      currencyCode:@"USD"] build]];    // (NSString) Currency code

}

参考にしたのがこちら
ios – Issue with Google Analytics in Swift 2 or 3 – Stack Overflow
と、こちら
iOSアプリでもGoogleAnalyticsでがっつり計測する実装方法【総集編】 | Nagisaのすゝめ

なんだかんだイジって、
Xcodeに文句言われないように変更して出来たのがこの形。

func onPurchacedCompleted() {
        
    let tracker = GAI.sharedInstance().defaultTracker
    var builder = (GAIDictionaryBuilder.createTransaction(withId: "トランザクションID", //トランザクションID ◎必須 String?
                                        affiliation: "App-Store", //ショップ? ◎必須 String?
                                        revenue: 100, //収益の合計(税金と送料)◎必須 NSNumber?
                                        tax: 0, //税金 ◎必須 NSNumber?
                                        shipping: 0, //送料 ◎必須 NSNumber?
                                        currencyCode: "JPY")) //通貨 ◎オプション String?
        
    tracker?.send(builder?.build() as! [AnyHashable : Any]?)
        
        
    builder = (GAIDictionaryBuilder.createItem(withTransactionId: "トランザクションID", //トランザクションID ◎必須 String?
                                         name: "商品名", //商品の名前 ◎必須 String?
                                         sku: "商品のSKU", //商品のSKU ◎必須 String?
                                         category: "カテゴリ", //カテゴリ ◎オプション String?
                                         price: 100, //価格 ◎必須 NSNumber?
                                         quantity: 1, //数量 ◎必須 NSNumber?
                                         currencyCode: "JPY")) //通貨 ◎オプション String?
        
    tracker?.send(builder?.build() as! [AnyHashable : Any]?)
}</pre> 

とりあえず自分用。
うまくいかなくったってしーらないしーらない。
うじゃうじゃ。/(^o^)\

【Swift4】tabbarとnavigationControllerを使ってる時のマイナーな?戻り方。

Tabbar Controllerでつながった、タブAの画面とタブB1の画面があり、タブBからはnavigationControllerをつかってタブB2の画面に遷移できるとする。
画面B2に居る時にtabbarでタブAに移動して、またタブBに移動しようとすると、タブB2に移動してしまう。

一旦、タブAに移動してタブBに移動するとき、自動的にタブB1に移動するようにしたかった。

言葉にするとすごくわかりにくい。orz

結論から書くとタブB2の画面が消えたあとにnavigationControllerで戻る指示をすれば良い。

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("viewDidDisappear")
        self.navigationController?.popToRootViewController(animated: true)
    }

こんな感じで。
うじゃうじゃ。

【Swift4】UIScrollViewで電子書籍風に。

UIScrollViewを使って電子書籍風?な感じで見れるものを作ってみました。

参考サイト
[iPhone] UIScrollView ページ スクロール
【Swift】UIScrollViewの使い方。大きい部品をスクロールして見る。 | はじはじアプリ体験記
【Swift】UIScrollViewとUIPageControlを使ってページを移動する方法 | はじはじアプリ体験記
UIScrollView、UIKit座標計算系に関しての復習 – これが最後じゃないからね

基本条件
・表示させる画像ファイルを用意する。
・画像のサイズは多少、大小してもscrollViewの幅にリサイズ、画面中央に配置。
・漫画のように右から左へ見ていく。
・1画面1ページ。

UIScrollViewについて。
UIScrollViewはいろいろとサイズのプロパティがあってややこしい。

とりあえず参考サイトから真似てプロパティを説明する画像を作ってみた。
UIScrollのプロパティ

UIScrollViewに使われるサイズは大きく分けて2つ。
frameとcontentSizeだ。
frameのサイズは画面に表示される「枠」のサイズ。
contentSizeが枠のなかに表示される「移動可能な領域」のサイズ。
イメージとしてはframeの上にcontentSizeが乗ってる感じだけど、
実際にはcontentSizeのうえにframeがあってそこから覗き込んでる感じなのでややこしく感じる。

frameには
frameの位置座標をあらわす(frame.origin.x , frame.origin.y)というプロパティと
frame.size.widthという幅と、
frame.size.heightという高さのプロパティを持っている。
frame.originはiPhoneの画面の起点からの距離というところに注意。

contentSizeは
contentOffset.x , contentOffset.yというscrollViewに対する位置情報と
contentSize.widthという幅と、
contentSize.heightという高さのプロパティを持っている。

contentInsetは今回使わないので割愛。

今回は電子書籍風ということでscrollViewのframeとcontentSizeの高さを揃えて、contentOffset.xの値が変動することによって画面が横スクロールするイメージです。
moveScroll

ではまず、StoryboardでViewControllerを開き、ScrollViewを配置します。
スクリーンショット 2018-07-13 15.49.29のコピー

AutoLayoutで位置を決めて、Paging Enabledにチェックを入れます。これでscrollViewがページ単位で動くようになります。

次いで、Assetsに画像を放り込みます。
スクリーンショット 2018-07-13 21.43.09

コードの記述

・AutoLayoutの処理を待ってviwDidApearから処理を始めます。
・scrollViewのサイズが決まったら高さと幅を取得。
・scrollView幅に合わせて画像のサイズを変更してsubviewに貼っていく。
・subviewを配列に取り込んで順番を逆転。
・順にscrollViewの幅ずつずらしていく。
・scrollViewの幅×画像の数をcontentSizeの幅にする。
・contentSizeの高さはscrollViewの高さと同じ。
・scrollViewの初期位置を一番右の画像に持ってくる。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    
    // contentSize(領域)のなかの各ページ《画像)の高さを入れるインスタンス
    var scrollPageHeight:CGFloat!
    // contentSize(領域)のなかの各ページ《画像)の幅を入れるインスタンス
    var scrollPageWidth:CGFloat!
    
    //表示する画像の名前を配列に入れる
    let img:[String] = ["image-1","image-2","image-3","image-4","image-5"]
    // ページ数を入れるインスタンス
    var pageNum:Int!
    //取り込む元の画像のサイズを保持しておくインスタンス
    var imageWidth:CGFloat!
    var imageHeight:CGFloat!

    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //AutoLayoutでscrollViewのサイズが決定してから計算しないとおかしくなるのでここで処理。
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // ページでスクロールするためにscrollViewのサイズを取得
        scrollPageWidth = scrollView.frame.width
        scrollPageHeight = scrollView.frame.height
        
        //画像の枚数を取得
        pageNum = img.count
        
        //配列のイメージを連続で取り込む
        for i in 0 ..< pageNum {
            // 配列から画像を取り込む
            let image:UIImage = UIImage(named:img[i])!
            let imageView = UIImageView(image:image)
            
            //元画像の幅と高さを取得
            imageWidth = image.size.width
            imageHeight = image.size.height
            
            //元画像からframe情報を取得
            var rect:CGRect = imageView.frame
            //scrollViewの幅に合わせて高さを計算し幅と高さを入力。
            rect.size.width = scrollPageWidth
            rect.size.height =  scrollPageWidth * imageHeight/imageWidth
            
            //scrollViewの幅に合わせたframeサイズを画像に適用
            imageView.frame = rect
            //タグを付ける
            imageView.tag = i + 1
            
            // UIScrollViewのインスタンスに画像を貼付ける
            self.scrollView.addSubview(imageView)
            
        }
        //①
        setupScrollImages()
    }
    
    
    
    func setupScrollImages(){
        
        //配列を作って、scrollViewに貼り付けられたsubviewの画像を取り込む。
        var subviews:Array = scrollView.subviews
        //配列の並び順を逆転させる。
        subviews = subviews.reversed()
        
        // 描画開始の x の位置
        var px:CGFloat = 0.0
        
        //配置された画像を一枚ずつscrollViweの幅でずらしていく
        for i in 0 ..< subviews.count {
            //配列からi番目の画像を取り出す
            let imgView = subviews[i] as! UIImageView
            
            if (imgView.isKind(of: UIImageView.self) && imgView.tag > 0){
                
                //画面の真ん中の高さに写真が来るように計算。
                let py:CGFloat = (scrollPageHeight - imgView.frame.height)/2
                
                //CGRectインスタンスを作ってiamgeViewの座標とサイズを保持。
                var viewFrame:CGRect = imgView.frame
                //原点座標を指定し直す。pxは毎回幅分増えていく。
                viewFrame.origin = CGPoint(x: px, y: py)
                imgView.frame = viewFrame
                
                //次の画像のX軸を画面の幅だけずらす。
                px += (scrollPageWidth)
                
                
            }
        }
        
        // UIScrollViewのcontentSizeを画像の合計サイズに合わせる
        //contentSizeの幅は、scrollViewの幅 × 画像の枚数
        let nWidth:CGFloat = scrollPageWidth * CGFloat(pageNum)
        scrollView.contentSize = CGSize(width: nWidth, height: scrollPageHeight)
        
        //scrollViewの初期位置を右端の画像に合わせる。
        scrollView.contentOffset.x = scrollPageWidth * CGFloat(pageNum - 1)
        
        //②
    }
    
}

①の時点でのイメージはこんな感じ。
scrollViewレイヤーイメージ-01

contentSizeはまだ指定していないので存在しないか(0,0)なので画面は動きません。

画像の順番を並べ替えて右へずらしてframeを初期位置へ移動させて、
最終的に起動した時のイメージは②でこんな感じ。

scrollViewレイヤーイメージ-02

contentSizeの範囲をflameの幅で左右にページ移動します。
一応こんな感じでscrollViewが使えました。

うじゃうじゃ。

【Swift4】効果音のクラスを作ってみた。【AVAudioPlayer】

ずーーっと以前にSwiftで音を出すのはやってみた。
【Swift3】【AVAudioPlayer】音を出してみる。 | iPhoneアプリ備忘録

最近、ちょっとまた効果音を出したくて勉強し直してみた。
効果音を出すにはAudioServicesPlaySystemSoundを使う方法もあって、連打できたりするのは良いのですが、
iPhone側の音量で調節ができないという欠点もあり、AVAudioPlayerを使って連打も出来るようにクラスを作ってみた。

ちなみにクラスとか作るの初めてなのでなんか間違ってても生暖かく見守るか、優しく教えてもらえるとありがたいです。(-人-)

さて、参考にしたのはこちらのサイト。
ARC環境下でAVAudioPlayerを使い柔軟かつシンプルにサウンドを再生する方法

これをSwiftで書いてみたいと思います。

まず、AVAudioPlayerを使うにはAVFoundation.frameworkが必要です。
TARGET>Generalで追加しておきます。

音声ファイルも用意してプロジェクトナビゲータに放り込んでおく。
とりあえずファイル名は「hit.mp3」とする。

んでは、SEManagerというNSObjectのクラスを作ります。

import UIKit
import AVFoundation
class SEManager: NSObject , AVAudioPlayerDelegate{

    //シングルとして使う
    static let sharedInstance = SEManager()
    var soundArray = NSMutableArray()
    
    override init() {
        super.init()
    }
    
    func playSound(soundName : String) throws {
        //サウンドファイルのパスを作ります。
        let soundPath :String = (Bundle.main.path(forAuxiliaryExecutable:soundName)! as NSString) as String
        let URLOfSound : NSURL = NSURL(fileURLWithPath: soundPath as String)
        
        do {
            //AVAudioPlayerのインスタンスを作成
            let player :AVAudioPlayer = try AVAudioPlayer(contentsOf:URLOfSound as URL)
            //配列に収納
            soundArray.insert (player, at: 0)
            //デリゲートを設定
            player.delegate = self
            //音を鳴らします。
            player.play()
            
        } catch {
            print("サウンドエラー",error)
        }
        
    }
    
    //音が鳴り終わったら呼ばれるデリゲート
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        //鳴り終わったインスタンスを削除
        soundArray.remove(player)
        print("サウンド残り",soundArray.count)
    }
    
}

これで音を鳴らすクラスが出来た。
インスタンスを配列に入れて削除する辺り、実はよく分かってない。/(^o^)\
do とか、try とか、throwsとかも理解していない。
動いてるから良しとしてほしい。(^_^;)

んで、音を鳴らして!っていう側。
ボタンアクションでSEManagerを呼び出します。

import UIKit
class playSoundViewController: UIViewController {

    //SEManager用インスタンス作成
    var seManager:SEManager!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        //SEManager用インスタンスを保持
        seManager = SEManager.sharedInstance
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func hitButton(_ sender: Any)  {
        do{
        try seManager.playSound(soundName: "hit.mp3")
            
        } catch {
            print(error)
        }
    }
}

SEManagerに投げる音声ファイルでいろいろと音が鳴らせるので汎用性は高いように思います。
あ、インスタンスとか、オブジェクトとかよく理解できてませんので、なんかおかしい所あるかもしれません。
動いてるから良しとして(以下ry

ま、そんな感じで自分用の備忘録です。
うじゃうじゃ。

【Swift4】objective-cからSwiftのクラスを呼び出す。

おぶしーからすいふとに切り替えて暫く経ちますが、古いアプリなど丸っとスイフトに書き換えるのは面倒なので、
objective-cはそのままで、新規クラスはSwiftでと混在させてます。

んで、objective-cからSwiftのクラスを呼び出す時にちょっと詰まったんで備忘録。

参考サイト
Objective-CコードからSwiftのクラス/プロトコルを使う方法(フレームワーク開発編)
Objective-CベースのプロジェクトからSwiftのクラスを呼び出す
SwiftとObjective-Cの相互利用する際の注意

例えば、
hogehogeというアプリがあって、
higehige.h higehige.m というobjective-cのクラスが有って、
hagehage.swift というswiftのクラスがあるとする。

higehige.m で hagehage.swiftクラスを使いたいので、適当にインポートしてみた。

#import "hagehage.swift"  //'hagehage.swift'file not found

エラーが出た。(´・ω・`)ショボーン

ネットで調べて、-Swift.hを着けたら良いのね!(`・ω・´)シャキーン

#import "hagehage-swift.h"  //'hagehage-swift.h'file not found

あれ?(´・ω・`)

#import "hagehage.swift"  //'hagehage.swift'file not found
#import "hagehage-swift.h"  //'hagehage-swift.h'file not found
#import "hagehage-Swift.h"  //'hagehage-Swift.h'file not found
#import "<hagehage>-Swift.h"  //'<hagehage>-Swift.h'file not found
#import <hagehage-swift.h>  //'hagehage-swift.h'file not found
#import <hagehage-Swift.h>  //'hagehage-Swift.h'file not found

ムキー!なんで通らんのや!/(^o^)\

じっくり調べなおすと、、、「プロダクト名」-Swift.h なのか…。orz

#import "hogehoge-Swift.h"

これでよかったようです。/(^o^)\
クラス呼び出すんやからクラス名だと思うやん…。(´・ω・`)

ちなみに呼ばれる側は

@objcMembers
class hagehage : NSObject {

のように@objcMembersをつけるか、メソッドの前に @objc をつけておくようです。

他にもハマりどころはありそうなので他サイトも参考に。
うじゃうじゃ。

2018/07/10追記
上記のようにswiftファイルをインポート出来るようになりました。
で、次の記事にあるSEManager.swiftで音を鳴らしたいのですが、
objective-cからSEManager.swiftのメソッドを呼び出そうとするとエラーが!!

[[SEManager sharedInstance]playSoundWithSoundName:@"hit.mp3"];    // No visible @interface for 'SEManager' declares the selector 'playSoundWithSoundName'

SEManager.swiftのplaySoundメソッドは引数があるのでobjective-c側からはplaySoundWithSoundNameで呼び出します。
xcodeの変換サポートでも候補に出るし、問題なく認識していそうなのですが、どーやってもNo visibleのままです。

よくわからないまま試行錯誤を繰り返すこと丸一日、SEManager.swiftのplaySoundメソッドの中にある’throws’を消すと認識されました。
throws,do,try,catchというのはよく分かってないですがAVAudioPlayerを使う時にXcodeからつけろーと言われます。
このthrowsを消すと、別のswiftファイルからplaySoundメソッドを呼び出す所で黄色三角のエラーが出ますがビルドは出来ます。
なんとも明確な解決策が見いだせませんでしたが、とりあえず動いたので良しとしつつ、自分の無能を呪っときましょう。/(^o^)\

【Swift】レイアウトがずれる。【AutoLayout】追記あり。

原因が分かってからネットを検索すると情報はいっぱい出てくるんですけどね。
原因にたどり着くまでが長かった。orz
忘れると困るので備忘録。

参考サイト
http://rindatetu.hatenablog.jp/entry/2016/03/06/154946

症状としてはStoryboardでAutoLayoutで指定したスクロールビューがおかしい。
機種(画面サイズ)によって位置がずれる。

結果的にはAutoLayoutで指定したビューを元にviewDidLoadメソッドで更に描画指定をしようとしたからずれたようだ。

自分が理解しているviewControllerのライフサイクルは以下のような感じ。

・viewDidLoad
最初に一回だけ呼ばれる。
初期設定とか何でも放り込みがちだよね。

・viewWillAppear
開くたびに呼ばれる。
他の画面で設定したものを毎回画面に来る度に反映させたいときなどにつかう。

・viewDidAppear
画面の表示、レイアウトが終わった所で呼ばれる。

・viewWillDisappear
他の画面に遷移する直前に呼ばれる。

・viewDidDisappear
他の画面に遷移したあとに呼ばれる。

今回はAutoLayoutの処理が終わったあとに更に描画処理をする必要があったため、viewDidAppearに処理を書くことで対処できた。
しかし気づくまでに3日もかかったとか…。orz

追記
さらにその後問題が見つかった。
viewDidAppearにレイアウトの処理を書くと、毎回開くたびにレイアウトがやり直される。
今回はscrollViewで電子書籍風のページを作ったが、別のviewに行って戻ってくると最初のページに戻ってしまう。

この問題の解決方法がこちら
参考サイト
[Swift] viewDidLayoutSubviewsで初回だけ処理をしたい | 黒ごまのおむすび

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    _ = self.initViewLayout
}

private lazy var initViewLayout : Void = {
    print(self.view.frame)
}() 

よくわからないが lazy というのあり、これを使うと
初回一回のみの処理となるらしい。

まだよく理解できてないが、一応思ったとおりの動作になったので良しとしよう。
うじゃうじゃ。

【Swift】CGFloat、CGPoint、CGSize、CGRect

よくわかんないので自分用まとめ。
よくわかんないので超ざっくりです。(^_^;)

参考サイト
Swiftで遊ぼう! – 249 – UIViewの座標システム: iOS12 – Swiftで遊ぼう! on Hatena
Swiftで遊ぼう! – 299 – Scroll Viewの座標システム – Swiftで遊ぼう! on Hatena
基本を理解するために(CGPoint,CGSize,CGRect,frame)

CGFloat
これは座標(x,y)や画像のサイズ(width,Height)などに入れる「数値」が入る型。Double型もしくはFloat型から変換できる。

let cgf = CGFloat(aDouble)

CGPoint
これは座標を表す型。.xと.yのプロパティを持っている。もちろん(x,y)はCGFloat型。

var point = CGPoint(x: 37.0, y: 55.2)

CGSize
これはそのままサイズを表す型。.widethと.heightのプロパティをもっている。もちろんCGFloat型。

var size = CGSize(width: 10.0, height: 50.5)

CGRect
これは、座標(CGPoint)とサイズ(CGSize)を持つ型。

struct CGRect {
    var origin: CGPoint //originプロパティは原点となるCGPoint(座標)を保持する。
    var size: CGSize  //sizeプロパティはCGSize(縦横のサイズ)を保持する。
}

let rect = CGRect(origin: aCGPont, size: aCGSize)

まだまだよく分かってないっすね。/(^o^)\