20201122のSwiftに関する記事は6件です。

電卓app 〜%計算〜

はじめに

 最近は便利なものでYoutubeで探せばアプリ開発を1〜10まで動画で説明してくれる動画が見つかります。

全くSwiftを触ったことがない状態で電卓アプリを1から作成したときに立ちはだかった壁についてご紹介します。

今回は壁の中でも%計算方法について書きたいと思います。

壁その1

 まず立ちはだかったのはどこを探しても%計算のコードが見当たらない!!!

私がやってほしかった計算は、1000円のものの30%引きはいくら?と計算してくれる%計算。

検索してもしてもこうやってね、が出てこずに途方に暮れていました。。。

コードはどこか探せばあって全てコピペするものだ、という勘違いをしていたこともいけませんでした。

%計算ってどうやるの??

1000円の商品が10%引きになってました。さて、いくらになるでしょう、が%の問題です。

1000ー(1000円の10%オフ)=支払い金額

ということは、1000円の10%オフという計算を覚えさせれば良いということだ!!と分かりました。

%計算のコード

かくかくしかじか、コードに当してみると、

       case "%":
                let huga:Double = Double(firstNumber) ?? 0
                let huga1:Double = Double(secondNumber) ?? 0
                let hoge:Double = 100
                secondNumber = String(huga * huga1 / hoge)

こんな感じに書いたら%の計算をしてくれる様になりました。

とりあえず今回は計算方法のみ、に焦点をしぼってみました。

最後に

プログラミングは理解すれば楽しいけど、理解するまでが時間かかりますね。。
精進あるのみだと再度確認できました!

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

no such module 'Firebase' 原因のかもしれないこと

no such module 'Firebase' 原因の一つの例

僕の場合、このエラーの原因はxcodeのbundleIdentifierを変えたのでFirebaseに登録したbundleIdentifierと異なりエラーが起こりました。

解決方法

  • bundleIdentifierを元のものに戻す
  • Firebaseを作り直す

bundleIdentifierを元に戻すのはわかると思うので
Firebaseの作り直しで気をつけるところをわかりやすく説明しようと思います。

demo

このIOSという丸いボタンを押します。
そうすると、

demo

このような画面になるので、新しくしたbundleIdentifierをここに入力してfirebaseを作ればエラーが解決されると思います!!

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

Develop in Swift Data CollectionsでiOS開発を学ぶ (2): Lesson 1.3からLesson1.4

https://qiita.com/mk2/items/6091f8eb195fa3237c4e の続き

Lesson 1.3: Model-View-Controller

iOS/Mac開発といえば、MVCだと個人的には思っています。(SwiftUIの登場で考え方も変わるのかもしれませんが…)

View ControllerModel Controllerはなんとなく知っていたのですが、Helper Controllerという考え方があるのは知りませんでした。ただ、この説明を見る限り、ユーティリティ的な感じがしますね。

“Helper Controllers
Helper controllers are useful anytime you want to consolidate related data or functionality so that it can be accessed by other objects in your app. One common example of a helper controller is a NetworkController, which manages all the network requests in a given app.”

抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970

また、下記のようにソースコード、リソースをグルーピングするのが良いというのはiOS開発をがっつりやったことがないので、へーという感じでした。

“Many developers make groups for the following:
- View controllers
- Views
- Models
- Model controllers
- Other controllers
- Protocols
- Extensions
- Resources
   - Storyboards
   - Frameworks

抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970

実験

今回は、好きなアスリートを記入できるアプリのようです。最終的に作成したコードを載せておきますね。

Athlete.swift
import Foundation

struct Athlete {
    var name: String
    var age: Int
    var league: String
    var team: String

    var description: String {
        return "\(name) is \(age) years old and plays for the \(team) in the \(league)."
    }
}
AthleteTableViewController.swift
import UIKit

class AthleteTableViewController: UITableViewController {

    struct PropertyKeys {
        static let athleteCell = "AthleteCell"
    }

    var athletes: [Athlete] = []

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

        tableView.reloadData()
    }

    // MARK: - Table view data source

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return athletes.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: PropertyKeys.athleteCell, for: indexPath)

        let athlete = athletes[indexPath.row]
        cell.textLabel?.text = athlete.name
        cell.detailTextLabel?.text = athlete.description

        return cell
    }

    @IBSegueAction func addAthlete(_ coder: NSCoder) -> AthleteFormViewController? {
        return AthleteFormViewController(coder: coder)
    }

    @IBSegueAction func editAthlete(_ coder: NSCoder, sender: Any?) -> AthleteFormViewController? {        let athleteToEdit: Athlete?
        if let cell = sender as? UITableViewCell,
           let indexPath = tableView.indexPath(for: cell) {
            athleteToEdit = athletes[indexPath.row]
        } else {
            athleteToEdit = nil
        }
        return AthleteFormViewController(coder: coder, athlete: athleteToEdit)
    }

    @IBAction func backToTable(_ segue: UIStoryboardSegue) {
        guard let controller = segue.source as? AthleteFormViewController,
              let athlete = controller.athlete else {
            return
        }

        if let selectedIndexPath = tableView.indexPathForSelectedRow {
            athletes[selectedIndexPath.row] = athlete
        } else {
            athletes.append(athlete)
        }
    }
}
AthleteFormViewController.swift
import UIKit

