20200702のSwiftに関する記事は11件です。

Swift学習要点(随時更新予定)

文法編

  • 空白(スペース)は、=、+、-などの演算子の前後にはあってもなくてもOKである。但し、前後どちらか一方にしかないとエラーになる

  • letもvarも複数の宣言を「;」で区切って1行にまとめて書くことができる
    ex) let max_length = 10; var current_length = 5

  • 変数var で再代入する際には、「var 」やデータ型はいらない

  • 連結ではなく、文字列の中に変数(定数)の値を埋め込むこともできる。(変数)のように書くことで文字列内に変数を埋め込むことができます。これを変数の「展開」と呼ぶ。

  • Bool型の定数や変数の名前は、慣習上”is”から始めることが多い

Xcode編

  • IBはInterface Builderの略

  • させたい処理を書くのがIBActionである

  • 要素とプログラムを結びつける方法をIBOutletという。IBActionで記した動作とストーリーボード上に配置したパーツとを紐付けるものである。

  • Outlet: UI部品をプロパティとして接続する
    Action: UI部品をメソッドとして接続する

  • viewDidLoadの中に書いたコードは、一番最初に実行される。

  • label.textに文字列を代入すると実際のラベルの内容が変化する。

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

Swiftで今「ニューモーフィズム」のアプリを作るなら

はじめに
こちらの記事は #Qiita夏祭り2020_パソナテック への応募記事になります。
LGTM上位の記事に豪華なプレゼントがもらえるそうなので、ぜひこの記事を読んでLooks Goodだと思っていただけたらLGTMお願いします!

ニューモーフィズムとは

ニューモフィズム

ニューモーフィズム(Neumorphism)とは背景から要素が押し出されていたり、凹んでいるように見えるスタイルのデザインのことを言います。
2020 年初頭から流行り始めているそうです。

カッコいいですね?
一目惚れした僕はニューモーフィズムで何かアプリを作ることにしました。 DDD(デザイン駆動開発)です(そんな言葉はありません)。

題材はAI寝言録音アプリです。
睡眠中の音声に音声認識をかけることで寝言だけを録音しちゃおうというアプリです。
自分は寝言がとても面白いらしく、自分でもぜひ聞いてみたくなり、世に出回っている寝言録音アプリを使ってみました。しかし寝返りの音といびきの音ばかりが録音されていて寝言を探すのが大変だったので、音声認識を噛ませることで寝言だけを録音できるのではないかと思いました。

ニューモーフィズムを作る

ニューモーフィズムのデザインの方法を学ぶために、まずはKeynote(Mac純正のパワポ)を使ってアプリアイコンを作ってみたいと思います。

1. ベースカラーを決める

ベースカラーは#C4D2E9にします。
背景色をベースカラーで染めます。

アイコン作成イメージ.001.jpeg

2. 2つの影のカラーを決める

ベースカラーの輝度を変更して「明るい影」と「暗い影」を決めます。
こちらのオンラインツールを使ってベースカラーの明るい影と暗い影のカラーコードを作成しました。

明るい影: #ECF0F7
暗い影: #0E1624

3. オブジェクトを置く

押し出したい(もしくは凹ませたい)オブジェクトを置きます。
今回は寝言っぽく吹き出しにしてみました。

アイコン作成イメージ.002.jpeg

4. 右下に暗い影を落としたオブジェクトを作ります

