20200601のSwiftに関する記事は19件です。

components(separatedBy: CharacterSet.decimalDigits.inverted)って何? と思ったら文字列分解についてよく理解していなかった話

※本記事のコードにおいて、import Foundation は省略しています。

はじめに

components(separatedBy: CharacterSet.decimalDigits.inverted) を読んでも処理結果がよく分からず記事を書こうとしたのですが、記事を書いてる途中でメソッド自身ではなく、文字列分解がよく理解していないことが分かりました。
本記事では文字列分解について、具体的には区切り文字が連続する際の文字列分解の処理について記載しました。
componentsの使い方も共に追っていきます。

componentsとは

文字列処理のメソッド
components(separatedBy:) - NSString | Apple Developer Documentation

components(separatedBy: "文字列")

let str = "ac7wa20tle7mle"
let result = str.components(separatedBy: "7")
// ["ac", "wa20tle", "mle"]

区切り文字:任意の文字列

components(separatedBy: CharacterSet.decimalDigits)

let str = "ac7wa20tle7mle"
let result = str.components(separatedBy: CharacterSet.decimalDigits)
// ["ac", "wa", "", "tle", "mle"]

区切り文字:10進数字の文字列
decimalDigits - NSCharacterSet | Apple Developer Documentation

components(separatedBy: CharacterSet.decimalDigits.inverted)

let str = "ac7wa20tle7mle"
let result = str.components(separatedBy: CharacterSet.decimalDigits.inverted)
// ["", "", "7", "", "20", "", "", "7", "", "", ""]

本題。
inverted()から反転が分かる、つまり10進数字外の文字を区切り文字としています。

よく分からないので短い文字列で見てみます。

let str = "ac7"
let result = str.components(separatedBy: CharacterSet.decimalDigits.inverted)
// ["", "", "7"]

?(空文字どこから来たのだろう?)

前から見ていきます。

  1. 初期状態
    "ac7"

  2. "a"の前後の領域を区切る。
    "", "a", "c7"

  3. "a"を除く
    "", "c7"

  4. "c"の前後の領域を区切る。
    "", "", "c", "7"

  5. "c"を除く (最終形)
    "", "", "7"

別の例で考えてみる。

"7/7" sep="/"

let str = "7/7"
let result = str.components(separatedBy: "/")
// ["7", "7"]

分かる。

"7//7" sep="/"

let str = "7//7"
let result = str.components(separatedBy: "/")
// ["7", "", "7"]
  1. 初期状態
    "7//7"

  2. "7"は区切り文字ではないのでパス
    "7//7"

  3. "/"の前後の領域を区切る
    "7", "/", "/7"

  4. "/"を除く
    "7", "/7"

  5. "/"の前後の領域を区切る
    `"7", "", "/", "7"

  6. "/"を除く (最終形)
    `"7", "", "7"

つまり、連続する区切り文字がある場合、{連続する個数-1}個分の空文字が生成されることになります。
…とは必ずしも言い切れません。次の例で考えてみます。

"/7/7" sep="/"

let str = "/7/7"
let result = str.components(separatedBy: "/")
// ["", "7", "7"]
  1. 初期状態
    "/7/7"

  2. "/"の前後の領域を区切る
    "", "/", "7/7"

  3. "/"を除く
    "", "7/7"

  4. (WIP) と同じ。 (最終形)
    "", "7", "7"

つまり、先頭と末尾には "" が暗黙的に存在しているので下記のように言い表すことが出来ます。

区切り文字が連続しているときに生成される空行の個数は、

  • 連続する区切り文字が先頭、末尾に含まれない時: {連続する個数-1}個
  • 含まれる時: {連続する個数}個

となります。

おわりに

冒頭にもありますが、文字列分解についての記事になりました。
区切り文字が連続するケースについて考えたことがありませんでした。

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

【xcode】swift5 BGMを流したまま効果音を鳴らす

やりたいこと

BGMを流したまま効果音を再生する

ソースコード

音声のファイル名:bgm.mp3と button.mp3

import UIKit
import AVFoundation

class ViewController: UIViewController {
    var player0:AVAudioPlayer?
    var player1:AVAudioPlayer?

   override func viewDidLoad() {
       super.viewDidLoad()

    // 再生する音声ファイルを指定する
       let url = Bundle.main.bundleURL.appendingPathComponent("bgm.mp3")
       do {
           try player0 = AVAudioPlayer(contentsOf: url)
       } catch {
           print("Error")
       }
       player0!.play()
       //音声をループさせる
       player0!.numberOfLoops = -1}

//ボタンを押したら効果音がなるように設定
@IBAction func playSound(sender: AnyObject) {
         // 再生する音声ファイルを指定する
        let soundURL = Bundle.main.url(forResource: "button", withExtension: "mp3")
         do {
        player1 = try AVAudioPlayer(contentsOf: soundURL!)
             player1?.play()
         } catch {
             print("error...")
         }
     }
}

まとめ

音を同時に再生しようとすると片方の音が止まってしまう問題に約1時間もハマってしまいました。
色々調べた結果【作るAVAudioPlayerのインスタンスの数だけ音楽再生関数を作る必要がある】とのことで上記のようなコードに至りました。
意外とシンプルだった。出来た時めっちゃ嬉しかったです。笑

初心者なので上記のコードがベストなのかはわからないのですがとりあえず動いたから良いと言うことで。笑
より良い同時再生方法があればご教授いただけると嬉しいです。

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

iOSアプリ ノッチ判定

はじめに

アプリ完成間際、ノッチの存在に気付き実装しました。
忘れがちなので、気をつけたいです。

やりたいこと

iPhoneX以降、ノッチが出現したことにより、画面上部ぎりぎりに配置すると、
ノッチと被ってしまい画面上に表示されなくなってしまいます。
なので、ノッチがあるかないかを判定して、

ノッチがある場合 → ノッチ分(44pixel)ずらす
ノッチがない場合 → 画面上部ギリギリに配置

という方法をとることにしました。

実装

view.swift
class view: UIViewController {
  let text = UILabel()

