20191021のiOSに関する記事は10件です。

Swift勉強の始まり

とりあえず電卓のUIを作ってみたいと思う

1。Xcodeを起動する
image.png

2。Create a new Xcode projectを選ぶ
image.png

3。Single View Appを選ぶ
image.png

4。基本情報欄で適当に入力し、Nextを押す。
image.png

5。保存先を選ぶ前に、Source Controlのチェックを外す。Createを押す
image.png

6。ここの xxxx.storyboardをクリックする
image.png

7。真ん中の画面でスマホ画面の位置を適切に調整する
image.png

8。右上の+ボタンを押すと、Objectの一覧表がポップアップされる。
何度も調節したら。。
image.png

9。三角を押せばいい
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UITableViewをUIStackView + UIStackViewに置き換える

UITableViewにCustomCellゴテゴテで実装された画面を、UIScrollViewに置き換える機会がありましたので共有します。
主にUIStackViewへの追加で制約が入らなくてつまりました。

※UITableViewCellをそのまま使う前提で書いてます。
普通にViewを置く場合はもっと簡単にできると思います。

前説

まずUITableViewCellってコードから生成できるの…?

というところから始まりました。
途中、
制約全然効いてくれない!!
セルの高さが全部同じになっちゃう!!
横幅がおかしくなる!!!

とか色々やりながら、結果生まれたのが以下のコードです。

その結果生まれたのが以下のコードです。

コードととストーリーボードの例

ViewController.swift
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var stack: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let row = 20
        for i in 0...row {
            let cell = Cell.init(style: .default, reuseIdentifier: "Cell", widht: self.stack.frame.width)
            cell.heightAnchor.constraint(equalToConstant: cell.frame.height).isActive = true
            cell.hogeLabel.text = "\(i)"
            print("add to stack:\(cell)")
            self.stack.addArrangedSubview(cell)
        }
    }
}
Cell.swift
import UIKit

class Cell:UITableViewCell {
    // セルにはラベル1つだけおいてある
    @IBOutlet weak var hogeLabel: UILabel!

    // ストーリーボードで配置した時の初期化処理※ストーリーボードからの初期化は想定してません
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.nibInit(width: 0.0)
    }

    // 横幅を受け取って初期化する(制約がうまく効かなかったので)
    init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, widht:CGFloat) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.nibInit(width: widht)
    }

    // viewの初期化
    fileprivate func nibInit(width: CGFloat) {
        guard let view = UINib(nibName: "Cell", bundle: nil)
            .instantiate(withOwner: self, options: nil)
            .first as? UIView else { return }

        //セルの高さはセル側でランダムに決める※使うときはちゃんとConstとか作って高さ設定してね
        let height = Bool.random() ? 50:100

        view.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height))
        self.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height))
        self.addSubview(view)
    }
}

ストーリーボードは特に特別な設定はしておりません。
普通にScrollViewをおいて中にStackViewを配置するだけです。
スクリーンショット 2019-10-21 21.24.23.png


もっとこんな方法あるよ!!や、
ここちょっとやばいよ!!などございましたらお教えいただけますと幸いです。

ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UITableViewをUIScrollView + UIStackViewに置き換える

UITableViewにCustomCellゴテゴテで実装された画面を、UIScrollViewに置き換える機会がありましたので共有します。
主にUIStackViewへの追加で制約が入らなくてつまりました。

※UITableViewCellをそのまま使う前提で書いてます。
普通にViewを置く場合はもっと簡単にできると思います。

前説

まずUITableViewCellってコードから生成できるの…?

というところから始まりました。
途中、
制約全然効いてくれない!!
セルの高さが全部同じになっちゃう!!
横幅がおかしくなる!!!

とか色々やりながら、結果生まれたのが以下のコードです。

その結果生まれたのが以下のコードです。

コードとストーリーボードの例

ViewController.swift
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var stack: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let row = 20
        for i in 0...row {
            let cell = Cell.init(style: .default, reuseIdentifier: "Cell", widht: self.stack.frame.width)
            cell.heightAnchor.constraint(equalToConstant: cell.frame.height).isActive = true
            cell.hogeLabel.text = "\(i)"
            print("add to stack:\(cell)")
            self.stack.addArrangedSubview(cell)
        }
    }
}
Cell.swift
import UIKit

class Cell:UITableViewCell {
    // セルにはラベル1つだけおいてある
    @IBOutlet weak var hogeLabel: UILabel!