枠線は消して、右下に先ほど決めた暗い影(#0E1624)を落とします
アイコン作成イメージ.003.jpeg

5. 左上に明るい影を落としたオブジェクトを作ります

同様に、枠線を消して左上に先ほど決めた明るい影(#0E1624)を落とします

アイコン作成イメージ.004.jpeg

6. 二つのオブジェクトを重ねます

5、6で作った二つのオブジェクトをぴったりと重ねます。
すると、オオ・・・浮かび上がった・・・

アイコン作成イメージ.005.jpeg

7. 最後に仕上げをしてアイコン完成

ZZZと文字を上に置いただけですが、これでアイコン完成です。

アイコン作成イメージ.006.jpeg

Swiftでニューモーフィズムのアプリを作る

さてアイコンもできたので、みなさんお待ちかね、Swiftでニューモーフィズムを実装していきましょう!!

早速僕は「Swift ニューモーフィズム github」で検索しました。
そして見つけました!!偉大な先人のライブラリ!!
https://github.com/hirokimu/EMTNeumorphicView

ニューモーフィズムなUIViewを提供してくれる最高のライブラリです。
え?フルスクラッチで実装しないのかって?しません。

AIの寝言録音機能を実装する

swiftにはSiriと同じ音声認識機能を使えるSFSpeechRecognizerがあります。
こちらを使って音声認識を行います。
いびきや物音に音声認識を当てるとエラーになるため、これを利用して寝言だけを識別することにします。

録画画面

EMTNeumorphicViewを使ってボタンを配置していきます。
凹んだボタンが可愛いですね。

6.5インチ1.png

再生画面

録音した音声に音声認識をかけて寝言らしき音だけを音声認識結果とともに表示します。
リストにしてもうるさくありません。

6.5インチ2.png

ソースコード公開します

技術的に面白いことはやっていないので今回はソースコードを公開するだけに留めます・・・
コードレビューしてくれる人歓迎!?‍♂️
https://github.com/KitaharaMugiro/NegotoRecorder

「AI寝言レコーダー」 AppStoreで配信中!

ついでにAppStoreにリリースもしました。250円です(無料なものなどない)。
バージョンアップも予定しています。
https://apps.apple.com/jp/app/id1521147935

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

アラート表示

アラートを表示する

swift
 SimpleAlert.showAlert(viewController: self, title: "タイトル", message: "内容を描いてください", buttonTitle: "OK")

アラート表示

swift
import UIKit
struct SimpleAlert {
    static func showAlert(viewController: UIViewController, title: String, message: String, buttonTitle: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: buttonTitle, style: .default) { (action) in
            alert.dismiss(animated: true, completion: nil)
        }
        alert.addAction(okAction)
        viewController.present(alert, animated: true, completion: nil)
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NavigationControllerからモーダル遷移をする時に出たエラーCould not cast value of type..

遭遇したエラー

Could not cast value of type..

状況

NavigationControllerにTableViewCellを配置し(LINEのイメージ)基本的な遷移(以下①)はTableViewCellを押すことで行っている。
ある時のみ(具体的には開発者によるアカウント削除)にはモーダル遷移(以下②)を行う。

原因と対処

segue.identifierによる条件分岐ができておらず、②の遷移の時に①で行うときの値遷移の処理がおこなわれ、クラッシュしていた。

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "cellSegue" {

        let indexPath = self.roomTableView.indexPathForSelectedRow
        let NextVC = segue.destination as! ChatViewController
        NextVC.roomName = self.joinedRoomName[indexPath!.section]!
        NextVC.roomPassword = self.joinedRoomPassword[indexPath!.section]!
        NextVC.password = "\(joinedRoomName[indexPath!.section]!)\(joinedRoomPassword[indexPath!.section]!)"
        } 

    }

のようにif segue.identifier == ""を設定することでクラッシュを防ぐことができた。

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

[Swift]30踏んだら負けのゲームを、iOSアプリ開発で作ってみた。

こんにちはMottyです。今回は話題のSwiftを使った簡単なアプリ制作です。

 2020-07-02 16.02.44.png

概要

SwiftはiOSアプリ開発用のプログラミング言語です。
以下のようなメリットが一般的に挙げられます。
・Webフレームワークが充実している
・処理速度が速い
・Apple製品のアプリを一貫して開発できる
・コーディングがシンプル
従来まではObjective-Cという言語でのアプリ開発が主流だったそうですが、
速さやコーディングのシンプルさなど性能面でSwiftが上回り、現在市場に
浸透しつつある新しい言語、といった特徴があります。

 2020-07-02 16.07.52.png

(データは少し古いですが・・・可視化しても高い成長率が特徴的ですね。)

30言ったら負けのゲーム

こちらに関しては説明は不要でしょうか(?)。
交互に数字を宣言し、先に30を宣言してしまった方の負けのゲームです。
・宣言は交互に行う。初めの数字は1
・プレイヤーが1度に宣言できる数は3つまで。
・連続した整数でつないでいかなければならない。つまり先行が1,2,3と言い終えたあと、
相手は4から始めていく。

ソースコード


 2020-07-02 16.17.05.png

30.swift
//
//  ViewController.swift
//  30
//
//  Created by Motty on 2020/07/01.
//  Copyright © 2020 Motty. All rights reserved.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        //初期化の際に1回だけ実行されるメソッド

    Counter.text = "0"
    Turn_Board.text = Turn1
    }

    //Propaties

    var CountNumber = 0 //0<CN<30
    var Turn1 = "Player1のターンです"
    var Turn2 = "Player2のターンです"
    var IsPlayer1 = true //If False : Now is Player2
    var AddCount1 = 0 //0<AC1<3
    var AddCount2 = 0 //0<AC2<3


    //Methods

    //プレイヤーのターンをスイッチする
    func player_switch() {
        if IsPlayer1 == true{
            IsPlayer1 = false
            Turn_Board.text = Turn2
        }else{
            IsPlayer1 = true
            Turn_Board.text = Turn1
        }
    }

    // 勝利判定のメソッド
    func WinnerJudge() {
        if CountNumber >= 30 {
            if IsPlayer1 == true{
                Turn_Board.text = "Player1の敗北です!"

            } else {
                Turn_Board.text = "Player2の敗北です”!"
            }
        }
    }

    //アドカウント判定のメソッド
    func AddCountJudge(){
        if IsPlayer1 == true{
            if AddCount1 >= 3{
                AddCount1 = 0
                player_switch()
            }
        }else{
            if AddCount2 >= 3{
                AddCount2 = 0
                player_switch()
            }
        }

    }

    // リセット
    func Reset0(){
        AddCount1 = 0
        AddCount2 = 0
        CountNumber = 0
        IsPlayer1 = true
        Counter.text = "0"
        Turn_Board.text = Turn1

    }


    //Arranging UIParts

    //Outlets
    @IBOutlet weak var Counter: UILabel!
    @IBOutlet weak var Turn_Board: UILabel!


    //Actions
    @IBAction func Add1(_ sender: Any) {
        CountNumber += 1
        AddCount1 += 1
        Counter.text = String(CountNumber)
        WinnerJudge()
        AddCountJudge()

    }
    @IBAction func Add2(_ sender: Any) {
        CountNumber += 1
        AddCount2 += 1
        Counter.text = String(CountNumber)
        WinnerJudge()
        AddCountJudge()
    }
    @IBAction func End1(_ sender: Any) {
        player_switch()
        Turn_Board.text = Turn2
    }
    @IBAction func End2(_ sender: Any) {
        player_switch()
        Turn_Board.text = Turn1
    }
    @IBAction func Reset(_ sender: Any) {
        Reset0()
    }    

}

