20200228のiOSに関する記事は8件です。

【iOS】UISegmentedControlの見た目を変える(iOS13対応)

概要

UISegmentedControlの見た目を変えたいなぁ - Qiita
さんを参考に、iOS13に対応したカスタムUISegmentedControlを紹介します。

見た目はこんな感じ。
image.png

コード

今回書いたカスタムクラスを丸ごと貼ります。
プロジェクト内にコピペしてください。

FlatSegmentedControl.swift
import UIKit

class FlatSegmentedControl: UISegmentedControl {
    private var segmentItemWidth: CGFloat = 0
    private var underline: CALayer = CALayer()
    private var themeColor: UIColor = UIColor(red: 41/255, green: 171/255, blue: 227/255, alpha: 1)
    private var layers: [CATextLayer] = []

    convenience init() {
        self.init(frame: CGRect.zero)
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUp()
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setUp()
    }

    private func setUp() {
        self.tintColor = UIColor.clear
        underline.backgroundColor = themeColor.cgColor
        self.layer.addSublayer(underline)
        for index in 0 ..< self.numberOfSegments {
            let textLayer = CATextLayer()
            textLayer.string = self.titleForSegment(at: index)
            textLayer.alignmentMode = CATextLayerAlignmentMode.center
            textLayer.contentsScale = UIScreen.main.scale
            textLayer.fontSize = 17
            layers.append(textLayer)
            self.layer.addSublayer(layers[index])
        }
        decorateSelectedItem()
    }

    func setThemeColor(_ color: UIColor) {
        themeColor = color
        underline.backgroundColor = themeColor.cgColor
        rewriteTextLayer()
    }

    override func setTitle(_ title: String?, forSegmentAt segment: Int) {
        super.setTitle(title, forSegmentAt: segment)
        layers[segment].string = title
    }

    private func rewriteTextLayer() {
        for index in 0 ..< self.numberOfSegments {
            let layer = layers[index]
            if index == self.selectedSegmentIndex {
                layer.font = UIFont.boldSystemFont(ofSize: layer.fontSize)
                layer.foregroundColor = themeColor.cgColor
            } else {
                layer.font = UIFont.systemFont(ofSize: layer.fontSize)
                layer.foregroundColor = UIColor.black.cgColor
            }
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        layoutLayerFrame()
    }
    private func decorateSelectedItem() {
        underline.frame.origin.x = CGFloat(self.selectedSegmentIndex) * segmentItemWidth
        rewriteTextLayer()
    }
    private func layoutLayerFrame() {
        segmentItemWidth = self.frame.width / CGFloat(self.numberOfSegments)
        underline.frame = CGRect(x: CGFloat(self.selectedSegmentIndex) * segmentItemWidth, y: self.frame.height - 4, width: segmentItemWidth, height: 4)
        if #available(iOS 13, *) {
            if let defaultLayers = self.layer.sublayers {
                defaultLayers.forEach { (layer) in
                    layer.isHidden = true
                }
            }
            layer.cornerRadius = 0
        }
        for index in 0 ..< self.numberOfSegments {
            layers[index].frame = CGRect(x: segmentItemWidth * CGFloat(index), y: 8, width: segmentItemWidth, height: self.frame.height - 12)
            if #available(iOS 13, *) { layers[index].isHidden = false }
        }
        if #available(iOS 13, *) { underline.isHidden = false }
        decorateSelectedItem()
    }
}

使い方

基本

Storyboardの例で進めます。

UISegmentedControlを選んで設置します。
image.png
FlatSegmentedControlクラスを使うようにします。
image.png
もし、コードでタイトル設定する場合はこのように。

    @IBOutlet weak var flatTab: FlatSegmentedControl! {
        didSet {
            flatTab.setTitle("1番目タブ", forSegmentAt: 0)
            flatTab.setTitle("2番目タブ", forSegmentAt: 1)
            flatTab.setTitle("3番目タブ", forSegmentAt: 2)
        }
    }

Actionにはタブ選択時の動作を書きます。これもUISegmentedControlと同じ。

    @IBAction func onTabTap(_ sender: FlatSegmentedControl) {
        switch sender.selectedSegmentIndex {
        case 0:
            // 1番目が選択された時にすること
        case 1:
            // 2番目が選択された時にすること
        case 2:
            // 3番目が選択された時にすること
        default:
            // デフォルト挙動
        }
    }

