しめ鯖日記

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

Admobのネイティブアドバンス広告を試してみる

Admobのネイティブアドバンス広告というフォーマットを試してみました。
これは他社でよくあるネイティブ同様、渡された文字列や画像で広告画面を作っていくもののようです。

ネイティブアドバンス広の広告ユニット作成

まずはAdmobの画面から広告ユニットを作成します。
下のネイティブを選択して、「ネイティブ広告アドバンスに切り替える」リンクをクリックします。

注) こちらのリンクですが、2017/10/23現在アカウントによって出たり出なかったりしているようです。
もしかすると一部ユーザーにしか開放されていないのかもしれません。

f:id:llcc:20171023152841p:plain

切り替えたら広告の種類を選んで作成完了です。

f:id:llcc:20171023153140p:plain

ネイティブアドバンス広告の実装

まずはCocoaPodsでAdmobをインストールします。

target 'MyApp' do
  use_frameworks!

  pod 'Google-Mobile-Ads-SDK'
end

ネイティブアドバンス広告のデータ取得は下の通りです。
adUnitIDは先程登録したものを利用してください。

import UIKit
import GoogleMobileAds

class ViewController: UIViewController {
    var adLoader: GADAdLoader?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        adLoader = GADAdLoader(adUnitID: "AD UNIT ID", rootViewController: self, adTypes: [.nativeAppInstall], options: nil)
        adLoader?.delegate = self
        adLoader?.load(GADRequest())
    }
}

extension ViewController: GADNativeAppInstallAdLoaderDelegate {
    func adLoader(_ adLoader: GADAdLoader, didReceive nativeAppInstallAd: GADNativeAppInstallAd) {
        
    }
    
    func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: GADRequestError) {
        
    }
}

Delegateメソッドでは、広告のタイトル・アイコン・本文などが渡されるので、それを使って広告を組み立てます。

extension ViewController: GADNativeAppInstallAdLoaderDelegate {
    func adLoader(_ adLoader: GADAdLoader, didReceive nativeAppInstallAd: GADNativeAppInstallAd) {
        nativeAppInstallAd.headline // → タイトル
        nativeAppInstallAd.body // → 説明文
    }
    
    func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: GADRequestError) {
        
    }
}

実際の広告表示はXibファイルを利用します。

f:id:llcc:20171023173501p:plain

Xibファイルを作り、そのViewをGADNativeAppInstallAdViewにします。

f:id:llcc:20171023173536p:plain

その上にラベルを配置して、GADNativeAppInstallAdViewのheadlineViewなど、各プロパティーに紐付けていきます。

f:id:llcc:20171023173730p:plain

ここまで終わったら、Xibを使って広告を表示します。
下では、Xibを読み込んでheadlineとcallToActionをセットしています。

extension ViewController: GADNativeAppInstallAdLoaderDelegate {
    func adLoader(_ adLoader: GADAdLoader, didReceive nativeAppInstallAd: GADNativeAppInstallAd) {
        let adView = Bundle.main.loadNibNamed("View", owner: self, options: nil)?.first as! GADNativeAppInstallAdView
        adView.backgroundColor = UIColor.lightGray
        adView.frame = CGRect(x: 0, y: view.frame.height - 50, width: view.frame.width, height: 50)
        adView.nativeAppInstallAd = nativeAppInstallAd
        view.addSubview(adView)
        
        (adView.headlineView as? UILabel)?.text = nativeAppInstallAd.headline
        (adView.callToActionView as? UILabel)?.text = nativeAppInstallAd.callToAction
    }
    
    func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: GADRequestError) {
        
    }
}

実行すると広告を表示する事ができます。
ラベルをタップすればAppStoreのページに遷移します。

f:id:llcc:20171023173851p:plain

今回は試してないのですが、広告タイプはインストール型以外にもあるようです。

GAD_EXTERN GADAdLoaderAdType const kGADAdLoaderAdTypeNativeAppInstall;
GAD_EXTERN GADAdLoaderAdType const kGADAdLoaderAdTypeNativeContent;
GAD_EXTERN GADAdLoaderAdType const kGADAdLoaderAdTypeNativeCustomTemplate;
GAD_EXTERN GADAdLoaderAdType const kGADAdLoaderAdTypeDFPBanner;

iPhoneアプリリリース時の「輸出コンプライアンス」のチェックを省く方法

iTunesConnectから審査に提出する際、「輸出コンプライアンス」と「広告ID」についてチェックをする必要があります。
毎回入力するのが手間だったので、省略する方法はないか調べてみました。

f:id:llcc:20171022154040p:plain

調べたところ、「輸出コンプライアンス」はInfo.plistにITSAppUsesNonExemptEncryptionを入れれば省略できるようです。

f:id:llcc:20171022154146p:plain

試したところ「輸出コンプライアンス」が表示されなくなりました。

f:id:llcc:20171022153716p:plain

objc_getAssociatedObjectで既存オブジェクトにプロパティーを追加

objc_getAssociatedObjectを使ってUIViewに新しいプロパティーを生やしてみました。

UIViewにプロパティーを追加する

UIViewにvalueというプロパティーを生やします。
実装は簡単で、下のようなextensionを追加すれば完了です。