class AthleteFormViewController: UIViewController {

    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var ageTextField: UITextField!
    @IBOutlet weak var leagueTextField: UITextField!
    @IBOutlet weak var teamTextField: UITextField!

    var athlete: Athlete?

    required init?(coder: NSCoder) {
        self.athlete = nil
        super.init(coder: coder)
    }

    init?(coder: NSCoder, athlete: Athlete?) {
        self.athlete = athlete
        super.init(coder: coder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        updateView()
    }

    func updateView() {
        nameTextField.text = athlete?.name
        if let age = athlete?.age {
            ageTextField.text = "\(age)"
        }
        leagueTextField.text = athlete?.league
        teamTextField.text = athlete?.team
    }


    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

    @IBAction func save(_ sender: Any) {
        guard let name = nameTextField.text,
              let ageString = ageTextField.text,
              let age = Int(ageString),
              let league = leagueTextField.text,
              let team = teamTextField.text
        else {
            return
        }
        athlete = Athlete(name: name, age: age, league: league, team: team)
        performSegue(withIdentifier: "SaveAthlete", sender: self)
    }
}
わかりにくかった点

本の通りに進めていけば、多分だいたい完成させられると思うのですが、Step 6 Perform the Unwind Segue in Storyboardの節の下記の記述がよくわからずかなり四苦八苦しました。

“Finally, you need to create the unwind segue. In the storyboard, Control-drag from the athlete form scene in the Document Outline to the view controller's Exit, then choose your unwind segue. Give this segue a name by selecting it in the Document Outline and adding the identifier in the Attributes inspector.”

抜粋:: Apple Education “Develop in Swift Data Collections”。 Apple Inc. - Education、2020年 Apple Books https://books.apple.com/jp/book/develop-in-swift-data-collections/id1511183970

正解は、下のようにAthele Form View ControllerからcontrolドラッグでExitまで接続すれば良い感じでした。

Lesson1.5 Storyboard操作説明.001.jpeg

Lesson 1.4: Scroll Views

Scrolling Form

AutoLayoutの設定が難しいですね。自分は5、6回constrainsを全部消してやりなおしました。また、折角なのでiOS各端末で幅を綺麗に表示させたいと思ったのですが、そこが結構大変でした。下のように、Stack Viewの幅を親のScroll Viewのwidthと同じにすれば、各端末で同じにできるようです。

Lesson 1.4.001.png

実験

画像をズームできるようにするものでした。

ViewController.swift
import UIKit

class ViewController: UIViewController, UIScrollViewDelegate {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        scrollView.delegate = self
    }