色を変える

setThemeColorというメソッドを生やしてあるので、このようにUIColorを渡してください。

@IBOutlet weak var flatTab: FlatSegmentedControl! {
    didSet {
        flatTab.setThemeColor(UIColor.cyan)
    }
}

image.png

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

Multiple targets match implicit dependency for linker flags エラーの解決法

FirebaseでGoogleSignInを導入したらエラーが発生。

share extensionを使った時も同じようなエラーが出た。

解決法

podfileを修正

以下を追加

  pod 'GTMSessionFetcher'

解決後

# Uncomment the next line to define a global platform for your project
platform :ios, '10.0'
use_frameworks!

def google_utilites
  pod 'GoogleUtilities/AppDelegateSwizzler'
  pod 'GoogleUtilities/Environment'
  pod 'GoogleUtilities/ISASwizzler'
  pod 'GoogleUtilities/Logger'
  pod 'GoogleUtilities/MethodSwizzler'
  pod 'GoogleUtilities/NSData+zlib'
  pod 'GoogleUtilities/Network'
  pod 'GoogleUtilities/Reachability'
  pod 'GoogleUtilities/UserDefaults'
  pod 'GTMSessionFetcher'
end

target 'myApp' do

  google_utilites

  pod 'Firebase/Auth'
  pod 'Firebase/Database'
  pod 'Firebase/Storage'
  pod 'Firebase/Analytics'
  pod 'GoogleSignIn'

end

target 'shareExtension' do

  google_utilites

  pod 'Firebase/Auth'
  pod 'Firebase/Database'
  pod 'Firebase/Storage'
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

謎リジェクトされてしまった件について+対応

初めて以下のようなリジェクトをくらいました。

差出人: Apple
Other - Other
Hello,

We are unable to continue this app’s review because your Apple Developer Program account is currently under investigation for not following the App Store Review Guidelines’ Developer Code of Conduct.

Common practices that may lead to an investigation include, but are not limited to:

・Inaccurately describing an app or service
・Misleading app content
・Engaging in inauthentic ratings and reviews manipulation
・Providing misleading customer support responses
・Providing misleading responses in Resolution Center
・Engaging in misleading purchasing or bait-and-switch schemes
・Engaging in other dishonest or fraudulent activity within or outside of the app

During our investigation, we will not review any apps you submit. Please do not create a new developer account or make any app transfers while waiting for the investigation to be completed. Once we have completed our investigation, we will notify you via Resolution Center. Due to the nature of the investigation you will be ineligible to receive an expedited review until the investigation is completed.

We do not require any additional information from you at this time, nor do we have any additional details to share. We appreciate your continued patience during our investigation.

Best regards,

差出人:アップル
その他-その他
こんにちは、

お使いのApple Developer Programアカウントは、App Storeレビューガイドラインのデベロッパー行動規範に従っていないため、現在調査中のため、このアプリのレビューを続行できません。

調査につながる可能性のある一般的な慣行には、以下が含まれますが、これらに限定されません。

・アプリまたはサービスの記述が不正確
・誤解を招くアプリコンテンツ
・不正な評価とレビュー操作の実施
・誤解を招くカスタマーサポートの対応を提供する
・解決センターで誤解を招くような回答を提供する
・誤解を招く購入または餌とスイッチのスキームに従事する
・アプリの内外で他の不正行為または詐欺行為に従事する

調査中、送信されたアプリは審査されません。調査の完了を待っている間は、新しいデベロッパーアカウントを作成したり、アプリを転送したりしないでください。調査が完了すると、解決センター経由で通知します。調査の性質上、調査が完了するまで、迅速なレビューを受ける資格はありません。

現時点では、お客様からの追加情報は必要ありません。また、共有する詳細情報もありません。調査中、しばらくお待ちいただきますようお願いいたします。

不正なんかしてませんよ!!
ちょっとググってみたら、昨年にデベロッパが大量検挙?されたようでその名残なのか
現在もちょこちょこ起きている様子。

https://forums.developer.apple.com/thread/116331
https://qiita.com/gureta/items/b21b264dfa95051e67bb
https://qiita.com/sowtara/items/101614f8183d5b3c1419
https://www.imgn.ltd/posts/developer-account-under-investigation/

一旦ストアからアプリを削除して申請し直すというのが対策として挙げられています。
ほんとにそれでいいのかな〜と疑問に思いつつも