    // ストーリーボードで配置した時の初期化処理※ストーリーボードからの初期化は想定してません
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.nibInit(width: 0.0)
    }

    // 横幅を受け取って初期化する(制約がうまく効かなかったので)
    init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, widht:CGFloat) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.nibInit(width: widht)
    }

    // viewの初期化
    fileprivate func nibInit(width: CGFloat) {
        guard let view = UINib(nibName: "Cell", bundle: nil)
            .instantiate(withOwner: self, options: nil)
            .first as? UIView else { return }

        //セルの高さはセル側でランダムに決める※使うときはちゃんとConstとか作って高さ設定してね
        let height = Bool.random() ? 50:100

        view.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height))
        self.frame = CGRect(x: 0, y: 0, width: width, height: CGFloat(height))
        self.addSubview(view)
    }
}

ストーリーボードは特に特別な設定はしておりません。
普通にScrollViewをおいて中にStackViewを配置するだけです。
スクリーンショット 2019-10-21 21.24.23.png


もっとこんな方法あるよ!!や、
ここちょっとやばいよ!!などございましたらお教えいただけますと幸いです。

ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無音カメラ on Pythonista

無音カメラ作った

Environment

  • iPhone 11 Pro
  • iOS 13.1.3
  • Pythonista 3.3

機能

  • iPhone 11 Proのタピオカメラに対応した
  • タップでピント調整できるようにした
  • ピンチイン/アウトでズームできるようにした
  • 横向きでとったらちゃんと回転するようにした
  • 4Kでちょっと画質がいい

バグ・まだなこと

  • 解像度がちょっとおかしい
  • 連写したら落ちる
  • フラッシュ
  • iPad
  • Objc_utilとiOSのカメラの仕組み
  • ☠️?スパゲティコード?☠️
  • 11 Pro以外で動くかはちょっとわかんない

スクリーンショット

175F8E9B-84AB-484D-81D5-0556FFABC724.jpeg

ソースコード

こちらからどうぞ?‍♂️

?Github?

cloneしてフォルダ構造はなるべくそのままで使ってください

感想

iOSのアプリ開発って難しいデス

感謝

宣伝

pythonで画像処理 〜黒板の写真を撮って文字以外を透過させた画像を作る〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[iOS] 無音カメラ on Pythonista

無音カメラ作った

Environment

  • iPhone 11 Pro
  • iOS 13.1.3
  • Pythonista 3.3

機能

  • iPhone 11 Proのタピオカメラに対応した
  • タップでピント調整できるようにした
  • ピンチイン/アウトでズームできるようにした
  • 横向きでとったらちゃんと回転するようにした
  • 4Kでちょっと画質がいい
  • 他のスクリプトから呼び出すことも考えた

バグ・まだなこと

  • 解像度がちょっとおかしい
  • 連写したら落ちる
  • フラッシュ
  • iPad
  • Objc_utilとiOSのカメラの仕組み
  • ☠️?スパゲティコード?☠️
  • 11 Pro以外で動くかはちょっとわかんない

スクリーンショット

175F8E9B-84AB-484D-81D5-0556FFABC724.jpeg

ソースコード

こちらからどうぞ?‍♂️

?Github?

cloneしてフォルダ構造はなるべくそのままで使ってください

感想

iOSのアプリ開発って難しいデス

感謝

宣伝

pythonで画像処理 〜黒板の写真を撮って文字以外を透過させた画像を作る〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIScrollVIewの上でも、キーボードが現れたときにいい感じにスクロールする

備忘録兼紹介です。
UIScrollView × 複数のUITextFieldで、キーボードが出たときにスクロールさせる処理を紹介します。

動き

コード

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scroll: UIScrollView!    // UITextFieldとかを配置するView
    @IBOutlet weak var stack: UIStackView!      // 表示サンプル用

    // ScrollViewのBottomの制約
    @IBOutlet weak var scrollViewBottomConstraints: NSLayoutConstraint!

    private var nowEditingField:UITextField!    // 編集中のUITextField
    private var offsetAdded:CGFloat?            // スクロール戻す時用

    override func viewDidLoad() {
        super.viewDidLoad()

        // 表示サンプルとして、UILabelを20個配置
        let lines = 20
        for count in 1...lines {
            let field = UITextField()
            field.placeholder = "\(count)"
            field.borderStyle = .roundedRect
            field.textAlignment = .center

            field.delegate = self // ← UITextFieldのdelegateを拾いたいので、delegateを設定する

            self.stack.addArrangedSubview(field)
            field.widthAnchor.constraint(equalToConstant: 250.0).isActive = true
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        // キーボードが表示されるときと消えるときに動作させるメソッドを登録
        // キーボードの高さがほしかった
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillShow),
            name: UIResponder.keyboardWillShowNotification,
            object: nil
        )
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(UIResponder.keyboardWillShowNotification)
    }
}