private var valueKey = 0
extension UIView {
    var value: Int? {
        get {
            return objc_getAssociatedObject(self, &valueKey) as? Int
        }
        set {
            objc_setAssociatedObject(self, &valueKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

実行すると、下のように値をセットする事ができます。

let view = UIView()
print(view.value) // → nil
view.value = 10
print(view.value) // → Optional(10)

もちろんインスタンスが違えばプロパティーも別です。

let view = UIView()
let view2 = UIView()
view.value = 10
print(view.value) // → Optional(10)
print(view2.value) // → nil

キーとして宣言したvalueKeyですが、型はInt以外でも問題ありません。

private var valueKey = "valueKey"
extension UIView {
    var value: Int? {
        get {
            return objc_getAssociatedObject(self, &valueKey) as? Int
        }
        set {
            objc_setAssociatedObject(self, &valueKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

valueKeyの値は他プロパティーと被っても問題ありません。

private var valueKey = "valueKey"
private var valueKey2 = "valueKey"
extension UIView {
    var value: Int? {
        get {
            return objc_getAssociatedObject(self, &valueKey) as? Int
        }
        set {
            objc_setAssociatedObject(self, &valueKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
    var value2: Int? {
        get {
            return objc_getAssociatedObject(self, &valueKey2) as? Int
        }
        set {
            objc_setAssociatedObject(self, &valueKey2, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

let view = UIView()
print(view.value) // → nil
print(view.value2) // → nil
view.value = 10
print(view.value) // → Optional(10)
print(view.value2) // → nil

UICollectionViewの並び替え

UICollectionViewでの並び替えを試してみました。

f:id:llcc:20171020143248p:plain

Storyboardを使わないでUICollectionViewを表示

まずは普通にUICollectionViewを表示してみます。
コードは下の通りです。

class CollectionViewController: UICollectionViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        collectionView?.backgroundColor = UIColor.white
    }
    
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }
    
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.lightGray
        
        return cell
    }
}

CollectionViewのレイアウトは下の通りです。

let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
layout.minimumLineSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)

実行すると下のようにCollectionViewが表示されます。

f:id:llcc:20171020134036p:plain

UICollectionViewの並び替え

UICollectionViewの並び替えはmoveItemAtを実装するだけで完了です。

class CollectionViewController: UICollectionViewController {
    override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        
    }
}

これで下のように並び替えをする事ができるようになります。

f:id:llcc:20171020143349p:plain

【Xcode】Debug Memory Graphでメモリリークをチェック

Xcode8から登場したDebug Memory Graphという機能を試してみました。
これはインスタンス間の参照を図として表示できるもので、循環参照の発見などに役立ちます。

Debug Memory Graphは、アプリ起動後にデバッグエリア右端のアイコンをタップする事で実行できます(下画像の右端)

f:id:llcc:20171018141743p:plain

アイコンをタップすると、下のようにアプリ上のインスタンス一覧が表示されます。

f:id:llcc:20171018141832p:plain

インスタンスを選択すると、下のようにインスタンス同士の参照を図として見る事ができます。

f:id:llcc:20171018141925p:plain

次に、下のように循環参照する処理を書くとどうなるか試してみました。

class MyClassA {
    var myClass: MyClassB?
}

class MyClassB {
    var myClass: MyClassA?
}

let myClassA = MyClassA()
let myClassB = MyClassB()
myClassA.myClass = myClassB
myClassB.myClass = myClassA

Debug Memory Graphで見ると、下のように相互に参照をもっている事が分かります。

f:id:llcc:20171018142244p:plain

インスタンス一覧には紫色の警告マークが出るので、循環参照がすぐに分かります。

f:id:llcc:20171018142259p:plain

循環参照がない場合は下のように一方向だけの矢印になります。

f:id:llcc:20171018142506p:plain

【Swift】mp3をバックグラウンド再生する

バックグラウンド再生を試してみました。

mp3をフォアグラウンドで再生

まずはフォアグラウンドでmp3を再生してみます。
再生したいmp3ファイルをAssets.xcassetsに追加して下さい。

f:id:llcc:20171018131635p:plain

そのあと下のようにmp3再生処理を記述すれば完了です。

import UIKit
import AVFoundation

class ViewController: UIViewController {
    var player: AVAudioPlayer?
    
    override func viewDidLoad() {
        if let sound = NSDataAsset(name: "bgm") {
            player = try? AVAudioPlayer(data: sound.data)
            player?.play() // → これで音が鳴る
        }
    }
}

mp3をバックグラウンドで再生

次はアプリをバックグラウンドにしても再生し続ける方法を試します。

まずはCapabilitiesのBackground ModesをONにして、Audio Airplay and Picture in Pictureにチェックを入れます。

f:id:llcc:20171018132041p:plain

その後、AppDelegateなどに下2行を追加すれば完了です。

try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
try? AVAudioSession.sharedInstance().setActive(true)
import UIKit
import AVFoundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
        try? AVAudioSession.sharedInstance().setActive(true)
        
        return true
    }
}

これでアプリがバックグラウンドに移動してもbgmが再生されます。
シミュレータでは上の処理を追加してもバックグラウンド再生されないので注意が必要です。

【iOS】1つの画像データで2xと3xにも対応する

毎回2xと3xのデータを作るのが大変だったので、1つの画像データ(PDF形式)で全部に対応する方法を調査しました。

今回は下のような画像で検証しました。
利用したツールはSketchです。

f:id:llcc:20171017135428p:plain

まずはこのデータをpdfとして書き出します。

f:id:llcc:20171017135437p:plain

次にAssets.xcassetsでImage Setを作成します。

f:id:llcc:20171017135651p:plain

今作ったImage SetのScaleをSingle Scaleに変更します。

f:id:llcc:20171017135725p:plain

変更したら先程作ったPDFをドラッグ&ドロップで追加します。

f:id:llcc:20171017135755p:plain

登録したPDFは、普通の画像と同じように利用する事ができます。

f:id:llcc:20171017135826p:plain

アプリを起動すると、画像が正常に表示されている事が分かります。

f:id:llcc:20171017135917p:plain

下はPDFの代わりにpngを使った画像です。
PDFでないと画像がぼやける事が分かります。

f:id:llcc:20171017140036p:plain