  override func viewDidLoad() {

     //safeAreaの値を取得します。
     let safeAreaInsets = UIApplication.shared.keyWindow?.safeAreaInsets.left

     super.viewDidLoad()

     //safeAreaの値が44.0以上であれば、ノッチがあるので、x位置をずらします。
     if(safeAreaInsets! >= 44.0){
         text.frame =  CGRect(x:50,y:width/2, width:self.view.frame.height,height: 50)
     }
     //ノッチがない場合の処理
     else{
         text.frame =  CGRect(x:10,y:width/2, width:self.view.frame.height,height: 50)
     }
 }

ノッチの値が44なので、今回は50ずらしました。

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

【Swift】WordPressにAPIリクエストをしてブログ情報を取得する

APIリクエストの飛ばし方がやっとわかった

Swiftには便利なライブラリAlamofireというものがあり、それを使用してHTTP接続をするのがセオリーですが、いかんせん最近はAlamofireの書き方が新しくなり、ネットでも調べても全然よくわかりませんでした。
それがやっと解消できたのでここに記します。

WprdPressが提供してくれてるAPI

情報が欲しいブログのURLの後ろに「/wp-json/wp/v2/posts?_embed」をつけてあげて、そのURLにリクエストを飛ばします。
私のブログはkimotii.comなので、

kimotii.com/wp-json/wp/v2/posts?_embed

となります。

ライブラリを入れる

CocoaPodsを使って便利なライブラリを入れましょう。
今回使用するのは
・Alamofire
・SwiftyJson
の2つです。
手順は、

1、ターミナルで以下を打つ

sudo gem install cocoapods

インストールが終わったら

pod setup

2、Xcode開いて今回作りたいアプリのプロジェクトを作る
ホームに作ると楽

3、ターミナルで

cd プロジェクトフォルダの名前

フォルダに移動できたら

pod init

これでpodファイルが生まれる

4、podファイルを開いて、

pod 'Alamofire'
pod 'SwiftyJson'

これらを記入したら

pod install

これでライブラリがインストールされました。

書いてみよう

以下が今回書いたものです。記事のタイトルを全て出してみました。

import UIKit
import Alamofire
import SwiftyJSON

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // APIリクエストの関数を呼び出す
        getArticles()
    }

    func getArticles() {
        // Alamofireを使う
        // URLでリクエストを飛ばし、レスポンスがresponseに入る
        AF.request("https://kimotii.com/wp-json/wp/v2/posts?_embed").responseJSON { response in
        // 確認のためresponseを見てみる
        print("Response JSON: \(response.value)")

        // responseがnilじゃなかったら変数jsonObjectにレスポンスを入れる
        if let jsonObject = response.value {

                // 変数jsonにJSON形式にしたjsonObjectを入れる
                let json = JSON(jsonObject)

                // 配列の数だけ処理を繰り返す
                for i in 0..<json.count {
                    let array = json[i]
                    // "taitle"の"rendered"を指定してあげると記事タイトルが取れる
                    let title = array["title"]["rendered"].stringValue

                    // 取得できたタイトルを表示
                    print(title)
                }
            }
        }
    }
}

タイトル以外にも色んな情報がとれるのでおためしあれ!

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

【Swift5�】 特定画面で縦画面/横画面に固定させる

はじめに

初投稿です。
ついにやってみたかったiPhoneのアプリを完成できました!
その備忘録です〜

やりたかったこと

ゲーム設定画面は縦、ゲーム画面自体は横画面としたかったので、
画面の向きを判定して、縦だったら横に、横だったら縦に向きを固定する。
※便宜上、
ホームボタン左 = 左横画面
ホームボタン右 = 右横画面 とします。

やってみたこと

supportedInterfaceOrientationsの単純な画面の向きを指定だけだと、
自動回転しちゃうのでそれも制御しないとだめなんですよね。

view.swift
     // 画面の向きを指定
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            return .portrait
   }

    // 画面を回転させるかどうか
    override var shouldAutorotate: Bool {
             return true
   }

単純にこれでいけそう!とか思ってたんですけど、
どうも処理の順番が
shouldAutorotate(自動回転) → supportedInterfaceOrientations(向き指定)らしく
自動回転OFF   → 縦画面に変更
になって、回転できないんですよね〜とほほ

なので、代案として現在の画面向きを取得して、以下の通りの処理を考えました。

画面 現在の画面向き 実装
縦の場合 自動回転OFF
横の場合 画面向きを縦にして自動回転をOFF
縦の場合 画面向きを横にして自動回転をOFF
横の場合 自動回転させる

実装コード

縦画面

TateViewController.swift
class TateViewController: UIViewController{
    // 自動回転はOFFで縦に固定
    override var shouldAutorotate: Bool {
        //縦画面なので縦に固定
        UIDevice.current.setValue(1, forKey: "orientation")
        return false

   }
    override func viewDidLoad() {
     <<Code>>
   }
}

画面を縦に固定なら自動回転ON/OFF決めるときに
縦にして自動回転OFFにすればいいじゃん!と思ってゴリ押しでオーバーライドしました。解決!

横画面

YokoViewController.swift
class YokoViewController: UIViewController {

  override var supportedInterfaceOrientations: UIInterfaceOrientationMask {   
     //最初の画面呼び出しで画面を右横画面に変更させる。
     UIDevice.current.setValue(3, forKey: "orientation")
     return .landscapeRight
  }

   // 画面を自動で回転させるかを決定する。
   override var shouldAutorotate: Bool {
      //画面が縦だった場合は回転させない
     if(UIDevice.current.orientation.rawValue == 1){
          return false
    }
      else{
          return true  
    }
  }
}

横画面のほうはちょっとめんどくさくなりました。
shouldAutorotateは画面回転ごとに呼び出されるので、画面回転すれば、横画面にすることができます。if条件で縦のとき自動回転OFFにすれば、縦に回転しません。

ただ、最初の画面遷移時に、自動回転OFF→縦画面に変更の順に処理されるので、先に横画面の変更処理をする必要があります。どうしたものか...

横画面 自動回転先呼び出し

適当にTry&Errorを繰り返してたら、viewDidLoad()内でprint()することで
画面の自動回転定義(shouldAutorotate)より先に呼び出すことができました。
いい子。

YokoViewController.swift
     override func viewDidLoad() {
        //画面の向きを変更させるために呼び出す。
        print(supportedInterfaceOrientations)
        <<Code>>
    }

横画面 右左判定

ただ、横向きでまとめてしまうと、現在の画面向きが左横画面なのに、右横画面だと逆向きになってしまいます。なので左横画面とそれ以外で場合分けしました。

YokoViewController.swift
   override var supportedInterfaceOrientations: UIInterfaceOrientationMask {   
      //左横画面に変更
      if(UIDevice.current.orientation.rawValue == 4){
             UIDevice.current.setValue(4, forKey: "orientation")
             return .landscapeLeft    
     }
       //左横画面以外の処理
      else {
              //右横画面に変更させる。
              UIDevice.current.setValue(3, forKey: "orientation")
              return .landscapeRight
     }
 }

それでうまく行くと思いきや、前画面で縦に指定しているので
画面向き判定が「縦」になってしまいます。なので強制的に右向き画面になります。こまった。

横画面 左横画面にセット

そこでviewDidLoad()内で以下の処理をします。

YokoViewController.swift
     override func viewDidLoad() {
     //画面向きを左横画面でセットする
     UIDevice.current.setValue(4, forKey: "orientation")

     //画面の向きを変更させるために呼び出す。
     print(supportedInterfaceOrientations)
     <<Code>>
   }

縦画面で遷移している前提ですが、一度強制的に左横画面にセットします。
自動回転は縦画面でなければ、回転するので左横画面になった後右横画面に自動的に回転します。解決!

以下横画面のソースコード全文です。

YokoViewController.swift
class YokoViewController: UIViewController {

  override var supportedInterfaceOrientations: UIInterfaceOrientationMask {   
     //左横画面に変更
       if(UIDevice.current.orientation.rawValue == 4){
              UIDevice.current.setValue(4, forKey: "orientation")
              return .landscapeLeft    
      }
       //左横画面以外の処理
       else {
              //最初の画面呼び出しで画面を右横画面に変更させる。
              UIDevice.current.setValue(3, forKey: "orientation")
              return .landscapeRight
      }
 }

   // 画面を自動で回転させるかを決定する。
   override var shouldAutorotate: Bool {
      //画面が縦だった場合は回転させない
      if(UIDevice.current.orientation.rawValue == 1){
           return false
     }
       else{
           return true  
     }
  }
   override func viewDidLoad() {
     //画面向きを左横画面でセットする
     UIDevice.current.setValue(4, forKey: "orientation")

     //画面の向きを変更させるために呼び出す。
     print(supportedInterfaceOrientations)
     <<Code>>
  }
}  

なんとかできました。

参考にした記事

https://qiita.com/orimomo/items/6ab57c6706a8dbc181a1

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