<表示画面>

 2020-07-01 23.58.06.png

動きました。一応機能は動きますが
レイアウトやバグ等はガバガバです。処女作ということでご勘弁を・・・。
しかし、UIパーツは便利ですね・・・GUI操作でアプリを作れるので
プログラミングの担う工程は全体の半分ー6割程度になります。

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

ラジオボタン(選択肢)を作る(xcode)

備忘録として。参考になったとしたら幸いです。

できるもの

どれか一つだけを選択させたい時。他の選択肢をタップしたらそれまで選択されていたもののチェックが外れるようなものを作りたいなんかに。

IMG_0388.png

使う知識

isSelected

UIButtonにはisSelectedというBool型のプロパティがあります。(推されているかいないかの状態を表す。)
ボタンを押す時にそのisSelectedを反対にするには@IBActionの中に
radio1Button.isSelected = !radio1Button.isSelectedとすることで状態を変えることができます。

そして、その時他のボタンが選択されているつまりisSelectedがtrueならばfalseにすれば良いので

if radio2Button.isSelected {
            radio2Button.isSelected = !radio2Button.isSelected
        }

のようにします。
radio2Button.isSelected = falseでもOK
これを選択肢の数だけ行うことでラジオボタンを実装することができます。

setImage

UIButtonのsetImageメソッドにはforという引数名があります。
radio1Button.setImage(uncheckedImage, for: .normal)
これは上述したisSelectedと密接に関わっていて、normalが選択されていない時、selectedが選択された時の状態を表します。
なのでsetImageの際にそれぞれの状態に応じた画像を決めることで、視覚的に選択されているのかどうかを表現することができます。
ex)normalにはハート、.selectedには塗りつぶされたハートを設定するなど