・ストアからアプリ削除
・bundleID変更
・ストアにアプリ新規作成して再申請

を行ってみたところ、次の日には審査をしてもらい、無事(?)に別の要因でリジェクトされました。
審査自体をしてもらっただけでもありがたい。長い人は半年とか待ってたみたいなので…
(50ドル返せよって感じですよね)

現在はこの対応で進むという認識で良さそうです。
なんとなく釈然としない感じではありますが。

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

アプリの署名を更新

開発者の証明書の期限が切れるなどで証明書を更新すると、過去に作ったipaファイルが開発用端末で使えなくなる。
ビルドし直すという手もあるが、時間が経っているとビルド環境が変わっている可能性が高い。

Fastlaneをipaを再度署名することができる。

コマンド例
FASTLANE_SKIP_UPDATE_CHECK=1 && \
FASTLANE_OPT_OUT_USAGE=1 && \
    fastlane sigh resign myapp.ipa \
    --use_app_entitlements \
    --signing_identity 'iPhone Distribution: Runo Sahara.' \
    --provisioning_profile "$HOME/Library/MobileDevice/Provisioning Profiles/myapp.Today.mobileprovision" \
    --provisioning_profile "$HOME/Library/MobileDevice/Provisioning Profiles/myapp.mobileprovision"

確認方法

次を確認したくなる。

  • 有効期限が正しいか。
  • Entitlements が正しく、ワイルドカードで無いこと。

QuickLook

ipamobileprovisionファイルをmac上でQuickLookするツールがある。
Github: https://github.com/ealeksandrov/ProvisionQL

インストールコマンド例
brew cask install provisionql

Codesign

Codesign で entitlement を確認できる。

コマンド例
unzip myapp.ipa
codesign -d --entitlements :- Payload/myapp.app/

関連コマンド

自動化するために役に立つコマンド。

現在の状態で使えるidentity一覧
security find-identity -v -p codesigning
アプリ側のidentity
codesign -vv -d Payload/myapp.app
mobileprovisionの情報を取得
security cms -D -i embedded.mobileprovision > temp.plist
APPNAME=`/usr/libexec/PlistBuddy -c "Print :AppIDName" temp.plist`
TEAMID=`/usr/libexec/PlistBuddy -c "Print :TeamIdentifier:0" temp.plist`
APPID=`/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" temp.plist`
EXPIRATIONDATE=`defaults read temp.plist ExpirationDate`

ipaを解凍してembedded.mobileprovisionをfindすれば、アプリ側のものが確認できる。)

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

iOSアプリの署名を更新

開発者の証明書の期限が切れるなどで証明書を更新すると、過去に作ったipaファイルが開発用端末で使えなくなる。
ビルドし直すという手もあるが、時間が経っているとビルド環境が変わっている可能性が高い。

Fastlaneをipaを再度署名することができる。

コマンド例
FASTLANE_SKIP_UPDATE_CHECK=1 && \
FASTLANE_OPT_OUT_USAGE=1 && \
    fastlane sigh resign myapp.ipa \
    --use_app_entitlements \
    --signing_identity 'iPhone Distribution: Runo Sahara.' \
    --provisioning_profile "$HOME/Library/MobileDevice/Provisioning Profiles/myapp.Today.mobileprovision" \
    --provisioning_profile "$HOME/Library/MobileDevice/Provisioning Profiles/myapp.mobileprovision"

確認方法

次を確認したくなる。

  • 有効期限が正しいか。
  • Entitlements が正しく、ワイルドカードで無いこと。

QuickLook

ipamobileprovisionファイルをmac上でQuickLookするツールがある。
Github: https://github.com/ealeksandrov/ProvisionQL

インストールコマンド例
brew cask install provisionql

Codesign

Codesign で entitlement を確認できる。

コマンド例
unzip myapp.ipa
codesign -d --entitlements :- Payload/myapp.app/

関連コマンド

自動化するために役に立つコマンド。

現在の状態で使えるidentity一覧
security find-identity -v -p codesigning
アプリ側のidentity
codesign -vv -d Payload/myapp.app
mobileprovisionの情報を取得
security cms -D -i embedded.mobileprovision > temp.plist
APPNAME=`/usr/libexec/PlistBuddy -c "Print :AppIDName" temp.plist`
TEAMID=`/usr/libexec/PlistBuddy -c "Print :TeamIdentifier:0" temp.plist`
APPID=`/usr/libexec/PlistBuddy -c "Print :Entitlements:application-identifier" temp.plist`
EXPIRATIONDATE=`defaults read temp.plist ExpirationDate`