【プログラミング初心者】Swift基礎~メソッド・関数~

はじめに

今回はSwiftでのメソッド作成方法についていくつか紹介します。

メソッド・関数とは

関数

そもそも関数がどういうものか説明します。

簡単にいうと処理の塊です。

プログラムにはmain関数と呼ばれるプログラムを実行した時最初に呼ばれる処理があります。
(iOSアプリをSwiftで開発する場合は内部で定義されているので見れませんが)
極論をいうとこのmain関数さえあれば関数を使わなくてもプログラムは書けます。

ですが現実的に数万行に及ぶ処理がずらずらと書かれていても処理を把握することは困難です。
そのため関数に処理を分けてあげて関係する処理を分割してあげています。

さらに同じ処理をする場合は関数に書くことで重複するコードを書かなくて済みます。

関数は数学で使っていた関数と似ており、何かInputを与えて処理の結果をOutputとして返します。
y = ax + bという1次関数があります。
これはxに何かInputを与えた結果yというOutputを得るという処理です。
これをプログラムで書くと以下になります。

let a = 5
let b = 10
func linearFunction(x: Int) -> Int {
    return a * x + b
}

let y = linearFunction(x: 3)

この何かしら処理をするlinearFunctionのようなものを関数と呼びます。

引数

関数には必須ではありませんが引数を設定できます。
上記の例で言うとx: Intが引数です。

引数は関数へのInputです。
この与えられた引数を使って処理を制御します。
もちろん引数なしで制御可能な場合は定義しなくても問題ありません。
必要とする場合は型アノテーションが必須となります。

与えられた引数はletつまり定数として定義されます。
従って変更することはできません。
あくまで与えられた値を使って何か処理をするという用途になります。

また複数指定することができます。
複数指定する場合は以下のようになります。

func function(arg1: Int, arg2: Int, arg3: String) {
}

戻り値・返り値

戻り値は関数からのOutputです。
関数が値を返してくる値を戻り値や返り値と呼びます。
戻り値なしの場合は不要ですが、戻り値がある場合は-> 戻り値の型というように戻り値の型を明示する必要があります。

func function(arg1: Int, arg2: Int, arg3: String) -> Int {
}

戻り値を設定した場合は必ず何かしらの値を返さなければなりません。

戻り値はreturn 値という構文で返します。
returnが呼ばれると後続の処理が呼ばれないので注意してください。

func function(arg1: Int) -> Int {
    return arg1 + 1
    print(arg1) // 実行されない
}

戻り値なし場合以下のように値なしreturnできます。

func function() {
    return
}

補足ですが戻り値の場合暗黙的にVoidという戻り値が返っています。
これは何もないというような意味です。
なので上記の例は以下と同じ意味です。

func function() -> Void {
    return
}

メソッド

メソッドというものもあります。
メソッドも関数の一種で、クラスが持つ関数のことです。

基本的にSwiftはクラス内で関数を定義しているため、メソッドという言葉を使うことが多いです。
ですが関数といっても間違いではないので問題ありません。

定義の仕方は関数と同じです。

呼び出し方

関数の場合、以下のようにそのまま直接呼び出すことができます。

var returnValue = function()

print()なども関数なのでそのまま呼び出すことができます。

メソッドの場合、オブジェクト.関数名()という形で呼び出します。

let object = Object()
let returnValue = object.function()

クラス内で自分のメソッドを呼び出す場合は以下のようにします。

let returnValue = self.function()

selfは自分自身のオブジェクトを示します。他の言語だとthisとかになったりします。
実はSwiftの場合selfを省略しても言語上問題ありません。

SampleClass.swift
class SampleClass {
    func function() -> Int {
        return 1 + 1
    }

    func test() {
        let selfvalue = self.function()
        let noSelfValue = function()
    }
}

これはチームのコーディング規約に則って付けるか付けないか決めてください。

Swiftメソッド記法

Swiftは言語として少し特殊で引数の書き方がいくつかあります。
よく使う例を紹介していきます。

デフォルト値指定

引数にデフォルト値を指定することができます。
デフォルトを設定した引数は引数なしで呼び出すことができます。

func function(arg1: Int = 0, arg2: Int = 0) {
    print("arg1: \(arg1), arg2: \(arg2)")
}

self.function()                    // arg1: 0, arg2: 0
self.function(arg1: 10)            // arg1: 10, arg2: 0
self.function(arg2: 10)            // arg1: 0, arg2: 10
self.function(arg1: 10, arg2: 10)  // arg1: 10, arg2: 10

引数のラベルを省略

以下のように引数前に_をつけて定義すると、メソッドコールのとき引数のarg1:というラベルを省略することができます。

func function(_ arg1: Int, arg2: Int) {
}

self.function(1, arg2: 1) // OK
self.function(1, 1) // NG
self.function(arg1: 1, arg2: 1) // NG

引数のラベルに別名を付ける

ラベルに別名をつけることができます。

func convert(from value: Int) -> String {
    return "\(value)"
}

let stringValue = self.convert(from: 1) // OK
let stringValue = self.convert(value: 1) // NG

補足ですが、この方法はSwfit特有のものではありますがよく使います。
よくfrom to withのような前置詞を使って別名を付けメソッド自体が英文として読めるように書くことが多いかと思います。

メソッドの引数について

以前変数がどのようにメモリに確保されるのかを説明しました。
メソッドの引数の場合はどうか見ていきます。

値渡し

先述しましたが引数はletとして新しく定義されているのと同義です。
そのため引数も変数もメモリ上の扱いは同じになります。

以下のように構造体とメソッドを定義し呼び出してみます。

struct Dog {
    var name: String
}

func update(with newName: String, dog: Dog) {
    dog.name = newName
}

var myDog = Dog(name: "ポチ")

print("更新前: " + myDog.name)
self.update(with: "ミケ", dog: myDog)
print("更新後: " + myDog.name)

この場合メソッド定義の部分でCannot assign to property: 'dog' is a 'let' constantとSwiftのエラーが発生し実行はできません。
これは引数がletで定義されているため発生したエラーです。
ですが、もし仮にSwiftがエラーを返さないと仮定します。
その場合であってもメソッド内のdog.nameは「ミケ」に更新されますが、元となっているmyDog.nameは更新されません。
これは構造体が値型のため引数として渡されたdogmyDogのコピーとなっているためです。
従って仮に実行できたとすると以下のような出力になります。

出力
更新前: ポチ
更新後: ポチ

このように値そのものを引数に渡すことを値渡しといいます。

参照渡し

ではクラスの場合はどうでしょう?
以下のようにクラスとメソッドを定義し呼び出してみます。

class Cat {
    var name: String

    init(name: String) {
        self.name = name
    }
}

func update(with newName: String, cat: Cat) {
    cat.name = newName
}

var myCat = Cat(name: "タマ")

print("更新前: " + myCat.name)
self.update(with: "トラ", cat: myCat)
print("更新後: " + myCat.name)

これはエラーは発生しません。
クラスの場合、参照型のため変数がletで定義されていても変数の値そのものを書換えているわけではないのでこれを実行できます。
実行すると以下のように出力されます。

出力
更新前: タマ
更新後: トラ

myCat.nameの値が更新されていますね。
参照型は引数にはmyCatへのアドレスをコピーとして渡しているため同じオブジェクトを参照しており、そのため内容の更新ができたというわけです。

このように引数にアドレス、つまり参照を渡しているものを参照渡しと言います。

値型を参照渡ししたいときは?