radio1Button.setImage(uncheckedImage, for: .normal)
radio2Button.setImage(uncheckedImage, for: .normal

機能そのものは

ソースコード

override func viewDidLoad() {

        super.viewDidLoad()
radio1Button.setImage(uncheckedImage, for: .normal)
radio2Button.setImage(uncheckedImage, for: .normal


}
@IBAction func radio1Button(_ sender: Any) {
        radio1Button.isSelected = !radio1Button.isSelected
        reportString = "スパム/宣伝目的"
        if radio2Button.isSelected {
            radio2Button.isSelected = !radio2Button.isSelected
        }
        if radio3Button.isSelected {
            radio3Button.isSelected = !radio3Button.isSelected
        }
        if radio4Button.isSelected {
            radio4Button.isSelected = !radio4Button.isSelected
        }
        if radio5Button.isSelected {
            radio5Button.isSelected = !radio5Button.isSelected
        }
        if radio1Button.isSelected || radio2Button.isSelected || radio3Button.isSelected || radio4Button.isSelected || radio5Button.isSelected {
        submitButton.isEnabled = true
            submitButton.setTitleColor(UIColor.black, for: .normal)
        } else {
            submitButton.isEnabled = false
            submitButton.setTitleColor(UIColor.gray, for: .normal)
        }
    }

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

textFieldShouldReturn

リターンキーを押した時にキーボードを閉じる

swift
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()

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

NavigationController下においての画面遷移

1つ前の画面に戻る

swift
    @IBAction func calendarButton(_ sender: Any) {
        self.navigationController?.popViewController(animated: true)

    }

階層のトップ画面に戻る

swift
    @IBAction func walkButton(_ sender: Any) {
        self.navigationController?.popToRootViewController(animated: true)
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メルカリ・フリルなどのフリマアプリホーム画面の3カラムデザインの作り方

はじめに

メルカリ/フリルなどのフリマアプリでよく目にするホーム画面の3カラムのデザインを作成する方法を紹介します。
今回はUICollectionViewControllerをSwiftUI上で動かしています。(これでUIKitにホットリロードのCanvasを導入する事が出来ます)

*ストーリーボードは使用していないので、Extension.swiftファイルに制約のルールを設定しています。(ステップ3をご覧ください)

開発環境

Swift 5.2.4
Xcode 11.5(Deployment Target 13.0)
ストーリーボードなし

ステップ1

SceneDelegate.swift内に初期ページ設定をします。初期ページはFrimaContentViewとします

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let frimaContentView = FrimaContentView()
        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: frimaContentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

ステップ2: SwiftUIからUICollectionViewControllerを表示する

以下を実装するとUIKitでもホットリロード機能(canvas)が使えるため一々Runしなくてもいいので便利です。

import UIKit
import SwiftUI

class FrimaController: UICollectionViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //背景を白にする
        collectionView.backgroundColor = .white

    }
}

struct FrimaIntegratedViewController: UIViewControllerRepresentable {

    func makeUIViewController(context: UIViewControllerRepresentableContext<FrimaIntegratedViewController>) -> FrimaController {
        return FrimaController(collectionViewLayout: UICollectionViewFlowLayout())
    }

    func updateUIViewController(_ uiViewController: FrimaController, context: Context) {

    }
}

struct FrimaContentView: View {
    var body: some View {
        FrimaIntegratedViewController().edgesIgnoringSafeArea(.all)
    }
}

struct FrimaContentView_Previews: PreviewProvider {
    static var previews: some View {
        FrimaContentView()
    }
}

ステップ3: 個々のアイテムセルを作成

セル上にアイテムの名前と値段、いいねボタンを作成します。

class FrimaItemCell: UICollectionViewCell {

    let priceLabel: UILabel = {
        let label = UILabel()
        label.text = "¥1,000"
        label.textColor = .white
        return label
    }()

    let itemNameLabel: UILabel = {
        let label = UILabel()
        label.text = "Tシャツ"
        label.textColor = .white
        return label
    }()

    let likeButton: UIButton = {
        let button = UIButton(type: .system)
        //Xcodeにデフォルトである画像を使用 *これはDeplotment Targetが13.0以降で使えます
        button.setImage(UIImage(systemName: "heart")?.withRenderingMode(.alwaysOriginal), for: .normal)
        return button
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        backgroundColor = .systemRed

        setupView()
    }

    fileprivate func setupView() {
        addSubview(priceLabel)
        priceLabel.anchor(top: nil, left: leftAnchor, bottom: bottomAnchor, right: nil, paddingTop: 0, paddingLeft: 5, paddingBottom: 5, paddingRight: 0, height: 0, width: 0)

        addSubview(itemNameLabel)
        itemNameLabel.anchor(top: nil, left: priceLabel.leftAnchor, bottom: priceLabel.topAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 5, paddingRight: 0, height: 0, width: 0)

        addSubview(likeButton)
        likeButton.anchor(top: nil, left: nil, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 5, paddingRight: 5, height: 0, width: 0)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

制約ルールは別ファイルExtensions.swiftで設定しています。

//Extensions.swift
import Foundation
import UIKit

extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?,  bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddingLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, height: CGFloat, width: CGFloat){

    translatesAutoresizingMaskIntoConstraints = false

    if let top = top {
        self.topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
    }

    if let left = left {
        self.leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
    }

    if let bottom = bottom {
        bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
    }

    if let right = right {
        rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
    }

    if width != 0 {
        widthAnchor.constraint(equalToConstant: width).isActive = true
    }

    if height != 0 {
        heightAnchor.constraint(equalToConstant: height).isActive = true
    }
    }
}


ステップ4: レイアウトを作成

ステップ3で作成したCellをFrimaController上に登録し、レイアウトを作成します。

class FrimaController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    let frimaItemCellId = "frimaItemCellId"

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.backgroundColor = .white

        //ステップ3で作成したFrimaCellを登録
        collectionView.register(FrimaItemCell.self, forCellWithReuseIdentifier: frimaItemCellId)

    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: frimaItemCellId, for: indexPath) as! FrimaItemCell
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.width / 3 - 4, height: view.frame.width / 3)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 4
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

}