extension ViewController: UITextFieldDelegate {

    // テキストフィールドにフォーカスがあたった時に呼ばれる
    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        self.nowEditingField = textField
        return true
    }

    // テキストフィールドからフォーカスが外れる時に呼ばれる
    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
        self.nowEditingField = nil
        let offsetAdded = self.offsetAdded
        self.offsetAdded = nil
        var offset:CGPoint = CGPoint(x: 0, y: 0)
        if self.scroll.contentOffset.y - (offsetAdded ?? 0) < 0 {
            // 上端
            offset = CGPoint(x: self.scroll.contentOffset.x, y: 0)
        }else if self.scroll.contentOffset.y > (self.scroll.contentSize.height - self.scroll.frame.size.height - self.scrollViewBottomConstraints.constant) {
            // 下端
            offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y)
        }else {
            // その他
            offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y - (offsetAdded ?? 0))
        }

        self.scrollViewBottomConstraints.constant = 0
        self.scroll.setContentOffset(offset, animated: true)

        return true
    }

    // キーボードが表示される直前に呼ばれる ※textFieldShouldBeginEditingより後に呼ばれる
    @objc func keyboardWillShow(_ notification: Notification) {
        guard let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
            return
        }
        guard let field = self.nowEditingField else { return }

        let keyboardHeight = keyboardFrame.cgRectValue.height
        let keyboardTopLine = self.view.frame.height - keyboardHeight

        let bottomLine = field.frame.origin.y + field.frame.height
        let displayBottom = bottomLine - self.scroll.contentOffset.y

        if displayBottom > keyboardTopLine {
            let sub = displayBottom - keyboardTopLine
            self.offsetAdded = sub
            let offset = CGPoint(x: self.scroll.contentOffset.x, y: self.scroll.contentOffset.y + sub)

            self.scroll.setContentOffset(offset, animated: true)
            self.scrollViewBottomConstraints.constant = keyboardHeight
        }
    }

    // キーボードのリターンでキーボードを閉じる
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        self.view.endEditing(true)
        return true
    }
}