基本的にはやりません。
その場合は以下のように戻り値として新しいオブジェクトを返すように実装する方法が一般的です。

func update(with newName: String, dog: Dog) -> Dog {
    var newDog = dog
    newDog.name = newName
    return newDog
} 

let myDog = Dog(name: "ポチ")
let newDog = self.update(with: "ミケ", dog: myDog)
print(newDog.name) // ミケ

それでもどうしても構造体を参照渡ししたいということもあるかと思います。
その場合は以下の方法で実装できます。

func update(with newName: String, dog: inout Dog) {
    dog.name = newName
} 

var myDog = Dog(name: "ポチ")

print(myDog.name) // ポチ
self.update(with: "ミケ", dog: &myDog)
print(myDog.name) // ミケ

update実行前と後でmyDog.nameの値が変わっていることから参照渡しになったことがわかります。

引数がdog: inout Dogとなりました。
inoutを引数の型の前に付けると参照型という意味になります。

引数に渡す値は&myDogとなっています。
変数名の前に&を付けるとその変数のアドレスを意味します。
myDogはスタック領域に確保されているのでそのスタック上のアドレスが引数として渡されました。
その結果参照渡しとなりnameを更新できるようになりました。

最後に

今回はメソッドについて、またそのメソッドの定義の仕方や引数の性質について説明しました。

引数の定義の仕方はおそらくチームによって別れてくるところかもしれません。
どれでも書けるようにしておくのが無難と言えます。

今回の内容は以上です。
本記事とは別でプログラミング未経験からiOSアプリ開発が行えるようになることを目的とした記事を連載しています。
連載は以下にまとめていますのでそちらも是非もご覧ください。
アジェンダ:https://qiita.com/euJcIKfcqwnzDui/items/0b480e96166e88945684

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

アプリ開発超初心者が休業中にiOSアプリを公開した話

iOSアプリ開発は初心者でもできる?

 Yes

はじめに 

 先に結論から述べると100%できます。なぜなら、ボンクラの僕にもできたからです(笑)どのくらいボンクラかと言うと、普通運転免許?の学科試験に二度落ちたくらいです(笑)おそらく、この記事の読者のほうが何十倍も賢いことでしょう。
 そんな僕がたった1ヶ月半でどうやって、自作アプリをApp store(アプリケーションのダウンロードサービス)に公開するまでに至ることができたのかを、勉強法やマインド面を中心に、専門用語は避けて語っていきたいと思います。
 iOSアプリを開発してみたいけど何から始めたら良いのかわからなかったり、はじめてみたけれど、進め方がよくわからないと感じている方への助け舟となれたら嬉しいです。ただし、科学的根拠は一切ないので、一意見として受け止めてください(笑)

用意するもの

 アプリ開発において用意するものは以下の2点です。
パソコンに関しては、Windowsでできないこともないらしいですが、使い方ガイドや参考資料が豊富なためMacが望ましいです。また、iOSアプリを開発するための環境は、Xcodeを利用します。以下のURLからダウンロードページに飛べます。
1.Macのパソコン
2.Xcode(できれば最新バージョン)
[ダウンロード] https://apps.apple.com/jp/app/xcode/id497799835?mt=12
 App storeに公開するためには上記の2点+Apple Developer(年会費:1万1800円)に登録する必要があります。実機実験やシミュレータの利用は登録しなくてもできます。詳しくはググってみてください。

勉強法

 僕の勉強法を大雑把に説明すると、以下の3つのステップです。
効率よく学習するコツは、ステップ③を重視することです。以下で説明します。

① Swift(iOS開発用言語)の基礎学習
 ・「15時間でわかるSwift集中講座」(Swiftのバージョン間違った)著者:高橋広樹
 ・「本気ではじめるiPhoneアプリ作り」著者:西磨翁
 ・ドットインストール(動画学習)
②サンプルコードを写経する(mainはXcodeの使い方を学習)
③自作(バス時刻表アプリ)
 
 かけた期間で言うと、①に2週間②に1~2週間③に2~3週間程度です。
自粛期間中と言うこともあり、1日の平均学習時間は3時間くらい取れていました。
 ここで大事なことは③の自作の段階にすぐに移ることです。
多くの方が、理解していないのにできる訳ない(怒)と考えると思います。
確かにそうです。基礎がわかっていないのに作れるわけがありません。ここで言いたいことは、①の基礎学習期間に時間を使いすぎるな!と言うことです。
 ここに時間を使いすぎる人の多くは、完璧に理解しないと、もしくは覚えないといけないと考えていると思います。しかし、はっきり言って、Swiftを完璧に理解してるエンジニアは、ほとんどいないですし、ましてやコードを全て暗記している人なんてSwiftの開発者ぐらいです(笑)多くのエンジニアは、ネットなり書籍なりを調べながら開発しています。iOS開発を仕事にしてバリバリお金を稼いでいる人たちがどうなのかはわかりませんが、その方達もはじめは、調べながら開発していたはずです。全てを理解しようとせずに、何となくわかってきたぞとフワッとした感じで十分です。英会話とは違って、コンピュータは入力を待ってくれます(笑)とにかく簡単なもので良いので、自作アプリを作ってみましょう。
 とは言っても、学習能力には個人差あるし、「やっぱりどのくらい学習すれば良いかわからないよ」と言う方もいると思います。そこで、ボンクラの僕を最底辺として、学習量の目安を立てると、多くても上の3つの様な参考書(何でも良い)を一周すれば十分だと思います。理解力がある人は3つ目の動画学習だけで良いかもしれません。

何を開発すれば良いの?

 とにかく作れと言われても、何を作ったら良いのかわからないし、そもそも何が作れるのかもわからないと思います。iOSアプリでは情報さえあれば、基本的に何でも作れると言っても良いです。しかし、初学者にとって技術的に難しすぎると言ったことはあると思います。例えば、ネットと通信を行ったりするようなアプリは、サーバーサイドの知識もある程度はないといけないため、初学者には難しいかもしれません。したがって、自分ができそうなレベルのちょっと上のレベルくらいのものが良いと思います。初学者が比較的に作りやすいアプリを以下に書いてみました。アイデアが浮かなければ、他者が開発したアプリを真似してみるのもありだと思います。
・バス時刻表アプリ
・メモ機能アプリ
・現在位置取得(GPS)を利用したアプリ
・画像認識系アプリ
・計算アプリ

アプリを公開するためには

 しかし、上記のようなアプリは世の中に無数にあり、そのまま公開するとなると難しいです。恐らく審査ではねられます。appleの審査にこのような項目があるからです。
4.1:模倣
4.2:必要最低限の機能しかない
 そのため、何かひとつ独自性のある機能を足してあげたら良いと思います。僕が初めて、公開したアプリ(バス時刻表アプリ)もその程度のレベルでした。

最後に

 独学でプログラミングを学習しようと思うと、正直かなり根気がいります。実際に、プログラミングを学習したけれど、挫折する人がほとんどだそうです。その原因の一つとしては、一緒に勉強する仲間や指南してくれる人がいないからだと思います。そんな時は、オンライン上のセミナーに参加したり、SNSで志が同じ仲間を探してみたりすることでモチベーションを保てるのではないかと思います。プログラミングも言語の学習と同様で、学び続ければ必ず会話ができるようになります。一緒に頑張りましょう。

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

`if (a == 1 && a == 2 && a == 3)` は真か?

背景

なんだか面白そうな記事を見つけた
(a == 1 && a == 2 && a == 3)は真と評価できますか?
のでSwiftでやってみた

コード

var b = 0