完成

これで完成。
スクリーンショット 0002-07-01 午後5.34.15.png

最後に

今は全てのアイテムが"Tシャツ"、と¥1,000そして画像がありませんが、
次回はアイテムクラスを作り個々のアイテムを設定していきます。

最後に一句(初心者時代にやっていたNG行動)
テラテイル 聞きすぎ怒られ 気がメイル

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

Swift UITextField

今回はTextFieldの使い方です

TextFieldは選択すると文字を入力できるものですね

今回もUIの接続をしたところから解説していきます

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func button(_ sender: Any) {
    }   
}

接続後のコードです

まずはtextFieldに入力した文字をbuttonを押した時にlabelに表示するようにしていきましょう

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func button(_ sender: Any) {
        label.text = textField.text
    }   
}

これでlabelに文字を表示できます
しかし、このままではキーボードが表示されたままになってしまいます
なので、キーボードのreturnを押した時にキーボードを閉じるようにします

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.delegate = self   //追記
    }

    @IBAction func button(_ sender: Any) {
        label.text = textField.text
    }   
}

//以下を追記
extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
    }
}

textField.delegate = selfでdelegateを設定してあげます
delegateについてはいづれ記事を書きます
delegateを設定するとUITextFieldDelegateを継承して!と言われるのでfixを押してもいいですし、extensionで拡張してから書いてもいいです
あとは決まり文句みたいなものだと思って書いてください

雑な解説になってしまいすみません
今回はこれで終わります

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

iOS 14 の Widgets 全サイズを一度に Preview する【コピペで使える】

本記事は Qrunch とのクロス投稿です。
iOS 14 の Widgets 全サイズを一度に Preview する【コピペで使える】 | Qrunch(クランチ)


