しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

openURLがDeprecatedだったので、新方式に置き換える

iOS10からopenURLが非推奨になったので、新しいメソッドに置き換えました。

  • 旧記法
UIApplication.shared.openURL(URL(string: "https://google.co.jp")!)
  • 新記法
UIApplication.shared.open(URL(string: "https://google.co.jp")!, options: [:], completionHandler: nil)

completionHandlerは遷移完了or遷移失敗したタイミングで呼ばれます。
引数は遷移が成功したかどうかの結果がBoolで渡されます。

UIApplication.shared.open(URL(string: "https://google.co.jp")!, options: [:], completionHandler: { result in
    print(result) // → true
})

下のように遷移失敗するとresultにfalseが入ります。

UIApplication.shared.open(URL(string: "hoge://")!, options: [:], completionHandler: { result in
    print(result) // → false
})

optionsは現在UIApplicationOpenURLOptionUniversalLinksOnlyのみサポートされています。
trueを渡すと、UniversalLink以外開けなくなります。
デフォルト値はfalseです。

UIApplication.shared.open(URL(string: "https://google.co.jp")!, options: [ UIApplicationOpenURLOptionUniversalLinksOnly: NSNumber(value: true)], completionHandler: nil)

UIApplicationOpenURLOptionUniversalLinksOnlyにはBool値を直接渡す事もできました。

UIApplication.shared.open(URL(string: "https://google.co.jp")!, options: [ UIApplicationOpenURLOptionUniversalLinksOnly: true], completionHandler: nil)

オプション一覧は下URLに記載されています。

developer.apple.com

Swift Playgroundsを使ってみる

今更ながら、Swift Playgroundsをしっかりと使ってみました。

Swift Playgrounds とは

Swiftを簡単に実行できる環境です。
書いたコードがすぐに実行されるので、簡単なコードを試したい時などに向いています。

Playgroundを使ってみる

PlaygroundはGet started with a playgroundから使う事ができます。

f:id:llcc:20170901124530p:plain

起動すると下のような画面になっています。

f:id:llcc:20170901124752p:plain

変数の定義やprint文の実行で右側に結果が表示されます。

f:id:llcc:20170901125031p:plain

printの結果は下にも表示されます。

f:id:llcc:20170901125952p:plain

右端の四角を押すことで変数の中身がコード上に表示されます。

f:id:llcc:20170901125510p:plain

Playgroundでグラフを描く

forやeachを使うとグラフを描画する事ができます。

f:id:llcc:20170901125625p:plain

複雑な形も描画可能です。

f:id:llcc:20170901125812p:plain

複数変数を定義すれば複数グラフを表示できます。

f:id:llcc:20170901130518p:plain

Value Historyを使えば実際の値を調べる事もできます。

f:id:llcc:20170901125852p:plain

f:id:llcc:20170901125921p:plain

様々なものを表示する

PlaygroundではViewの描画もできます。

f:id:llcc:20170901130737p:plain

Viewにラベルを追加する事もできます。

f:id:llcc:20170901130902p:plain

画像も表示する事ができます。
素材はhttp://flat-icon-design.com/の素材を利用させて頂きました。

f:id:llcc:20170901131304p:plain

画像の追加をするには、右上のボタンから左側のナビゲーターを表示します。
そこで表示されるResourcesに画像をドラッグすればPlayground中で使えるようになります。

f:id:llcc:20170901131424p:plain

【Swift】DateとDateComponentsの相互変換

Date → DateComponentsの変換とDateComponents → Dateの変換について調べてみました。

Date → DateComponents

下のようにCalendarを使うとDateComponentsを取得できます。

let date = Date()
let components = Calendar.current.dateComponents(in: TimeZone.current, from: date)

components.year // 2017
components.month // 8
components.day // 31

下のように、Dateの一部情報だけを使ってDateComponentsを作る事もできます。

let date = Date()
let components = Calendar.current.dateComponents([.calendar, .year, .month, .day], from: date)
components.year // 2017
components.hour // nil

DateComponents → Date

DateComponentsからDateは下の通りです。
DateComponentsのdateプロパティーを通してDateを取得します。

let components = DateComponents(calendar: Calendar.current, year: 2017, month: 9, day: 1)
components.date // → 2017/9/1 00:00

Dateへの変換ですが、Calendarがセットされてないとnilになるので注意が必要です。

let components = DateComponents(year: 2017, month: 9, day: 1)
components.date // → nil

Calendarさえセットしておけば、Dateには変換できます。

let components = DateComponents(calendar: Calendar.current)
components.date // → 1/1/1 00:00

それとyearとyearForWeekOfYearという近い意味の、両方セットするとどうなるか確かめてみました。
試したところyearForWeekOfYearが優先されるようです。

let components = DateComponents(calendar: Calendar.current, year: 2017, yearForWeekOfYear: 2016)
components.date // → 2016/01/01

【Swift, UIActionController】アクションシートで外側のビューを押された時のイベント

アクションシートで外側のビュー(下の灰色部分)を押された時のイベント周りについて調べてみました。