var a: Int {
    get {
        b += 1
        return b
    }
}

if (a == 1 && a == 2 && a == 3) {
    print("Complete!")
}

解説

aとは別で変数を用意(ここではb)して、aのgetterでbをインクリメントし、bをreturnする。
if文でaを3回取得するが、その度にaのgetterを通るため、取得されるaの値は1ずつ増えていく。

結果

できた。?
スクリーンショット 2020-06-01 14.53.36.png

まとめ

getterを使うと、if文の中で同じ変数でも、異なる値を取得できる?

p.s. 他にいい方法があれば教えてください!

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

SwiftUIでScrollViewするときに影が横に出ないなどのトラブル対処法

この記事で解決できそうなこと

スクリーンショット 2020-06-01 14.25.08.jpg  スクリーンショット 2020-06-01 14.30.36.jpg

せっかく影をつけたのに横に出てないから見栄えが悪い。
スクロールインジケータが画面内側に出ている。

スクリーンショット 2020-06-01 14.33.06.jpg
画像のような状態を目指します

起きている問題の原因

SwiftUIのビューの周りにはframeと呼ばれる外枠が存在します。下の画像のように、デフォルトではビューの周りに存在しています。frame()モディファイアは外枠の大きさを変更することで中にあるビューの大きさを変更させるといったイメージですね。
これをスクロールビューで並べると、frameが画面全体に広がっていないので影が外側に出ない、インジケータが画面内に出現するというトラブルが起きます。
スクリーンショット 2020-06-01 14.36.13.jpg

問題の解決方法

paddingモディファイアによってframeをビューより大きく設定する必要があります。
スクリーンショット 2020-06-01 14.35.59.jpg
.padding(.init(top: 0, leading: 50, bottom: 0, trailing: 50))
のように、中身のビューのどのくらい外に外枠を配置するのか設定することができます。

ソースコード

**padding``をコメントアウトすることで横に影が表示されるようになります。

import SwiftUI

struct test: View {
    var body: some View {
        ScrollView{
            VStack(spacing: 8){
            ForEach(0..<15){ _ in
        Text("Hello, World!")
            .foregroundColor(.white)
            .frame(width: 300, height: 60)
            .background(Color.yellow)
            .padding(.init(top: 0, leading: 50, bottom: 0, trailing: 50))
            .shadow(radius: 10)
                }
            }
        }
    }
}

struct test_Previews: PreviewProvider {
    static var previews: some View {
        test()
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Swift] クロージャ(Closures)の記法

クロージャ(Closures)ってなんだか色々書き方あってややこしいですよね。
というわけで整理してみます。

想定読者

  • Swiftの基本的な文法はなんとなく理解しているが、クロージャはややこしいな、という人
    • Swiftはある程度書けるようになったけど、いまいちクロージャの書き方に自信がない人
    • 他言語でプログラミング経験があり、これからSwiftを学びたい人

クロージャとは

Closures are self-contained blocks of functionality that can be passed around and used in your code.
Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

クロージャは、コードの中で渡したり使用したりすることができる自己完結型の機能ブロックです。
Swiftのクロージャは、CやObjective-Cのブロックや他のプログラミング言語のラムダに似ています。

クロージャの書き方

基本構文(全部盛り)

まずはこれをしっかりと覚えると、後に紹介する省略記法が理解しやすくなります。

{ (引数名1: 型, 引数名2: 型...) -> 戻り値の型 in
     実行するコード...
} 

// 定義
let intToString = { (a: Int, b: Int) -> String in
    return "\(a)\(b)です"
}

// 利用する
let sample1 = intToString(1, 5)
print(sample1) // 1と5です

省略

型表記

型が推論できるケースでは、引数や戻り値の型表記を省略できます。