iOS & iPadOS 14 および macOS Big Sur から WidgetKit が導入され、Widget を作るためには SwiftUI で書くこととなっています。ここでは iOS 14 での Widget のお話をします。

Widgets の iOS Human Interface Guidelines を見ると "Adapting to Different Screen Sizes" セクションに1つの表があります。そこには各 iPhone のスクリーンサイズと、それぞれに対応する "Small"、"Medium"、"Large" Widget のサイズが記載されています。

せっかく SwiftUI で書くので、Xcode Previews で全てのサイズを表示させながらレイアウトするのはいかがでしょうか。このように一度に表示されると何かと便利です。

スクリーンショット 2020-07-02 3.19.00.png
※スクリーンショットは Xcode 11.5 のものです。

以下、3サイズの Widget のサイズを記載済みの PreviewProvider です。コピペしてお使いください。

Small Widget

struct SmallWidgetContentView_Previews: PreviewProvider {
    static var contentView: some View {
        ContentView()
            .preferredColorScheme(.dark)
    }

    static var previews: some View {
        Group {
            // 320pt × 568pt (iPhone SE 第1世代)
            contentView
                .previewLayout(.fixed(width: 141.0, height: 141.0))

            // 375pt × 667pt (iPhone 6/6s/7/7s/8/SE 第2世代)
            contentView
                .previewLayout(.fixed(width: 148.0, height: 148.0))

            // 414pt × 736pt (iPhone 6 Plus/6s Plus/7 Plus/8 Plus)
            contentView
                .previewLayout(.fixed(width: 159.0, height: 159.0))

            // 375pt × 812pt (iPhone X/XS/11 Pro)
            contentView
                .previewLayout(.fixed(width: 155.0, height: 155.0))

            // 414pt × 896pt (iPhone XR/XS Max/11/11 Pro Max)
            contentView
                .previewLayout(.fixed(width: 169.0, height: 169.0))
        }
    }
}

Medium Widget

struct MediumWidgetContentView_Previews: PreviewProvider {
    static var contentView: some View {
        ContentView()
            .preferredColorScheme(.dark)
    }

    static var previews: some View {
        Group {
            // 320pt × 568pt (iPhone SE 第1世代)
            contentView
                .previewLayout(.fixed(width: 291.0, height: 141.0))

            // 375pt × 667pt (iPhone 6/6s/7/7s/8/SE 第2世代)
            contentView
                .previewLayout(.fixed(width: 322.0, height: 148.0))

            // 414pt × 736pt (iPhone 6 Plus/6s Plus/7 Plus/8 Plus)
            contentView
                .previewLayout(.fixed(width: 348.0, height: 159.0))

            // 375pt × 812pt (iPhone X/XS/11 Pro)
            contentView
                .previewLayout(.fixed(width: 329.0, height: 155.0))

            // 414pt × 896pt (iPhone XR/XS Max/11/11 Pro Max)
            contentView
                .previewLayout(.fixed(width: 360.0, height: 169.0))
        }
    }
}

Large Widget

struct LargeWidgetContentView_Previews: PreviewProvider {
    static var contentView: some View {
        ContentView()
            .preferredColorScheme(.dark)
    }

    static var previews: some View {
        Group {
            // 320pt × 568pt (iPhone SE 第1世代)
            contentView
                .previewLayout(.fixed(width: 291.0, height: 299.0))

            // 375pt × 667pt (iPhone 6/6s/7/7s/8/SE 第2世代)
            contentView
                .previewLayout(.fixed(width: 322.0, height: 324.0))

            // 414pt × 736pt (iPhone 6 Plus/6s Plus/7 Plus/8 Plus)
            contentView
                .previewLayout(.fixed(width: 348.0, height: 357.0))

            // 375pt × 812pt (iPhone X/XS/11 Pro)
            contentView
                .previewLayout(.fixed(width: 329.0, height: 345.0))

            // 414pt × 896pt (iPhone XR/XS Max/11/11 Pro Max)
            contentView
                .previewLayout(.fixed(width: 360.0, height: 376.0))
        }
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む