ipaを解凍してembedded.mobileprovisionをfindすれば、アプリ側のものが確認できる。)

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

Core Haptics - カスタムハプティックパターンの作成と再生

Core Hapticsは、iOS 13で新たに追加された「ハプティック(触覚)パターンを作成し、再生する」ためのフレームワークです。ついにTaptic Engineを開発者が制御するためのAPIが公開されたというわけです。1

従来手法(UIFeedbackGenerator)との違い

Taptic Engineが初めて搭載されたのはiPhone 6sで、ハプティックパターンの再生自体は、iOS 10で追加されたUIFeedbackGeneratorにより以前から可能でした。

UIFeedbackGeneratorは抽象クラスで、UIImpactFeedbackGenerator, UINotificationFeedbackGenerator, UISelectionFeedbackGeneratorの3種類の具象クラスがあります。それぞれの実装例を以下に示します。

  • UIImpactFeedbackGenerator
let impactFeedbacker = UIImpactFeedbackGenerator(style: .heavy)
impactFeedbacker.prepare()
impactFeedbacker.impactOccurred()
  • UINotificationFeedbackGenerator
let notificationFeedbacker = UINotificationFeedbackGenerator()
notificationFeedbacker.notificationOccurred(.success)
  • UISelectionFeedbackGenerator
let selectionFeedbacker = UISelectionFeedbackGenerator()

@IBAction func sliderChanged(_ sender: UISlider) {
    selectionFeedbacker.selectionChanged()
}

上記のコード例からもわかる通り、いずれの場合もあらかじめ決められたタイプのハプティックパターンを再生するというものでした。

Core Hapticsの場合は、このハプティックパターンをカスタマイズできる点が従来手法と大きく違う点です。

Core Hapticsの実装

まずはCore Hapticsの全体感を掴むため、基本的な実装の流れを見てみましょう。

1. インポート

CoreHapticsをインポートします。

import CoreHaptics

2. エンジンをスタートする

エンジン(CHHapticEngine)を初期化し、

let engine = try! CHHapticEngine()

start()メソッドを呼んでスタートします。

try! engine.start()

3. ハプティックパターンを生成する

ハプティックイベント(CHHapticEvent)を生成し(詳細は後述)、

let audioEvent = CHHapticEvent(eventType: .audioContinuous, parameters: [
    CHHapticEventParameter(parameterID: .audioPitch, value: -0.15),
    CHHapticEventParameter(parameterID: .audioVolume, value: volume),
    CHHapticEventParameter(parameterID: .decayTime, value: decay),
    CHHapticEventParameter(parameterID: .sustained, value: 0)
], relativeTime: 0)

let hapticEvent = CHHapticEvent(eventType: .hapticTransient, parameters: [
    CHHapticEventParameter(parameterID: .hapticSharpness, value: sharpness),
    CHHapticEventParameter(parameterID: .hapticIntensity, value: intensity)
], relativeTime: 0)

複数のハプティックイベントを組み合わせてパターン(CHHapticPattern)を生成します。

let pattern = 
    try! CHHapticPattern(events: [audioEvent, hapticEvent], parameters: [])

4. ハプティックパターンを再生する

CHHapticEnginemakePlayer(with:)メソッドにパターンを渡して、プレイヤー(CHHapticPatternPlayer)オブジェクトを生成します。

let player = try! engine.makePlayer(with: pattern)

CHHapticPatternPlayerstart(atTime)メソッドを呼んでハプティックを再生します。

try! player.start(atTime: CHHapticTimeImmediate)

ハプティックイベント(CHHapticEvent)

Core Hapticsとはカスタムハプティックパターンをつくって再生できるフレームワークです。そして、そのパターン(CHHapticPattern)は複数のハプティックイベント(CHHapticEvent)から構成され2

hapticpattern.png

ハプティックイベントは発生タイミング、イベントタイプ、長さ、そして複数のイベントパラメータ(CHHapticEventParameter)より規定されます。