  • 型が推論できるケース
    • 代入する変数の型が明確
    • 関数の引数(関数定義で明確に記載される)
// 代入する変数の型が明確
var sample2: ((Int, Int) -> String)

// 引数の()も省略可能
sample2 = { a, b in
    return "\(a)\(b)です"
}

print(sample2(2, 3)) // 2と3です

return

クロージャ内の文がひとつしかない場合は return 省略可能

// return省略
let intToString = { (a: Int, b: Int) -> String in
    "\(a)\(b)です"
}

下記コードはコンパイルエラーとなります。

// 2つ以上文があると省略不可
let sum = { (a: Int, b: Int) -> Int in
    let sum = a + b
    sum
}

// ;で1行にしてもダメ
let sum = { (a: Int, b: Int) -> Int in
    let sum = a + b; sum
}

Shorthand Argument Names

型表記を省略できるケースでは、さらに引数名も省略可能です。
Shorthand Argument Names$ に引数のindexを合わせ、$0$1 と記載します。

// 引数名も省略可能
// 型の表記も省略 -> in も省略しないとコンパイルエラー
sample2 = { 
    "\($0)\($1)です"
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Stringで指定した文字数を抜き出す

Sample

var sampleText = "0123456789"

let targetStart = sampleText.index(sampleText.startIndex, offsetBy: 2)
let targetEnd   = sampleText.index(sampleText.startIndex, offsetBy: 5)
let substring   = sampleText[targetStart...targetEnd]
print(substring)  // 2345

Reference

Practice

  • 前述の内容を基に、複数行のテキストを指定した行数に整形する関数を作りました。
    • ただしWord Wrapは考慮していません。
import Cocoa


/// テキストを指定した行数に整形する
/// - Parameters:
///   - text: 対象のテキスト
///   - maxLineNumber: 整形したテキストの最大行数
/// - Returns: 最大行数以下に整形されたテキスト
func createArrangedText(for text: String, maxLineNumber: Int) -> String {
    var lines = [String]()

    // 行ごとに分割する
    // また空行がある場合は削除する
    text.enumerateLines { (line, stop) -> () in
        let arranged_line = line.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
        if arranged_line.count > 0 {
            lines.append(arranged_line)
        }
    }

    if maxLineNumber < lines.count && maxLineNumber > 0 {
        let oneLineText = lines.joined()
        lines = []

        let numberOfCharacterByLine = oneLineText.count / maxLineNumber  // 1行あたりの文字数

        var targetStart   = oneLineText.index(oneLineText.startIndex, offsetBy: 0)
        var targetEnd     = oneLineText.index(oneLineText.startIndex, offsetBy: numberOfCharacterByLine)
        let targetVeryEnd = oneLineText.index(oneLineText.endIndex,   offsetBy: 0)

        for i in 0..<maxLineNumber {
            let newLine: String
            if i == maxLineNumber - 1 {
                // 最終行は割り切れなかった字数文長くなる
                newLine = String(oneLineText[targetStart..<targetVeryEnd])
            } else {
                newLine = String(oneLineText[targetStart..<targetEnd])
                // 次のインデックス取得のために値を更新する
                targetStart = oneLineText.index(targetStart, offsetBy: numberOfCharacterByLine)
                targetEnd   = oneLineText.index(targetEnd,   offsetBy: numberOfCharacterByLine)
            }

            lines.append(newLine)
        }
    }

    return lines.joined(separator: "\r\n")
}

// from https://lipsum.sugutsukaeru.jp/index.cgi
let inputText =
"""
ここは場合常にその相違式というのの後よりするないな。
どうしても結果をお話者はたとい大した鑑定なでなどを認めるが致したがも参考ありますですながら、どうには片づけませあっないた。

自分をおらで事もけっしてその間のまあなますな。

とにかく嘉納さんに忠告個性そう講演に聞いなくこの世その例外だれか享有にとかいうご教育たうですですば、
こういうたくさんも私か西洋底でいるて、大森さんののの人の私に何でもかでもお相当となっで私鶴嘴にご発展よりするようにせっかくお話が勤まりましでしと、
"""

print(createArrangedText(for: inputText, maxLineNumber: 4))

/*
ここは場合常にその相違式というのの後よりするないな。どうしても結果をお話者はたとい大した鑑定なでなどを認めるが致した
がも参考ありますですながら、どうには片づけませあっないた。自分をおらで事もけっしてその間のまあなますな。とにかく嘉納
さんに忠告個性そう講演に聞いなくこの世その例外だれか享有にとかいうご教育たうですですば、こういうたくさんも私か西洋底
でいるて、大森さんののの人の私に何でもかでもお相当となっで私鶴嘴にご発展よりするようにせっかくお話が勤まりましでしと、
*/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RealmSwiftで値が保存されない

はじめに

Realm Swiftを使っていてデータの保存がうまく出来ていなかったのですが
原因は単純なことだったので同じようにハマる人はいないかもしれませんが、念の為書き残しておきます。

環境

Xcode: 11.3.1
Swift: 5
Realm Swift: 5.0.1

間違っていたコード

class User: Object {
    dynamic var id = ""
    dynamic var name = ""
}

let user = User()
user.id = "0001"
user.name = "Test"

let realm = try! Realm()
try! realm.write() {
    realm.add(user)
}

let users = realm.objects(User.self)
print(users.count)
for u in users {
    print(u.id)
    print(u.name)
}

保存後に値を出力していますがレコードが追加されている(countがある)ことは確認できるものの
値は保存されておらず空文字が出力されていました。

正しいコード

class User: Object {
    // 修正
    @objc dynamic var id = ""
    @objc dynamic var name = ""
}

let user = User()
user.id = "0001"
user.name = "Test"

let realm = try! Realm()
try! realm.write() {
    realm.add(user)
}

let users = realm.objects(User.self)
print(users.count)
for u in users {
    print(u.id)
    print(u.name)
}

正しいコードはモデルクラスの変数に@objcが必要で
こちらを実行すると値が保存されていることが確認できました。

おわりに

ちなみになぜ間違ったコードを書いていたかというと、
ググって出てきた最新のドキュメント(日本語版)を参考にしていましたが
このドキュメントがかなり古いバージョン(2.10.0)のもので
最新バージョンとの違いになかなか気づかなかったからでした。

日本語版最新ドキュメント:https://realm.io/jp/docs/swift/latest/
英語版最新ドキュメント:https://realm.io/docs/swift/latest

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

UITableView セルのサイズを自動的に変更する

スタータープロジェクト

スタータープロジェクトでは、テーブルビューのセルに UITextView が一つだけあり、それには次のようなビューの制約(レイアウト)が含まれています:

Screen Shot 2020-05-31 at 2.42.41 PM.png

このプログラムビュー Storyboard では、テキストが表示されるはずです。

Screen Shot 2020-05-31 at 2.56.21 PM.png

しかしプログラムを実行しても画面には何も表示されません。テーブルビューのセルの高さが0になっているようです。

Simulator Screen Shot - iPhone 11 - 2020-05-31 at 15.00.56.png

この記事ではこの問題の修正の仕方について学んでいきます。

問題を修正する

スクロールを無効化する

デフォルトでは UITextView はスクロールが有効になっています。固定した高さにするためにそれを無効化する必要があります。

もともと、次のようなテーブルビューのクラスコードになっています。

class demoCell: UITableViewCell {
    @IBOutlet weak var textField: UITextView!
}

このコードを次のように更新します。

class demoCell: UITableViewCell {

    @IBOutlet weak var textField: UITextView!

    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
    }

}

再びシミュレーターでプログラムを実行すれば、UITextView のコンテンツが正常に表示されているのがわかります。

Simulator Screen Shot - iPhone 11 - 2020-05-31 at 15.43.35.png

テキストの内容が変更された時にビューの高さを更新する。

UITextView ]内のテキストを編集しようとしても、テーブルビューのセルの高さは変わりません。しかし、高さは動的に変更する必要があります。

自動で高さを設定する

テーブルビューのセルの高さを自動的に設定するようにシステムに指示することができます。

tableView.rowHeight = UITableView.automaticDimension

テキストの編集と連動させる

UITextViewDelegate を設定して、テキストが変更されたときにイベントが連動するようにします

class demoCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet weak var textField: UITextView!

    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
        textField.delegate = self
    }

    func textViewDidChange(_ textView: UITextView) {
        //新しいテキスト

    }

}

テキスト編集をテーブルビューコントローラに報告する

この変更を UITableViewController に報告すると、UITableViewController がテーブルビューをリフレッシュしてセルの高さが更新されます。

プロトコルを作成する

protocol demoCellDelegate : AnyObject {
    func textDidChange()
}

テーブルビューのセルで、demoCellDelegate.textDidChange() の関数を呼び出します

class demoCell: UITableViewCell, UITextViewDelegate {

    @IBOutlet weak var textField: UITextView!

    weak var delegate: demoCellDelegate?

    override func awakeFromNib() {
        super.awakeFromNib()
        textField.isScrollEnabled = false
        textField.delegate = self
    }

    func textViewDidChange(_ textView: UITextView) {
        //新しいテキスト
        delegate?.textDidChange()
    }

}

そして、テーブルビューのクラスで、デリゲートを設定します:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "demo") as! demoCell
    cell.textField.text = "テスト テスト テスト テスト テスト テスト テスト テスト テスト テスト テスト"
    cell.delegate = self
    return cell
}
extension ViewController: demoCellDelegate {

    func textDidChange() {
        //TODO
    }

}

テーブルをリロードせずにテーブルビューを更新する

ここで、テーブルの高さを更新するために、reloadData() を使用しないでください。代わりに tableView.beginUpdates()tableView.endUpdates() を使用してください。

extension ViewController: demoCellDelegate {

    func textDidChange() {
        tableView.beginUpdates()
        tableView.endUpdates()
    }

}

そして、このプログラムをシミュレータで再度実行すると、次のようになります。

ezgif-3-d9b71c7981f9.gif

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

【Swift】2次元配列をCSVファイルに変換してUIActivityで共有したい!

2次元配列をCSVファイルに変換してえ!

2次元配列をユーザーに渡したい時ってあるよね?
あれ、、意外とない、、?今ぱっと考えたけど全然思いつかない?
とりまそういうこともたまにあるよね!
というわけで2次元配列をCSVに変換して、ついでにUIActivityで共有する方法までまとめてみました!!!!✨

2次元配列 → CSV

まず2次元配列とファイル名を決める

ViewController.swift
    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

ボタンも作る!

ViewController.swift
    @IBAction func button(){

    }

2次元配列をCSVに変換!
Documentsの直下に配置しましょ!

ViewController.swift
func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }
    }

CSV → UIActivity

共有するファイルをNSURLで指定して、UIActivityを設定!

ViewController.swift
        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)

まとめ!

ViewController.swift
import UIKit

class ViewController: UIViewController {

    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

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

    @IBAction func button(){
        createFile(fileName: fileName, fileArrData: fruitsArray)
    }

    func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }

        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)
    }   
}

引用元・参考元

http://begigrammer.hatenablog.com/entry/2017/01/01/041039
https://qiita.com/nashirox/items/56894599013d712faa0a

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

2次元配列をCSVファイルに変換してUIActivityで共有してえ!