f:id:llcc:20170905141224p:plain

キャンセルボタンがある場合、外側のビューを押すとアクションシートは閉じます。
その際、キャンセルボタンが押された時と同じ処理が呼び出されます。

import UIKit

class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let c = UIAlertController(title: nil, message: "めっせーじ", preferredStyle: .actionSheet)
        c.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        c.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: { action in
            print("cancelled") // → 外側のビューをタップ or キャンセルボタンタップでここが呼ばれる
        }))
        
        present(c, animated: true, completion: nil)
    }
}

handlerにnilを渡しても、同じようにアクションシートは閉じます。

let c = UIAlertController(title: nil, message: "めっせーじ", preferredStyle: .actionSheet)
c.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
c.addAction(UIAlertAction(title: "キャンセル", style: .cancel, handler: nil))

キャンセルボタンがない場合は、外側のビューを押してもアクションシートが消えなくなります。

f:id:llcc:20170905141545p:plain

let c = UIAlertController(title: nil, message: "めっせーじ", preferredStyle: .actionSheet)
c.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

キャンセルボタンを2つセットしようとすると、下のようなエラーが出てアプリがクラッシュします。

UIAlertController can only have one action with a style of UIAlertActionStyleCancel
let c = UIAlertController(title: nil, message: "めっせーじ", preferredStyle: .actionSheet)
c.addAction(UIAlertAction(title: "キャンセル1", style: .cancel, handler: nil))
c.addAction(UIAlertAction(title: "キャンセル2", style: .cancel, handler: nil))

アクションシートをアラートに置き換えても、同じようにエラーになります。

let c = UIAlertController(title: nil, message: "めっせーじ", preferredStyle: .alert)
c.addAction(UIAlertAction(title: "キャンセル1", style: .cancel, handler: nil))
c.addAction(UIAlertAction(title: "キャンセル2", style: .cancel, handler: nil))

【iOS】Referencing Outlet Collectionで複数の要素をひとまとめ

Storyboardにある、Referencing Outlet Collectionというものを試してみました。
Referencing Outletsはいつも使うのですが、Referencing Outlet Collectionは触ったことがないので一度使ってみました。

f:id:llcc:20171016142608p:plain

最初にStoryboard上で複数のUILabelを配置します。

f:id:llcc:20171016142825p:plain

次にViewControllerにUILabelの配列を定義します。
配列なので、通常の@IBOutletと違いweakが付きません。

class ViewController: UIViewController {
    @IBOutlet var labels: [UILabel]!
}

それをStoryboardのUILabelと紐付ければ設定完了です。

f:id:llcc:20171016143031p:plain

設定をしたら、実際にラベルの値を変える処理を書いてみました。

class ViewController: UIViewController {
    @IBOutlet var labels: [UILabel]!
    
    override func viewDidLoad() {
        labels.enumerated().forEach {
            $0.element.text = "\($0.offset)"
        }
    }
}

実行したらラベルの値が変わってる事を確認できました。

f:id:llcc:20171016143232p:plain

それと、labelsの並び順は紐付け順になっていました。

Facebook Audience NetworkでInitial request from a bundle must come from a App Admin, Developer or Tester.エラーが出た時の対処法

表題のエラーの対処法です。
調べたところ、開発者アカウントでFacebookにログインすれば大丈夫との記事があったんですがそれでは動きませんでした。
自分の場合は「iOS10のiPhoneの設定アプリでFacebookにログイン」 → 「Facebookアプリをインストールしてログイン」の手順で解決しました。

【iOS】ナビゲーションバーの背景を画像にする

ナビゲーションバーの背景を画像にしてみました。

f:id:llcc:20170818121811p:plain

画像は下のフリー素材を使っています。

青色のざらざらした紙のテクスチャ素材 | Paper-co | 紙のテクスチャー素材を無料でダウンロードできるサイト

バーに背景画像を設定する方法は下の通りです。
titleTextAttributesはタイトルの色を白くする為の設定です。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        navigationController?.navigationBar.setBackgroundImage(UIImage(named: "texture"), for: .default)
        navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
    }
}

起動すると以下のようにバーの背景が代わります。
もし画像サイズが小さい場合は、画像をリピート描画をしてくれます。

f:id:llcc:20170818121817p:plain

下のように、UIImageをUIColorに変換した上でbarTintColorに設定しても同様の結果になります。

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        navigationController?.navigationBar.barTintColor = UIColor(patternImage: UIImage(named: "texture")!)
    }
}

アプリ内の全てのナビゲーションバーの背景を変える場合は、以下のようにappearanceを使います。

ちなみにbarTintColorで画像をセットしている時にSafariViewControllerを使うと'NSInternalInconsistencyException', reason: 'Only RGBA or White color spaces are supported in this situation.'というエラーでクラッシュします。
SafariViewController以外でもエラーになるかもしれないので、できるだけsetBackgroundImageを使う方が安全そうです。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        UINavigationBar.appearance().barTintColor = UIColor(patternImage: UIImage(named: "texture")!)
        
        return true
    }
}