つまり、カスタムなハプティックパターンをつくる重要な構成要素であるCHHapticEventを規定する各種プロパティとCHHapticEventParameterを理解することがCore Hapticsを使いこなす鍵となります。

というわけで、以下でCHHapticEventを規定する各種要素について順番に解説していきます。

ハプティックイベントタイプ(CHHapticEvent.EventType)

CHHapticEventは次のようなイニシャライザを持ち、第1引数にイベントタイプ(CHHapticEvent.EventType)を渡せるようになっています。

init(eventType type: CHHapticEvent.EventType, 
     parameters eventParams: [CHHapticEventParameter], 
     relativeTime time: TimeInterval)

これはその名の通りハプティックイベントのタイプを決めるもので、次の4種類が定義されています。

static let audioContinuous: CHHapticEvent.EventType
static let audioCustom: CHHapticEvent.EventType
static let hapticTransient: CHHapticEvent.EventType
static let hapticContinuous: CHHapticEvent.EventType

audio〜はオーディオによるフィードバック、haptic〜は触覚によるフィードバックです。

hapticタイプの方は"Transient"と"Continuous"とがありますが、"Transient"とは「一時的な」という意味で、hapticTransientは短いインパルス的なハプティックを、hapticContinuousは任意の長さを持ち、ループするハプティックを示します。

CHHapticEventの発生タイミング(relativeTime)

上述したCHHapticEventのイニシャライザは、第3引数にイベントを開始する時間(relativeTime)を指定できるようになっています。

名前に"relative"とある通り、絶対時間ではなく、ハプティックパターン内における相対時間(単位は秒)で指定します。

let hapticEvent = CHHapticEvent(
    eventType: type, parameters:params, 
    relativeTime: 0.1)  // 0.1秒後に開始

CHHapticEventの長さ(duration)

CHHapticEventはハプティックイベントの長さを指定するdurationプロパティを持ちます。

var duration: TimeInterval

Continuousなイベントタイプの場合は、本プロパティに0.0より大きい値をセットしておかないと再生時にクラッシュします。3

最大値は30秒です。

イベントパラメータ(CHHapticEventParameter)

前述のCHHapticEventのイニシャライザの第2引数に配列で渡すのが、イベントパラメータ(CHHapticEventParameter)です。

let audioEvent = CHHapticEvent(
    eventType: type, 
    parameters: [param1, param2],   // [CHHapticEventParameter]
    relativeTime: 0)

イニシャライザは次のように定義されており、

init(parameterID: CHHapticEvent.ParameterID, value: Float)

第1引数にパラメータの種類を示すID(CHHapticEvent.ParameterID)、第2引数にパラメータの値(Float)を渡します。

let pitch  = CHHapticEventParameter(parameterID: .audioPitch, value: -0.15)
let volume = CHHapticEventParameter(parameterID: .audioVolume, value: 0.5)

CHHapticEvent.ParameterIDには多くの種類があるので、ここでは主なものを紹介します。

haptic用

イベントタイプがhapticTransient, hapticContinuousなハプティックイベント専用のパラメータIDが次の2つです。

static let hapticIntensity: CHHapticEvent.ParameterID
static let hapticSharpness: CHHapticEvent.ParameterID
  • hapticIntensity:ハプティックの強さを0.01.0で指定
  • hapticSharpness:ハプティックの鋭さを0.01.0で指定

audio用

イベントタイプがaudioContinuous, audioCustomなハプティックイベント専用のパラメータIDが次の4つです。

static let audioVolume: CHHapticEvent.ParameterID
static let audioPitch: CHHapticEvent.ParameterID
static let audioPan: CHHapticEvent.ParameterID
static let audioBrightness: CHHapticEvent.ParameterID
  • audioVolume:音量を0.01.0で指定
  • audioPitch:音のピッチを-1.0(低い)〜1.0(高い)で指定
  • audioPan:音の位置(定位)を-1.0(左)〜1.0(右)で指定。デフォルトは0.0(中央)
  • audioBrightness:音の高周波成分を0.01.0で指定。デフォルトは1.0(高周波成分を減らさない)

共通

audio, haptic共通で"Continuous"なイベントタイプに使えるパラメータのIDとして、次のようなものがあります。それぞれハプティックイベントの波形を細かく調整するためのパラメータです。

static let attackTime: CHHapticEvent.ParameterID
static let decayTime: CHHapticEvent.ParameterID
static let releaseTime: CHHapticEvent.ParameterID
static let sustained: CHHapticEvent.ParameterID