2次元配列をCSVファイルに変換してえ!

2次元配列をユーザーに渡したい時ってあるよね?
あれ、、意外とない、、?今ぱっと考えたけど全然思いつかない()
とりまそういうこともたまにあるよね!というわけで2次元配列をCSVに変換して、ついでにUIActivityで共有する方法までまとめてみました!!!!

2次元配列 → CSV

まず2次元配列とファイル名を決める

ViewController.swift
    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

ボタンも作る!

ViewController.swift
    @IBAction func button(){

    }

2次元配列をCSVに変換!
Documentsの直下に配置しましょ!

ViewController.swift
func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }
    }

CSV → UIActivity

共有するファイルをNSURLで指定して、UIActivityを設定!

ViewController.swift
        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)

まとめ!

ViewController.swift
import UIKit

class ViewController: UIViewController {

    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

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

    @IBAction func button(){
        createFile(fileName: fileName, fileArrData: fruitsArray)
    }

    func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }

        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)
    }   
}

引用元・参考元

http://begigrammer.hatenablog.com/entry/2017/01/01/041039
https://qiita.com/nashirox/items/56894599013d712faa0a

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

2次元配列をCSVファイルに変換してUIActivityで共有したい!

2次元配列をCSVファイルに変換してえ!

2次元配列をユーザーに渡したい時ってあるよね?
あれ、、意外とない、、?今ぱっと考えたけど全然思いつかない?
とりまそういうこともたまにあるよね!
というわけで2次元配列をCSVに変換して、ついでにUIActivityで共有する方法までまとめてみました!!!!✨

2次元配列 → CSV

まず2次元配列とファイル名を決める

ViewController.swift
    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

ボタンも作る!

ViewController.swift
    @IBAction func button(){

    }

2次元配列をCSVに変換!
Documentsの直下に配置しましょ!

ViewController.swift
func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }
    }

CSV → UIActivity

共有するファイルをNSURLで指定して、UIActivityを設定!

ViewController.swift
        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)

まとめ!

ViewController.swift
import UIKit

class ViewController: UIViewController {

    var fruitsArray : [[String]] = [["りんご","150円"],["みかん","120円"],["バナナ","180円"]]
    var fileName = "fruits"

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

    @IBAction func button(){
        createFile(fileName: fileName, fileArrData: fruitsArray)
    }

    func createFile(fileName : String, fileArrData : [[String]]){
        let filePath = NSHomeDirectory() + "/Documents/" + fileName + ".csv"
        print(filePath)
        var fileStrData:String = ""

        //StringのCSV用データを準備
        for singleArray in fileArrData{
            for singleString in singleArray{
                fileStrData += "\"" + singleString + "\""
                if singleString != singleArray[singleArray.count-1]{
                    fileStrData += ","
                }
            }
            fileStrData += "\n"
        }
        print(fileStrData)

        do{
            try fileStrData.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)
            print("Success to Wite the File")
        }catch let error as NSError{
            print("Failure to Write File\n\(error)")
        }

        let shareFile = NSURL(fileURLWithPath: filePath)

        let activityItems = [shareFile]

        let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

        self.present(activityVC, animated: true, completion: nil)
    }   
}

引用元・参考元

http://begigrammer.hatenablog.com/entry/2017/01/01/041039
https://qiita.com/nashirox/items/56894599013d712faa0a

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

【Swift】Twitterの下から出てくるメニューを作りたい!!!(PanModal)

Twitterの下から出てくるメニュー!使いやすいよね

405C6D77-69EB-440C-9B95-E47DCDC65CE1.png
15D45886-C620-47D8-BB85-CE5B974FF478.jpg
4DFF4EDB-633D-469C-B1CA-D459F0E84E26.png
↑こんな感じでしたからニョキって出るやつ
実装してえ!!!???

ライブラリを使って実装しよう

普通にライブラリなしでも作れるとは思うけど、やっぱりめんどくさいなーーーということで
それっぽいことができそうなライブラリを探してきたよ!?‍♂️
https://github.com/slackhq/PanModal
image.png
どっかでみたことあるなーって人いるでしょ?
「スッカカカ」でおなじみのSlackのライブラリなんですねー

というわけでこいつを使ってTwitterみたいなニョキッとメニューを作ってくぞ!!!

作ろう!

1. ライブラリを入れよう

cocoapodsを使いますよー!
Podfileにこれを追記して pod installしてください!

pod 'PanModal'

2. ニョキっとさせるボタンを追加

お好みでButtonとかTableViewとかを置いてあげよう
これを押したときにニョキっとメニューが出るようなイメージ!
image.png

3. ニョキっとメニュー本体を作る

新しいファイルを作ろう!
名前はなんでも
image.png

そんでとりまUITableViewDelegateとUITableViewDataSource、あとそれに関連したなんやかんやを書く!
(UITableViewでいいじゃんと思ったんですけど、なんかうまく動かなかったのでこうしてます 謎です)
image.png
次にニョキっとのキモを作ってくぞ!
image.png
PanModalをimportして、
image.png
もろもろ書こう!
これでニョキっとメニュー自体は完成!

4. ニョキっとメニューを呼び出そう!

ニョキッとメニューを出したいタイミングで、

ViewController.swift
presentPanModal(NyokittoViewController())

こいつを呼び出せばオッケー!
image.png

5. 完成!

これでtwitterのメニューっぽいニョキっとメニューが完成!お疲れさまです〜〜〜〜〜〜
ビデオ開けなかったわ...ぴえん...
image.png

6. いろいろ改良してえ!

↑のやつだと問題点がいろいろあるから改良しようぜ!っていうやつです

なんかメニューがスクロールするんだけど

そういう時はこれを追記だ!

tableView.isScrollEnabled = false

これを設定するとtableViewがスクロールしなくなるよ!便利だね!

ヘッダー欲しいんだけど

そういう時はヘッダーを作ろう!

let headerView:UIView = UIView()
let label: UILabel = UILabel()

ヘッダービューとそこに載せたいものを定義

これを追記

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerView
    }
    override func viewDidLayoutSubviews() {
        label.text = "共有"
        label.textAlignment = .center
        label.textColor = .black
        label.font = UIFont.systemFont(ofSize: 14)
        label.frame = CGRect(x: 0, y: 0, width: headerView.frame.width, height: headerView.frame.height)
        headerView.addSubview(label)
    }

あとheaderの高さを変えたい時は

tableView.sectionHeaderHeight = 40

そうするとこんな感じ!
image.png

区切り線邪魔なんだけど

そういう時はこれを追記

tableView.separatorStyle = .none

image.png

メニュー押された時の動き書きたいんだけど

こう書く!

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)    //選択解除
        /*

            ここになんやかんや書く

         */
    }

みんなもニョキっとメニュー作ろうぜ!

みんなもニョキっとメニュー作ろうぜ!
なんか結構付け焼刃なやり方だけど、ちょちょっと実装したい時とかに参考にしてみてください(絶対こんなのよりいいやり方ある、、、、、、)
GitHubもあげといたから参考にしてみて!
https://github.com/sugijotaro/NyokittoMenu

参考

https://github.com/slackhq/PanModal

環境

Xcodeのバージョン:11.5
ライブラリ:PanModal

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

Twitterの下から出てくるメニューを作りたい!!!!!!(PanModalを使用)

Twitterのしたから出てくるメニュー!使いやすいよね