ありがとうございました。
よかったら使ってください。(もっといい実装があれば教えて下さい。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UILabelが省略されているか判定する

意外と見つからなくて困ったので、自分なりの実装方法を共有します。

コード

extension UILabel {

    var isOverflowing:Bool {
        return (realLineCount() > self.numberOfLines)
    }

    private func realLineCount()-> Int {
        guard let font = self.font else { return 0 }
        guard let text = self.text, text != "" else { return 0 }
        let sizeForWidthCheck = CGSize(width: Int.max, height: Int(ceil(font.pointSize)))

        let oneLineWidth = text.boundingRect(with: sizeForWidthCheck,
                                             options: .usesLineFragmentOrigin,
                                             attributes: [NSAttributedString.Key.font: font],
                                             context: nil).width
        let boundingWidth = text.boundingRect(with: self.bounds.size,
                                              options: .usesLineFragmentOrigin,
                                              attributes: [NSAttributedString.Key.font: font],
                                              context: nil).width
        return Int(ceil(oneLineWidth / boundingWidth))
    }

}

解説

UILabel.textは省略されている場合でも全文を返してくれるので、それを使用します。
realLineCountメソッドで、省略せずに全文を描画したときの行数を計算します。

let oneLineWidth = text.boundingRect(with: sizeForWidthCheck,
                                             options: .usesLineFragmentOrigin,
                                             attributes: [NSAttributedString.Key.font: font],
                                             context: nil).width

boundingRectメソッドはStringを描画した場合のRectを返してくれます。
これを使って、1行で全部描画したときの横幅を計算します。1

return Int(ceil(oneLineWidth / boundingWidth))

それをboundingWidth(Labelに描画したときの横幅)で割って、行数を計算します。2
小数点以下はいらないので、繰り上げます。

var isOverflowing:Bool {
    return (realLineCount() > self.numberOfLines)
}

計算した行数を自身の行数(ここは実際に表示される行数)と比較し、
大きければ省略されているのでtrueを返します。

純正で用意して欲しいな……

ありがとうございました。


  1. widthがInt.maxになっておりますが、
    こんなに使うことないと思うので適宜小さめの値に変えたほうが良いかもしれません。 

  2. 行数が増えると若干の誤差が出ると思います。(多分) 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIScreenとUIWindowの違い

前回iOSアプリの基本構造という記事を書いたのですが、
長くなりすぎて、UIScreenとUIResponderについても入れたかったのですが、割愛しました。
今回はUIScreenについて書きます。

画面のサイズUIScreenからとるか?viewからとるか?

image.png

端末の画面サイズがコードの中で欲しいとき、ありますよね。
ViewControllerの中なら、

view.bounds

でとれますが、viewプロパティ持ってないクラスだと、この手はつかえません。
あと参照しているViewが画面全体を指定してないと、思ってたのと違うことになります。
色々やりようはあると思いますが、僕は

UIScreen.main.bounds

でとっていました。
あるいは、

view.window?.bounds

と指定する方法もありますね。
これだとUIViewが所属するUIWindowの領域がとれます。

コード的には、どれでもいいっちゃどれでもいいのですが、自分の中でもやもやしていたところなので、調べてみました。

UIScreen/UIWindow/UIView

ところで、appDelegateの中に、windowプロパティがあって、UIWindowの参照を持ってますね?
これでも画面サイズは取得することができます。

UIApplication.shared.keyWindow?.bounds

つまり端末の画面サイズをとる選択肢としては、3つあるわけです。

  1. UIScreen
  2. UIWindow
  3. UIView

3番目のUIViewからとるケースは、UIViewが端末サイズとイコールで定義されている場合限定なので、使える状況は限られます。
なので、UIScreenとUIWindow、どっちを使うのがええんや? というのが問題なわけです。

UIScreenとUIWindowの違い

端末サイズをとりたい、という目的だけで見ると、UIScreenとUIWindowは似たようなもんに見えるのですが、
ちゃんとドキュメント読んでみたら全然違うクラスでした。

Windows and Screens

UIScreenとは

UIScreenは、「ハードウェアの画面を抽象化したクラス」です。
iPhoneで言えばiPhoneの画面を表現していますし、AppleTVで言えばテレビ画面を表現しています。
iOSアプリで、最低1つ持つ必要があります。
つまりUIScreenは、iOSアプリが表示したいもの(What)を表しているのではなくて、表示する場所(Where)を表現しています。

通常のアプリであれば、UIScreen = 今アプリが稼働している端末の画面という理解でいいのですが、問題はミラーリングするようなアプリの場合です。
その場合、UIScreenはメインとサブが存在することになります。

UIWindowとは

一方、UIWindowは、iOSアプリが表示したいもの(What)を表します。
ただUIWindowそのものはユーザーの目に見えるコンテンツを直接配置する場所ではなく、
具体的なコンテンツはUIViewに乗せて、そのUIViewを乗せる場所がUIWindowです。
いわば台紙ですね。

image.png

UIWindowの取得方法ですが、「UIApplication.shared.keyWindow?」や「view.window」のwindowが実際に入るのが、
ViewDidAppear後らしいので、ViewControllerのタイミングによっては、とれないみたいです。

UIViewControllerのview.windowがロードされるタイミング

となると、appDelegate経由になるんで、下記でとらなきゃですね。

UIApplication.shared.delegate?.window

余談ですが、これでbounds取得しようとすると、

let window = UIApplication.shared.delegate?.window //UIWindow??型が返る
print(window!?.bounds) //Optional((0.0, 0.0, 375.0, 812.0))

となって、ちょっとビックリしました。

ベストプラクティスは?

というわけで、マルチスクリーンでないのであれば、UIScreenでとろうが、UIWindowでとろうが、返ってくる値としては一緒なのですが、

iPadに対応したアプリケーションを作る場合、マルチタスク機能が存在し、ScreenサイズとWindowサイズが等しくない場合があるため、アプリの表示領域のサイズを知りたい場合はWindowサイズを取得すると良い。
アプリの表示領域のサイズを取得する

という注意点もあるみたいです。

まとめると、画面サイズを取得する際にどのクラス使うかの選択基準は、こんな感じですかね。

  • UIScreenを使う→マルチスクリーンやマルチタスクを考えなくてよい
  • UIWindow(not Application Delegate)を使う→ViewControllerのライフサイクルを考えなくてよい
  • UIWindow(Application Delegate)を使う→特に考慮点が思いつかない……あれ? もしかして万能?→いやごめんなさい。UIWindow??型なので扱いがめんどくさいですねこいつ

「今アプリが稼働している端末の画面サイズが正確に欲しい」という意味合いでは、原理主義的に考えたら、AppDelegateでとるのが筋なのかな〜と思うものの、
UIScreenでとるのは楽ですし、ググったらすぐ出てくるし、ほとんどのケースで正しい値がとれるので、
マルチスクリーン対応するようなことが想定されるアプリ(動画系とか)でなければ、UIScreenでやっちゃっていい気がします。

それぞれのクラスの違いを認識して、ケースバイケースで使っていくしかなさげです。

(おまけ)Application Delegateのwindowが2重オプショナル値返す件

この件、Swift書いていてもあんまり遭遇しないケースだったので、調べてみたら、StackOverflowで質問投げてる人がいました。

Why is main window of type double optional?

UIKitフレームワークの中のイマイチなトコなので、ユーザー的にはどうしようもないですが、

let window = app.delegate?.window??.`self`()

とすることで、UIWindow?型にできます。
これはちょっとあまりエレガントではないので、

let window = app.delegate?.window ?? nil // UIWindow?

こっちにした方がいいんじゃね? という指摘が飛んでいます。
いずれにせよちょっと扱いづらいですね。

(おまけ2)UIDevice

これを書いてしばらく経って急にそういえばUIDeviceってあったなと思い出しました。

https://developer.apple.com/documentation/uikit/uidevice

ただこのクラスは端末サイズはプロパティとして持ってませんでした。残念。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flutterウィークリー #80

Flutterウィークリーとは?

FlutterファンによるFlutterファンのためのニュースレター
https://flutterweekly.net/

この記事は#80の日本語訳です
https://mailchi.mp/flutterweekly/flutter-weekly-80

※Google翻訳を使って自動翻訳を行っています。翻訳に問題がある箇所を発見しましたら編集リクエストを送っていただければ幸いです。

読み物&チュートリアル

フレアとFlutter使用したソロノードのアニメーション化

https://medium.com/@mandylowry/animating-solo-nodes-with-flare-and-flutter-20e92c54cacd


She Who Codesによるこのチュートリアルで、単一のFlareアニメーションでさまざまなアクセサリーを切り替える方法を学びます。

WordPressバックエンドでFlutterアプリを構築する-パート1

https://medium.com/flutter-community/building-flutter-apps-with-wordpress-backend-part-1-e56414a4a79b


Ritesh Sharmaが、WordpressをFlutterアプリのバックエンドとして使用する方法を説明します。

Gitpod + Flutter =モバイルアプリ開発者向けの外出先での生産性

https://medium.com/@jacksonz666/gitpod-flutter-productivity-on-the-go-for-mobile-app-developers-cc2495049d52


GitpodとオンラインIDEを使用してFlutterプロジェクトを作成する方法。

FlutterプロジェクトをREST APIとしてホストする

https://medium.com/@rody.davis.jr/host-your-flutter-project-as-a-rest-api-751b8301f71f


Rody Davisは、クライアントFlutterアプリとDart APIサーバーでモデルとビジネスロジックを再利用する方法について説明しています。

FlutterネイティブのサードパーティSDKを統合する

https://medium.com/@jessanirahim/integrating-native-third-party-sdk-in-flutter-8aab03afa9da


FlutterアプリでネイティブのサードパーティSDKを使用する必要がありますか? Rahim Khalidが記事を書いてくれました。

最高のFlutterレスポンシブUIパターン

https://medium.com/flutter-community/the-best-flutter-responsive-ui-pattern-ba52875d70cd


Dane Mackierは、アプリをレスポンシブにするために必要なすべての情報を取得して使用する方法について説明します。

Flutterアプリのアニメーション化

https://www.smashingmagazine.com/2019/10/animation-apps-flutter/


Shubhamは、 Flutterアニメーションのこのすばらしい詳細な紹介を書きました。

ビデオ&メディア

Dart & Flutterタイプを使用した設計-合計タイプチュートリアル

https://www.youtube.com/watch?v=Pjx_Q5styqs


Dart Sum Typesを使用して、コードのエラーを起こしやすくする方法を学ぶ

Flutter + TensorFlow Lite |オブジェクト検出| YoloV2 | SSDチュートリアル

https://www.youtube.com/watch?v=0pYh7Js4GM8


TFLiteをTiny Yolov2およびSSDモデルで使用して、デバイス上のオブジェクト検出を実行する方法

Flutter Provider:はじめに

https://www.youtube.com/watch?v=O71rYKcxUgA


Flutter Providerパッケージを使用する方法を学びます。

フラッターのテキスト読み上げ(TTS)

https://www.youtube.com/watch?v=WnJZOi57oTY


Text To Speechについて、このビデオを使ってアプリに話しかけます

ライブラリ&コード

アントニオット15 / fgf

https://github.com/antoniott15/fgf

flutterでのGoogleフォントのインストール用CLI

bratan / flutter_translate

https://github.com/bratan/flutter_translate

Flutterの国際化(i18n)ライブラリ。

feilfeilundfeil / flutter_in_app_update

https://github.com/feilfeilundfeil/flutter_in_app_update

公式のAndroid APIを使用して、Androidのアプリ内アップデートを有効にします。

snowballdigital / flutter-unity-view-widget

https://github.com/snowballdigital/flutter-unity-view-widget

Flutter埋め込み可能な単一ゲームエンジンビュー

Vardiak /アニメーション

https://github.com/Vardiak/Animated

Flutter用の非常にシンプルな(まだ強力な)暗黙的なアニメーションライブラリ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xcode11のIBに追加されたContentLayoutGuideとFrameLayoutGuideについて

要約

  • Xcode11からIBでUIScrollViewを使うときはContent Layout GuideFrame Layout Guideを利用するといいかも
  • iOS10以下をサポートしたい時は今までとちょっとやり方が変わったから気をつけよう

IB + UIScrollViewは大変

例として、夏目漱石の「我輩は猫である」を表示するだけの画面を作ってみます。
ここではXcode10.2を使用しています。

01.png

この画面は、UIScrollViewContentView(UIView)、2つのUILabelで構成されています。
02.png

UIScrollViewをAutolayoutを使って組む場合、以下の制約が必要です。

  • UIScrollView自身の位置
  • UIScrollViewの中身のサイズ
  • UIScrollViewの中身の固定された横/縦幅

この制約を満たすために、以下の手順でConstraintを設定しています。

  1. UIScrollViewの上下左右をUIViewControllerViewに合わせる
  2. ContentViewの上下左右をUIScrollViewに合わせる
  3. ContentViewの横幅をUIViewControllerViewに合わせる(Equal Widths)
  4. ContentViewにオブジェクトを追加して高さを確定させる

慣れれば大したことはないのかもしれませんが、UIScrollViewが持つフレーム領域とコンテンツ領域が混ざって扱われているので、後からStoryboardをみた時どこがどうなってるんだっけ??と混乱すると思います。

ContentLayoutGuideとFrameLayoutGuideの登場

Xcode11から、ScrollViewの中にContent Layout GuideFrame Layout Guideが追加されました。
名前の通り、Content Layout GuideUIScrollViewの中身、Frame Layout GuideUIScrollViewのフレーム領域を示します。
以下の画面を見ると、中身とフレーム領域が明確に分けられていることがわかります。
03.png

これらはiOS11から追加された機能でしたが、IB上では扱えず、コード上でUIを組み立てる時しか恩恵を得られませんでした。
Xcode11ではScrollViewの中に追加されており、IBでUIScrollViewを扱う人でも使えるようになりました。

さて、先ほど作った画面をContent Layout GuideFrame Layout Guideを使うように直すとこうなります。
04.png

  1. UIScrollViewの上下左右をUIViewControllerViewに合わせる
  2. ContentView上下左右をContent Layout Guideに合わせる
  3. ContentView横幅をFrame Layout Guideに合わせる(Equal Widths)
  4. ContentViewにオブジェクトを追加して高さを確定させる

手順としては大きく変わっていないのですが、今までContentViewUIScrollViewUIViewControllerViewに合わせていたところを、それぞれのLayout Guideに合わせることで役割が明確になりました。

iOS10以下もサポートしたいんだけど。。。

残念ながら、contentLayoutGuideframeLayoutGuideはiOS11からの機能なので、iOS10以下で実行するとクラッシュします。
その場合は従来と同じ方法で実装する流れになりますが、Xcode11では上記の方法の

3 . ContentViewの横幅をUIViewControllerViewに合わせる(Equal Widths)

これをやろうとすると、そもそもEqual Widthsの項目がなくなっています(バグ?)

05.png

一時的な解決策として、Viewに対してではなくViewのSafeAreaに向けると項目が出てきますので、そっちを使っています。

参考

Scroll View Layouts With Interface Builder

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む