AHAP

AHAP (Apple Haptic and Audio Pattern)はハプティックパターンを定義するJSONライクなファイルフォーマットです。次のような構造でパターンを定義できます。

ahap.png

以下にAHAPファイルの例を示します。4

{
  "Version": 1.0,
  "Metadata":
    {
      "Project" : "Haptic Sampler",
      "Created" : "5 June 2019",
      "Description" : "An effect that builds in sharpness and intensity."
    },
  "Pattern":
  [
    {
      "Event":
      {
        "Time": 0.0,
        "EventType": "HapticContinuous",
        "EventDuration": 1.7,
        "EventParameters":
        [
          { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 },
          { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 }
        ]
      }
    },
    {
      "ParameterCurve":
      {
        "ParameterID": "HapticIntensityControl",
        "Time": 0.0,
        "ParameterCurveControlPoints":
        [
          { "Time": 0, "ParameterValue": 0.0 },
          { "Time": 1.1, "ParameterValue": 0.5 },
          { "Time": 1.7, "ParameterValue": 0.0 }
        ]
      }
    },
    ...(略)
  ]
}

この定義のうちEventキーはCore HapticsのCHHapticEventに相当し、その配下のTimeキーはrelativeTimeプロパティ、EventTypeキーはeventTypeプロパティ、EventDurationキーはdurationEventParametersキーはeventParametersプロパティに相当します。またEventParametersキー配下の配列に入る要素はCHHapticEventParameterに相当します。こうしてみれば、AHAPというフォーマットはハプティックパターン(CHHapticPattern)の実装をそのままJSONに落とし込んだだけであり、難しくはありません。

JSONフォーマットなので、AHAPは通常のテキストファイルとして閲覧・編集でき、Xcodeプロジェクトへの追加方法も他のリソースと同様です。

Core Hapticsは、このAHAPファイルからハプティックパターンを読み出して、再生することができます。

AHAPファイルからの再生

AHAPファイルを読み込んでハプティックパターンを再生する実装方法は非常にシンプルです。

エンジン(CHHapticEngine)を初期化し、

let engine = try! CHHapticEngine()

開始します。

try! engine.start()

あとはCHHapticEngineplayPattern(from:)メソッドを呼び、引数にAHAPファイルのURLを渡すだけです。

try! engine.playPattern(from: URL(fileURLWithPath: path))

以上でAHAPファイルからハプティックパターンを再生することができます。

サンプルコードのダウンロード

本記事は2019年9月に発売した書籍「iOS 13の新機能をざっくり把握する本」からの転載です。同書籍は現在は100円で販売しています。

またCore Hapticsは実際に手元で試してみないと文章だけでは伝わりにくいと思います。上記記事やWEBに転がっている情報で十分実装できるとは思いますが、サクッと試してみたい方はBOOTHにてサンプルも100円で販売しているので投げ銭がてらよろしければご利用ください。

https://shu223.booth.pm/items/1461791

カスタムパターンを生成するサンプルと、AHAPファイルを再生するサンプルが入っています。

IMG_0367.PNG


  1. Core Hapticsはオーディオによるフィードバックも扱うので、厳密にはTaptic Engineを制御するだけではありません。 

  2. 実際にはハプティックパターンを構成する要素としては他にCHHapticParameterCurveCHHapticDynamicParameterといったものがあります。 

  3. ただし、sustainedパラメータにfalse(0.0)をセットしておくと、Continuousなイベントタイプに対してduration0.0でも再生可能です。 

  4. Appleの"Haptic Sampler"というサンプルコードに付属しているInflate.ahapというファイルから抜粋したものです。 

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

iOS13.3.1の実機でadmobバナーを表示できなかった件

やりたかったこと

iOS向けのアプリケーション作成に手を出しはじめたのですが、admobによる広告をつけようとしたときの件です。