405C6D77-69EB-440C-9B95-E47DCDC65CE1.png
15D45886-C620-47D8-BB85-CE5B974FF478.jpg
4DFF4EDB-633D-469C-B1CA-D459F0E84E26.png
↑こんな感じでしたからニョキって出るやつ
実装してえ!!!

ライブラリを使って実装しよう

普通にライブラリなしでも作れるとは思うけど、やっぱりめんどくさいなーーーということで
それっぽいことができそうなライブラリを探してきたよ!
https://github.com/slackhq/PanModal
image.png
どっかでみたことあるなーって人いるでしょ?
「スッカカカ」でおなじみのSlackのライブラリなんですねー

というわけでこいつを使ってTwitterみたいなニョキッとメニューを作ってくぞ!!!

作ろう!

1. ライブラリを入れよう

cocoapodsを使いますよー!
Podfileにこれを追記して pod installしてください!

pod 'PanModal'

2. ニョキっとさせるボタンを追加

お好みでButtonとかTableViewとかを置いてあげよう
これを押したときにニョキっとメニューが出るようなイメージ!
image.png

3. ニョキっとメニュー本体を作る

新しいファイルを作ろう!
名前はなんでも
image.png

そんでとりまUITableViewDelegateとUITableViewDataSource、あとそれに関連したなんやかんやを書く!
(UITableViewでいいじゃんと思ったんですけど、なんかうまく動かなかったのでこうしてます 謎です)
image.png
次にニョキっとのキモを作ってくぞ!
image.png
PanModalをimportして、
image.png
もろもろ書こう!
これでニョキっとメニュー自体は完成!

4. ニョキっとメニューを呼び出そう!

ニョキッとメニューを出したいタイミングで、

ViewController.swift
presentPanModal(NyokittoViewController())

こいつを呼び出せばオッケー!
image.png

5. 完成!

これでtwitterのメニューっぽいニョキっとメニューが完成!お疲れさまです〜〜〜〜〜〜
ビデオ開けなかったわ...ぴえん...
image.png

6. いろいろ改良してえ!

↑のやつだと問題点がいろいろあるから改良しようぜ!っていうやつです

なんかメニューがスクロールするんだけど

そういう時はこれを追記だ!

tableView.isScrollEnabled = false

これを設定するとtableViewがスクロールしなくなるよ!便利だね!

ヘッダー欲しいんだけど

そういう時はヘッダーを作ろう!

let headerView:UIView = UIView()
let label: UILabel = UILabel()

ヘッダービューとそこに載せたいものを定義

これを追記

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerView
    }
    override func viewDidLayoutSubviews() {
        label.text = "共有"
        label.textAlignment = .center
        label.textColor = .black
        label.font = UIFont.systemFont(ofSize: 14)
        label.frame = CGRect(x: 0, y: 0, width: headerView.frame.width, height: headerView.frame.height)
        headerView.addSubview(label)
    }

あとheaderの高さを変えたい時は

tableView.sectionHeaderHeight = 40

そうするとこんな感じ!
image.png

区切り線邪魔なんだけど

そういう時はこれを追記

tableView.separatorStyle = .none

image.png

メニュー押された時の動き書きたいんだけど

こう書く!

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)    //選択解除
        /*

            ここになんやかんや書く

         */
    }

みんなもニョキっとメニュー作ろうぜ!

みんなもニョキっとメニュー作ろうぜ!
なんか結構付け焼刃なやり方だけど、ちょちょっと実装したい時とかに参考にしてみてください(絶対こんなのよりいいやり方ある、、、、、、)
GitHubもあげといたから参考にしてみて!
https://github.com/sugijotaro/NyokittoMenu

参考

https://github.com/slackhq/PanModal

環境

Xcodeのバージョン:11.5
ライブラリ:PanModal

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

Twitterの下から出てくるメニューを作りたい!!!(PanModal)

Twitterのしたから出てくるメニュー!使いやすいよね

405C6D77-69EB-440C-9B95-E47DCDC65CE1.png
15D45886-C620-47D8-BB85-CE5B974FF478.jpg
4DFF4EDB-633D-469C-B1CA-D459F0E84E26.png
↑こんな感じでしたからニョキって出るやつ
実装してえ!!!???

ライブラリを使って実装しよう

普通にライブラリなしでも作れるとは思うけど、やっぱりめんどくさいなーーーということで
それっぽいことができそうなライブラリを探してきたよ!?‍♂️
https://github.com/slackhq/PanModal
image.png
どっかでみたことあるなーって人いるでしょ?
「スッカカカ」でおなじみのSlackのライブラリなんですねー

というわけでこいつを使ってTwitterみたいなニョキッとメニューを作ってくぞ!!!

作ろう!

1. ライブラリを入れよう

cocoapodsを使いますよー!
Podfileにこれを追記して pod installしてください!

pod 'PanModal'

2. ニョキっとさせるボタンを追加

お好みでButtonとかTableViewとかを置いてあげよう
これを押したときにニョキっとメニューが出るようなイメージ!
image.png

3. ニョキっとメニュー本体を作る

新しいファイルを作ろう!
名前はなんでも
image.png

そんでとりまUITableViewDelegateとUITableViewDataSource、あとそれに関連したなんやかんやを書く!
(UITableViewでいいじゃんと思ったんですけど、なんかうまく動かなかったのでこうしてます 謎です)
image.png
次にニョキっとのキモを作ってくぞ!
image.png
PanModalをimportして、
image.png
もろもろ書こう!
これでニョキっとメニュー自体は完成!

4. ニョキっとメニューを呼び出そう!

ニョキッとメニューを出したいタイミングで、

ViewController.swift
presentPanModal(NyokittoViewController())

こいつを呼び出せばオッケー!
image.png

5. 完成!

これでtwitterのメニューっぽいニョキっとメニューが完成!お疲れさまです〜〜〜〜〜〜
ビデオ開けなかったわ...ぴえん...
image.png

6. いろいろ改良してえ!

↑のやつだと問題点がいろいろあるから改良しようぜ!っていうやつです

なんかメニューがスクロールするんだけど

そういう時はこれを追記だ!

tableView.isScrollEnabled = false

これを設定するとtableViewがスクロールしなくなるよ!便利だね!

ヘッダー欲しいんだけど

そういう時はヘッダーを作ろう!

let headerView:UIView = UIView()
let label: UILabel = UILabel()

ヘッダービューとそこに載せたいものを定義

これを追記

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerView
    }
    override func viewDidLayoutSubviews() {
        label.text = "共有"
        label.textAlignment = .center
        label.textColor = .black
        label.font = UIFont.systemFont(ofSize: 14)
        label.frame = CGRect(x: 0, y: 0, width: headerView.frame.width, height: headerView.frame.height)
        headerView.addSubview(label)
    }

あとheaderの高さを変えたい時は

tableView.sectionHeaderHeight = 40

そうするとこんな感じ!
image.png

区切り線邪魔なんだけど

そういう時はこれを追記

tableView.separatorStyle = .none

image.png

メニュー押された時の動き書きたいんだけど

こう書く!

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)    //選択解除
        /*

            ここになんやかんや書く

         */
    }

みんなもニョキっとメニュー作ろうぜ!

みんなもニョキっとメニュー作ろうぜ!
なんか結構付け焼刃なやり方だけど、ちょちょっと実装したい時とかに参考にしてみてください(絶対こんなのよりいいやり方ある、、、、、、)
GitHubもあげといたから参考にしてみて!
https://github.com/sugijotaro/NyokittoMenu

参考

https://github.com/slackhq/PanModal

環境

Xcodeのバージョン:11.5
ライブラリ:PanModal

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