iosアプリの画面遷移をプログラムから呼び出す
プログラムから任意のタイミングでsegueを呼び出したかったが、やり方をググっても何故か中々ヒットしなかったのでメモしておく。
ついでにViewControllerを指定して画面遷移する方法もメモしておく。
Segueを使用して遷移する場合
事前準備
StoryBoard上で、ViewController間をつなぐsegueを作っておく。 作ったsegueのidentifierはちゃんと埋めておく。
コード
以下のコードでsegueを呼び出せる。
performSegue(withIdentifier: "segueのidentifier", sender: self)
またsegueが呼ばれた際の遷移前処理はVieController内で以下のように書ける。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // やりたい処理 }
遷移先の画面に何か渡したい時には下記のように利用するらしい。
//segueを呼び出す処理 func callSegue( ) { let message = "hogehoge" performSegue(withIdentifier: "targetSegue", sender: message) } //次のviewのcontrollerに値を渡す override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "targetSegue") { let nextViewController : NextViewController = segue.destination as! NextViewController nextViewController.item = sender as! String } }
ViewControllerを指定して遷移する場合
プログラムから画面遷移する場合、こっちの方がよく使われるかも
事前準備
storyboard上で遷移先のViewControllerのIdentifierを設定しておく
コード
let storyboard = UIStoryboard(name: "Main", bundle: nil) let nextView = storyboard.instantiateViewController(withIdentifier: "nextViewController") let navi = UINavigationController(rootViewController: nextView) navi.modalTransitionStyle = .coverVertical self.present(navi, animated: true, completion: nil)
iosアプリの起動時の画面をコントロールする
ログイン状態など条件に応じてrootViewControllerを切り替えたかった。 ちょっと調べたところUIWindowのrootViewControllerを設定すれば良いらしい。
var window: UIWindow? let storyboard = UIStoryboard(name: "Main", bundle: nil) if (flag) { let mainViewController = storyboard.instantiateViewControllerWithIdentifier("MainViewController") let navigationController = UINavigationController(rootViewController: mainViewController) self.window!.rootViewController = navigationController } else { let loginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") let navigationController = UINavigationController(rootViewController: loginViewController) self.window!.rootViewController = navigationController }
しかし、このようにすると最初のviewが表示される前に一瞬真っ暗な画面が表示されてしまった。
原因はデフォルトのrootViewControllerが設定されておらず、上記のコードの実行前に未設定のrootViewControllerを表示しようとしているためだった。
ひとまずlaunch中に表示するviewを作りデフォルトのrootViewControllerとして設定することで見かけ上は解決した。
launch前にrootViewControllerを設定する方法があればそれを使いたいが、ググっても見つからなかった。 より良い方法が見つかったら追記したい。
NSUserDefaultsによる永続化
iosアプリを停止させても消したくない情報を永続化する方法を調べた。 永続化の方法としてはNSUserDefaults、NSKeyedArchiver、プロパティリスト、CoreDataなどの方法があるらしい。
CoreDataはちょっとしたデータの保存には大変すぎる。 NSKeyedArchiverはシリアライズしたデータをファイルとして保存する方法で、保存する場所を指定する必要がある。 プロパティリストはkey、valueを指定して.pistファイルに情報を保存する方法で、アプリの設定などを扱っていることが多い。
NSUserDefaultsは保存する場所の指定などが不要で、最も手軽そうだったので今回はこれを使う。
使い方
let userDefaults = NSUserDefaults.standardUserDefaults() //データを保存する self.userDefaults.setObject("hoge", forKey: "huga") //データを取り出す let authData = self.userDefaults.objectForKey("huga") as? String
かなり手軽にデータの保存ができるが上記のコードをそのまま使うと、キーや型をベタ書きすることになり管理がし難い。 ググってみたところ、wrapperを用意して使うのが良さそう。 データがある程度複雑ならプロパティリストやCoreDataを使用するべきだと思う。
また、NSUserDefaultsは消える恐れがあるらしいく、消えてもいいデータに限ってしか使えないようだ。 今回のケースでは問題なかったけど、アプリ中断時に開いていたページの情報とか、トークンのような揮発しても問題ないなものにしか使うべきじゃないのかもしれない。
UIImageをリサイズする
IOSアプリで画像を表示する時、画像サイズに依存せずにViewいっぱいに表示したい。
その際、縦長の画像が縦に押しつぶされた状態になるなど、画像が変にリサイズされて歪んでしまうのは避けたいというコード。
一旦リサイズしさらにトリミングするという処理をしているため、無駄が多いように思える。
もっといい方法がありそうだけ、swift初心者なので思いつかなかった。。。
func fixImageSize(image: UIImage, width: CGFloat, height: CGFloat) -> UIImage{ let widthRatio = width / image.size.width let heightRatio = height / image.size.height let (resizeWidth, resizeHeight) = heightRatio > widthRatio ? (image.size.width * heightRatio, height):(width, image.size.height * widthRatio) let (x, y) = ((resizeWidth - width) / 2, (resizeHeight - height) / 2) let size: CGSize = CGSize(width: resizeWidth, height: resizeHeight) UIGraphicsBeginImageContext(size) image.drawInRect(CGRectMake(0, 0, resizeWidth, resizeHeight)) let resizeImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() let trimRect = CGRectMake(x, y, width, height) let trimRef = CGImageCreateWithImageInRect(resizeImage.CGImage, trimRect) let trimImage = UIImage(CGImage: trimRef!) return trimImage }