公式のスタートガイド(https://developers.google.com/admob/ios/banner?hl=ja) に従い進めたところ、
シミュレータでのテスト広告表示まで問題なく確認できたが、実機(iPad mini 4)でのテストで躓いた部分を書きます。

前提/背景

admobによる広告表示のため、Xcodeのプロジェクトを作成した後に、CocoaPodsを用いてGoogle-Mobile-Ads-SDKをインポートしました。

公式ガイドの通りに実装をしてiPhoneSE, iPhone8のシミュレータでテスト広告の表示まで順調に確認できてました。

実機でのテストにおけるランタイムエラー

しかし、いざ実機でアプリケーションを動かそうとしたときに問題が発生しました。
ビルドは成功するのだが、実行時エラーでアプリケーションが落ちてしまう事象に遭遇しました。

  • エラーメッセージは下記の通りです(一部)。

dyld: Library not loaded: @rpath/GoogleUtilities.framework/GoogleUtilities
Referenced from 〜以下略

原因

iOS 13.3.1 以降はfree developerの作成したアプリでは、ダイナミックフレームワークを拒否するようで、実行時にエラーが起きるらしいです。
詳しく理解できていないのですが、Googleの提供するフレームワークはOSアップデートにより、開発用アカウントの作成したアプリでは動かないみたいです。

対応

Podfileの下記部分を変更し、pod updateを行ったところ無事実機での動作を確認できました。

target 'xxx' do
  # Comment the next line if you don't want to use dynamic frameworks
-use_frameworks!
+use_modular_headers!

記事作成時に気がついたんですが、思いっきり該当するコメントありますね...
# Comment the next line if you don't want to use dynamic frameworks
もしかしたら上記の変更ではなく、コメントアウトだけで動作するのかもしれないですね。

開発者登録していないアカウント(下記参照先の原文におけるfree developer)で起こる事象のようなので、
きちんと年貢を納めていれば起こらないエラーなのでしょうね。

多分事象が起こる対象が限定されることから、日本語記事があまり見つかりませんでした。
私のような駆け出しの方が躓くようなことがあった時に助けになれば幸いです。

環境

  • Xcode: Version 11.3.1
  • iPad mini 4: システムバージョン 13.3.1

参照先


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

admobの広告を実機で表示する際にエラーが起きたときの対処

やりたかったこと

iOS向けのアプリケーション作成に手を出しはじめたのですが、admobによる広告表示を実機で確認しようとしたとき、
下記のエラーに見舞われましたが、なんとか表示まで持っていけたという話です。

公式のスタートガイド(https://developers.google.com/admob/ios/banner?hl=ja) に従い進めたところ、
シミュレータでのテスト広告表示まで問題なく確認できたが、実機(iPad mini 4)でのテストで躓いた部分を書きます。

前提/背景

admobによる広告表示のため、Xcodeのプロジェクトを作成した後に、CocoaPodsを用いてGoogle-Mobile-Ads-SDKをインポートしました。

公式ガイドの通りに実装をしてiPhoneSE, iPhone8のシミュレータでテスト広告の表示まで順調に確認できてました。

実機でのテストにおけるランタイムエラー

しかし、いざ実機でアプリケーションを動かそうとしたときに問題が発生しました。
ビルドは成功するのだが、実行時エラーでアプリケーションが落ちてしまう事象に遭遇しました。

  • エラーメッセージは下記の通りです(一部)。

dyld: Library not loaded: @rpath/GoogleUtilities.framework/GoogleUtilities
Referenced from 〜以下略

原因

iOS 13.3.1 以降はfree developerの作成したアプリでは、ダイナミックフレームワークを拒否するようで、実行時にエラーが起きるらしいです。
詳しく理解できていないのですが、Googleの提供するフレームワークはOSアップデートにより、開発用アカウントの作成したアプリでは動かないみたいです。

対応

Podfileの下記部分を変更し、pod updateを行ったところ無事実機での動作を確認できました。

target 'xxx' do
  # Comment the next line if you don't want to use dynamic frameworks
-use_frameworks!
+use_modular_headers!

記事作成時に気がついたんですが、思いっきり該当するコメントありますね...
# Comment the next line if you don't want to use dynamic frameworks
もしかしたら上記の変更ではなく、コメントアウトだけで動作するのかもしれないですね。

開発者登録していないアカウント(下記参照先の原文におけるfree developer)で起こる事象のようなので、
きちんと年貢を納めていれば起こらないエラーなのでしょうね。

多分事象が起こる対象が限定されることから、日本語記事があまり見つかりませんでした。
私のような駆け出しの方が躓くようなことがあった時に助けになれば幸いです。

環境

  • Xcode: Version 11.3.1
  • iPad mini 4: システムバージョン 13.3.1

参照先


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