    override func viewDidAppear(_ animated: Bool) {
        updateZoomFor(size: view.bounds.size)
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func updateZoomFor(size: CGSize) {
        let widthScale = size.width / imageView.bounds.width
        let heightScale = size.height / imageView.bounds.height
        let scale = min(widthScale, heightScale)
        scrollView.minimumZoomScale = scale
        scrollView.zoomScale = scale
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

構造体について【Swift】

構造体とは?

  • 変数、定数、関数を1つのグループとしてまとめる仕組み。
  • 構造体の中で作られた変数・定数を プロパティ 、関数を メソッド という。
  • 構造体の中で作られた変数・定数を初期化することを イニシャライズ という。
  • 構造体を利用するにはインスタンス化が必要。
  • データ型は全て構造体である。( IntFloatDoubleStringBool

実装例

struct Human {
    // プロパティを定義
    let name: String
    let age: Int

    // initを使用することでイニシャライザ定義
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func introduction() {
        print("私の名前は\(name)です。年齢は\(age)歳です。")
    }

}
// Human構造体をインスタンス化
let human = Human(name: "山田太郎", age: 25)
// introductionメソッドへアクセス
human.introduction() // 私の名前は山田太郎です。年齢は25歳です。

定義方法

struct 構造体名 {
    // 構造体の定義
}

プロパティとは?

インスタンスプロパティ

  • 型のインスタンスに紐付くプロパティ
  • 型のインスタンスに紐付くため、インスタンス化しなければ使うことができません。

スタティックプロパティ

  • 型自身に紐付くプロパティ
  • 型の性質を表すプロパティを定義したいとき(大規模なプロジェクト以外はあまり使う機会がない?)
  • 静的という意味なので let を推奨

インスタンスプロパティとスタティックプロパティの違い

struct Human {
    // スタティックプロパティ
    static let name = "山田太郎"
    // インスタンスプロパティ
    let age = 25 
}
// スタティックプロパティへアクセス
Human.name // OK
Human().name // NG

// インスタンスプロパティへアクセス
Human.age // NG
Human().age // OK
  • 型名().プロパティ名(型名に()をつけることでデフォルトのイニシャライザが働き、インスタンス化される)

イニシャライザとは?

  • プロパティを初期化する為のもの(他の言語ではコンストラクタとほぼ同様)
  • インスタンス化時に実行される特殊なメソッド

書式

init(引数名: 型名, ...) {
    // selfは構造体自身のインスタンスを意味する
    self.プロパティ名 = 引数を使ったりする
}

メンバーワイズイニシャライザ

  • デフォルトで用意されている特殊なイニシャライザ
  • イニシャライザは定義しない
  • 単純な初期化ならメンバーワイズイニシャライザが使えるが、イニシャライザを定義すると使えなくなる

インスタンス化の方法

イニシャライザを定義していない時

let 変数名 = 構造体名()

イニシャライザを定義した時

let 変数名 = 構造体名(引数名: 型名, ...)

メンバーワイズイニシャライザを使用する時

let 変数名 = 構造体名(プロパティ名: , ...) // プロパティを直接指定する

アクセス方法

let 変数名 = 構造体名()
変数名.プロパティ名
変数名.メソッド名

参考

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

Failed to register bundle identifierの解決法について

Failed to register bundle identifier が出たときのエラーの原因

エラーの原因は、別の誰かが今使っている bundleidentifierのIDを
使用しているため、この Bundle Identifier を使えなかったというところにあります。

どうして使われていると出るのか

  • 使用していたAppleIDを変更した
  • 誰かにプロジェクトを渡して、その人のmacでビルドして実機で実行してもらった

という可能性が高いです。

その場合、その別のAppleIDでBundle Identifierを使われてしまったため自分で使えなくなったということです。
ただ、このBundle Identifierの占有は1週間で期限が切れますので、その後はまた使えるようになります。

解決方法

  • 一週間待つ
  • bundle Identifierの後ろに日付をつけてbundle Identifierの重複を防ぐ

bundleIdentifierの後ろに日付を付け方の画像を貼っておきます。
demo

このようにすると重複がなくなり、エラーが解消されると思います!

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

Image+GeometryReaderを使ってAspectFill + clipToBoundsを再現する

https://medium.com/@wendyabrantes/swift-ui-square-image-modifier-3a4370ca561f
こちらの記事を参考に実装しました。

GeometryReader(content: { geometry in
    Image(uiImage: image)
        .resizable()
        .scaledToFill()
        .frame(width: geometry.size.width, height: geometry.size.height)
})
    .aspectRatio(1, contentMode: .fit)

上記の記事との違いはGeometryReader内のImageの属性に.frameでGeometryReaderのsizeを渡しています。Imageにframeを渡すことで、GeometryReaderの表示領域に対してscaledToFillのリサイズが当てられます。よってアスペクト比を固定しつつ中央寄せで任意のアスペクト比でクリップすることができるようになります。

この方法のメリットとして、子要素でサイズが決まるのではなくサイズは親要素側で提供された表示可能領域にあわせてリサイズするのでLazyVGridなどで使用する際などにサイズを固定化する必要がなくなります。

サンプルコード

gist

struct ProductGridCell: View {
    let name: String
    let price: Int
    let image: UIImage
    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            GeometryReader(content: { geometry in
                Image(uiImage: image)
                    .resizable()
                    .scaledToFill()
                    .frame(width: geometry.size.width, height: geometry.size.height)
            })
            .aspectRatio(1, contentMode: .fit)
            .clipShape(RoundedRectangle(cornerRadius: 4))
            Text(name)
                .foregroundColor(.primary)
                .font(.caption)
            Text(\(price)")
                .font(.caption2)
                .foregroundColor(.secondary)
        }
    }
}

struct ProductGridCell_Previews: PreviewProvider {
    static var previews: some View {
        ScrollView {
            LazyVGrid(columns: [GridItem(), GridItem(), GridItem()], content: {
                ForEach(0..<20) { _ in
                    ProductGridCell(name: "どら焼き", price: 300, image: UIImage(imageLiteralResourceName: "product"))
                }
            })
                .padding()
        }
    }
}

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