20190820のiOSに関する記事は7件です。

iOS13(beta)でASWebAuthenticationSessionの変更点

注:本記事は Xcode 11 beta 6 (2019/08/20)時点での変更内容での話になります

iOS13でログイン画面が開かない

開発中のアプリでASWebAuthenticationSessionを利用してOAuthログインする機能があり、iOS13(beta)で動作確認をしたところログインボタンを押してログイン画面を出そうとしても出てくれない状態でした。

ログを見てみると以下のエラーの出力がありました。

Cannot start ASWebAuthenticationSession without providing presentation context. Set presentationContextProvider before calling -start.

どうやらiOS13からはstart()を呼び出す前にpresentationContextProviderをセットする必要があるようです。

ASWebAuthenticationPresentationContextProviding

presentationContextProviderASWebAuthenticationPresentationContextProviding(document)というprotocolに適合したNSObjectのサブクラスのインスタンスのようです。

定義は以下のようになっています。

/** @abstract Provides context to target where in an application's UI the authorization view should be shown.
 */
@available(iOS 13.0, *)
public protocol ASWebAuthenticationPresentationContextProviding : NSObjectProtocol {


    /** @abstract Return the ASPresentationAnchor in the closest proximity to where a user interacted with your app to trigger
     authentication. If starting an ASWebAuthenticationSession on first launch, use the application's main window.
     @param session The session requesting a presentation anchor.
     @result The ASPresentationAnchor most closely associated with the UI used to trigger authentication.
     */
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor
}

ASPresentationAnchor

なるほど、そのprotocolのインスタンスを用意すればいいことはわかったけど、ASPresentationAnchorってなんだ?と思ったらUIWindow(iOS, Mac Catalyst, tvOS) or NSWindow(macOS)のtypealiasでした。

直してみる

シンプルに書くと多分こんな感じに実装すれば良さそう

import AuthenticationServices
import UIKit

class ViewController: UIViewController {

    private var authenticationSession: ASWebAuthenticationSession?

    // ログインボタンを押した後に呼ばれる
    func loginStart() {
        let authenticationSession = ASWebAuthenticationSession(
            url: URL(string: "https://...")!,
            callbackURLScheme: "<url-scheme>",
            completionHandler: loginCompletionHandler(url:error:))
        if #available(iOS 13.0, *) {
            // 
            authenticationSession.presentationContextProvider = self
        }
        authenticationSession.start()

        // authenticationSessionが解放されないようにプロパティに入れておく
        self.authenticationSession = authenticationSession
    }

    // ログインの結果のハンドリング
    func loginCompletionHandler(url: URL?, error: Error?) {
        authenticationSession = nil
        ...
    }

    ...
}

@available(iOS 13.0, *)
extension ViewController: ASWebAuthenticationPresentationContextProviding {

    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
            return view.window!
    }
}

ちなみに今開発中のアプリではiOS11も対応しており、ASWebAuthenticationSessionSFAuthenticationSessionをラップしたクラスを定義していたため、ASWebAuthenticationPresentationContextProvidingを適合させたprivateなクラスを用意して実装しました。
このように実装する場合、presentationContextProviderは弱参照なので解放されてしまいます。そのためASWebAuthenticationPresentationContextProvidingを適合したオブジェクトのインスタンスもプロパティとして保持しないといけないことに注意してください。

class AuthenticationSession {

    ...
    private var authenticationSession: Any?
    private var presentationContextProvider: Any?

    func loginStart(from viewController: UIViewController) {
        if #available(iOS 12.0, *) {
            let authenticationSession = ASWebAuthenticationSession(
                url: loginURL,
                callbackURLScheme: "<url-scheme>",
                completionHandler: loginCompletionHandler(url:error:))
            if #available(iOS 13.0, *) {
                let presentationContextProvider = AuthPresentationContextProver(viewController: viewController)
                authenticationSession.presentationContextProvider = presentationContextProvider
                self.presentationContextProvider = presentationContextProvider
            }
            authenticationSession.start()
            self.authenticationSession = authenticationSession
        } else { // iOS 11.x
            let authenticationSession = SFAuthenticationSession(
                url: loginURL,
                callbackURLScheme: "<url-scheme>",
                completionHandler: loginCompletionHandler(url:error:))
            authenticationSession.start()
            self.authenticationSession = authenticationSession
        }
    }

    func loginCompletionHandler(url: URL?, error: Error?) {
        authenticationSession = nil
        presentationContextProvider = nil
        ...
    }

    ...

    @available(iOS 13.0, *)
    private class AuthPresentationContextProver: NSObject, ASWebAuthenticationPresentationContextProviding {

        private weak var viewController: UIViewController!

        init(viewController: UIViewController) {
            self.viewController = viewController
            super.init()
        }

        func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
            return viewController?.view.window!
        }
    }
}

おわり

ASWebAuthenticationSessionを使っているアプリはiOS13が出るまでに対応しないとログインが出来なくなってしまう可能性があるので注意しましょう。

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

pod installにハマった話

背景

2015年の正月以来、iOSの開発から遠ざかってきましたが、
久しぶりにそういうお仕事をいただき、
環境構築しなきゃいけなくなったので、
その時のお話です。

当初の思っていたこと

  • 既存アプリをgitでクローンする
  • xCodeを起動して、シミュレータで動かす
    • エラーが出たらトライアンドエラーの繰り返し・・・A

「上記やればいいんでしょ」的な軽い気持ちに。

→で、このAに色々ハマったのでメモのため残します。

目的

cocoaPodsのインストール。ただこれだけ。

ハマったこと概要

  1. rbenvでrubyをインストールしようとしたら、エラーが全くでなくてすごいハマる
  2. opensslがbrewのに切り替わらなくてハマる
  3. cocoaPodsのsetupが終わらなくてハマる

0.序章:xCodeでのエラー

xCodeでビルドしたら
rojectName/Pods/Pods...ProjectName.debug.xcconfig unable to open file
というようなエラーが出ました。

プロジェクトでcocoaPodsを使っていたんですが、私のPCには入っていなかったのが原因。

https://stackoverflow.com/questions/55558984/xcode-pods-projectname-debug-xcconfig-unable-to-open-file-wrong-directory
を参考にして、

$ pod deintegrate
$ pod install

を完了させることで良いらしいことが判明。
これを解消することが目的だったんですが、色々とやりました。

1.rbenvのアップグレート

podインストールするために、gemが必要。
→gemを使うために、rubyをアップデートしないと。
→rbenv使っていたのでまずは最新にしないと。

と言う流れでした。

$ brew update
$ brew upgrade rbenv

これでOK。

2.rubyのアップグレード

結論から言うとこれに一番ハマりました。。。

$ rbenv install 2.5.0
ruby-build: use openssl from homebrew
Downloading ruby-2.5.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.bz2
error: failed to download ruby-2.5.0.tar.bz2

BUILD FAILED (OS X 10.13.6 using ruby-build 20190423)
$

ログが少ない。。。
じゃあcurlを使ってやってみようかとなり(ここが一番ハマってた)実行。

$ curl -v -O https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.bz2
* Hostname was NOT found in DNS cache
*   Trying 151.101.1.178...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to cache.ruby-lang.org (151.101.1.178) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
} [data not shown]
* SSLv3, TLS alert, Server hello (2):
{ [data not shown]
* error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
* Closing connection 0
curl: (35) error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version
$

これかーーーー!!
opensslもcurlも何も最近してなかったから更新しようということに。

2-1.curlのアップデート

さてさて、curlはどこにあるんだろ?

$ which curl
/usr/local/php5/bin/curl
$ curl --version
curl 7.38.0 (x86_64-apple-darwin14.0.0) libcurl/7.38.0 OpenSSL/0.9.8zd zlib/1.2.11 libidn/1.20 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp 
Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz 

まさかphp5のを見に言っているとは。。。

・・・さ、気を取り直してupgradeしてパスの変更を

$ brew upgrade curl
$ brew link curl
$ sudo vi ~/.bash_profile 
$ source ~/.bash_profile
~/.bash_profile
## 以下を追加
export PATH="/usr/local/opt/curl/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/curl/lib"
export CPPFLAGS="-I/usr/local/opt/curl/include"
export PKG_CONFIG_PATH="/usr/local/opt/curl/lib/pkgconfig"

2-2.opensslのアップデート

さてさて、opensslはどこにあるんだろ?

$ which openssl
/usr/bin/openssl
$ openssl version
LibreSSL 2.2.7

これまたbrewのを見ていないので、これを機に変更しようと。
opensslのバージョンがLibreSSL 2.2.7なのが謎だけど今度調べよう。

・・・さ、気を取り直してupgradeしてパスの変更を

$ brew upgrade openssl
$ brew link openssl
$ sudo vi ~/.bash_profile 
$ source ~/.bash_profile
~/.bash_profile
## 以下を追加
export PATH="/usr/local/opt/openssl/bin:$PATH"

2.3 curlの再実行

さて、これでcurlうまく行くはず!

$ curl -v -O https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.bz2
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 151.101.1.178:443...
* TCP_NODELAY set
* Connected to cache.ruby-lang.org (151.101.1.178) port 443 (#0)

・・・省略・・・

{ [2759 bytes data]
100 13.3M  100 13.3M    0     0  4815k      0  0:00:02  0:00:02 --:--:-- 4815k
* Connection #0 to host cache.ruby-lang.org left intact
$

という感じでうまくいったので、rubyをhttpsで取ってこれるハズ!

2.4 rubyインストールの再実行

結構時間かかりましたが無事に終了しました。

$ rbenv install 2.5.0 -v
ruby-build: use openssl from homebrew
/var/folders/cq/lhcj5t515fg5smk__8r_nwj00000gn/T/ruby-build.20190820113501.15161 ~/development/20190819
Downloading ruby-2.5.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.bz2
/var/folders/cq/lhcj5t515fg5smk__8r_nwj00000gn/T/ruby-build.20190820113501.15161/ruby-2.5.0 /var/folders/cq/lhcj5t515fg5smk__8r_nwj00000gn/T/ruby-build.20190820113501.15161 ~/development/20190819
Installing ruby-2.5.0...

・・・省略・・・

Installed ruby-2.5.0 to /Users/hoge/.rbenv/versions/2.5.0
$

rubyのバージョンも変えて起きましょうね、と。

$ rbenv local 2.5.0
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]
$ which ruby
/Users/hoge/.rbenv/shims/ruby

3.gemのアップデート

しばらく使ってなかったのでアップデート。

$ sudo gem update --system
Password:
Updating rubygems-update
Fetching: rubygems-update-3.0.6.gem (100%)

・・・省略・・・

If you do not wish to install this documentation in the future, use the
--no-document flag, or set it as the default in your ~/.gemrc file. See
'gem help env' for details.

RubyGems system software updated
$

4.cocoaPodsのインストール

きました!今回の目的!!

$ sudo gem install cocoapods
Fetching concurrent-ruby-1.1.5.gem
Fetching thread_safe-0.3.6.gem

・・・省略・・・

Done installing documentation for thread_safe, tzinfo, concurrent-ruby, i18n, activesupport, nap, fuzzy_match, cocoapods-core, claide, cocoapods-deintegrate, cocoapods-downloader, cocoapods-plugins, cocoapods-search, cocoapods-stats, netrc, cocoapods-trunk, cocoapods-try, molinillo, atomos, CFPropertyList, colored2, nanaimo, xcodeproj, escape, fourflusher, gh_inspector, ruby-macho, cocoapods after 22 seconds
28 gems installed
$ pod --version
1.7.5

順調順調。
さてあとは $ pod setupさえ実行できちゃえばと思い実行・・・したんですが、終わらない。。。

そしたら他の方もハマっているようで、参考にさせていただきました。
https://qiita.com/pei0804/items/be16a07db87c21703693

4.1 pod setupの短縮化

上記を参考にさせていただき、以下を実行しました。

$ cd ~/.cocoapods/repos
$ rm -rf master
$ git config --global http.postBuffer 524288000
$ git clone https://github.com/CocoaPods/Specs.git master
$ pod setup

5.pod install の実行

$ pod deintegrate
$ pod install

これで全て終了。
iOSアプリもビルドが通り、無事に立ち上がりました。

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

Could not get GOOGLE_APP_ID in Google Services file from build environmentが出るときの解決法

もしかして: Xcodegenをお使いではありませんか? もしくは、Crashlyticsのスクリプトをかなり序盤で使っていませんか?

原因

Fabric/runは、cocoapodのスクリプトの後に走らせないといけないところを、xcodegenでは必ず最後にcocoapodsのスクリプトを最後に走らせる暗黙の仕様があるので、問題が発生している。

解決方法

https://qiita.com/gin0606/items/3935027fef9597ddf6e7

以上の記事にも書いてあるが、こんな感じでPodfile内にbuild phaseスクリプトを書けるドンピシャの機能があるので、これで実行順序を保証しつつ解決できる。もちろんXcodegenの機能で解決できるのが一番良いので、本体のGitHubにPR出すのが良さそう。

Podfile
target 'Project' do
    pod 'Fabric', '~> 1.10.2'
    pod 'Crashlytics', '~> 3.13.3'

    script_phase :name=> 'Fabric',
                 :script=> '"${PODS_ROOT}/Fabric/run"',
                 :input_files=> ['$SRCROOT/$PRODUCT_NAME/$(INFOPLIST_PATH)']
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リリース済みアプリの iOS Distribution Certificate Expires in 30 Days 対応

概要

  • リリース済みの iOS アプリで使用している証明書の期限が切れそうなので更新する
  • 別の Mac でも同じ iOS アプリを開発するための証明書登録を行う

発端

Apple Developer から「Action Needed: iOS Distribution Certificate Expires in 30 Days」というタイトルのメールが届く。

Your iOS Distribution Certificate will no longer be valid in 30 days. To generate a new certificate, sign in and visit Certificates, Identifiers & Profiles.

iOS Distribution Certificate の期限があと30日で切れるので更新する必要があるとのこと。

証明書 iOS Distribution Certificate を更新する

対応の流れ

  1. macOS で CertificateSigningRequest.certSigningRequest を生成する
  2. Apple Developer で Certificate を生成する
  3. macOS で Certificate をキーチェーンに登録する
  4. Apple Developer で Provisioning Profile に Certificate を設定する

macOS で CertificateSigningRequest.certSigningRequest を生成する

macOS で キーチェーンアクセス (Keychain Access.app) を起動する。

ios-distribution-certificate-01.png

メニューから [キーチェーンアクセス] → [証明書アシスタント] → [認証局に証明書を要求] を選択。

ios-distribution-certificate-02.png

証明書情報を入力する。

  • ユーザのメールアドレス: Apple Developer に登録した Apple ID のメールアドレス
  • 通称: キーチェーンアクセス等に表示される通称 (何でも良い)
  • CAのメールアドレス: (空のままで)
  • 要求の処理: ディスクに保存
  • 鍵ペア情報を指定: チェックを入れる

ios-distribution-certificate-03.png

「続ける」をクリックすると、ダイアログが表示されるので、ファイルの保存場所を指定する。

鍵ペア情報を入力する。

  • 鍵のサイズ: 2048ビット
  • アルゴリズム: RSA

ios-distribution-certificate-04.png

先ほど指定したファイルの保存場所に Certificate Signing Request (CSR) のファイル CertificateSigningRequest.certSigningRequest が保存される。

Apple Developer で Certificate を生成する

Apple Developer にログインして Certificates のページへ移動する。

Certificates の右側にある「+」ボタンをクリックする。

ios-distribution-certificate-05.png

iOS Distribution (App Store and Ad Hoc) を選択し、「Continue」をクリックする。

ios-distribution-certificate-06.png

「Choose File」をクリックして、先ほど生成した CertificateSigningRequest.certSigningRequest ファイルを選択し、「Continue」をクリックする。

ios-distribution-certificate-07.png

「Download」ボタンを押して証明書ファイル ios_distribution.cer をダウンロードする。

ios-distribution-certificate-08.png

このページにはうっすらした色で英文の説明が載っている(上の画像には載っていない)。

Download your certificate to your Mac, then double click the .cer file to install in Keychain Access. Make sure to save a backup copy of your private and public keys somewhere secure.

macOS で Certificate をキーチェーンに登録する

macOS で キーチェーンアクセス (Keychain Access.app) を起動する。

キーチェーンアクセスのメニューから [ファイル] → [読み込む] でダウンロードした証明書ファイル ios_distribution.cer を読み込む。

ios-distribution-certificate-09.png

あるいは証明書ファイル ios_distribution.cer をダブルクリックする。

ios-distribution-certificate-10.png

これでキーチェーンに証明書を追加できる。

Apple Developer で Provisioning Profile に Certificate を設定する

Apple Developer にログインして Profiles のページへ移動する。

該当する Provisioning Profile をクリックする。

ios-distribution-certificate-11.png

「Edit」をクリックする。

ios-distribution-certificate-12.png

Certificates から先ほど生成した Certificate を選択して、「Save」をクリックする。

ios-distribution-certificate-13.png

「Download」をクリックして Provisioning Profile をダウンロードする。

ios-distribution-certificate-14.png

別の Mac に証明書 iOS Distribution Certificate を登録する

対応の流れ

  1. Certificate 登録済みの Mac で個人情報交換ファイル p12 を書き出す
  2. 別の Mac で Certificate をキーチェーンに登録する

Certificate 登録済みの Mac で個人情報交換ファイル p12 を書き出す

Certificate 登録済みの Mac で キーチェーンアクセス (Keychain Access.app) を起動する。

ウィンドウ左上の「キーチェーン」にて「ログイン」を選択。ウィンドウ左下の「分類」にて「自分の証明書」を選択。これを選択しておかないと、ファイルを書き出す際に個人情報交換ファイル p12 ファイルがグレーアウトして選択できないことがある。

p12-1.png

キーチェーンアクセスに登録した Certificate を右クリックして、iPhone Distribution を書き出す。

p12-2.png

ファイル名を指定して p12 ファイルを保存する。
その際、この p12 ファイルを使うためのパスワードを設定する。

p12-3.png

別の Mac で Certificate をキーチェーンに登録する

別の Mac の Xcode のメニューから [Xcode] → [Preferences] → [Accounts] → [Manage Certificates] を選択すると、該当の iOS Disutribution Certificate に 「Not in Keychain」 や 「Missing Private Key」 と表示されている状況を解決する。

p12-4.png

p12 ファイルをダブルクリックして、設定したパスワードを入力する。

p12-5.png

キーチェーンアクセスで確認すると、秘密鍵が登録されているのを確認できる。これでこの Mac でも該当の iOS アプリを開発できる。

p12-6.png

参考資料

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

iOS13でUISegmentedControlの見た目をカスタマイズする

※ 実装/動作確認環境: Xcode 11 beta5 + Simulator
※※ iOS13正式リリース前につき、スクリーンショットは未掲載(100%見た目についての話なのでなんとも物足りない感じですが…)。

背景

iOS13ではUISegmentedControlの見た目が変更される。
それに伴い、見た目(主に色)のカスタマイズをする場合、iOS12以前とは異なる方法で行う必要がある。

さしあたりの対応として行ったことをメモ。
(DarkMode対応なども鑑みるとカスタマイズなしで使うようにUIを設計し直すのが妥当とは思うが…)

iOS12以前

tintColorがビューやラベルの色に適用されるため、ここに任意の色を指定するだけでOKだった。

tintColor = UIColor.orange

iOS13

デフォルトの表示が無彩色となり、tintColorに依存しなくなった。

以下の方法でそれなりにカスタマイズできる。しかしそれぞれ一長一短あり。

selectedSegmentTintColor を使用する方法

tintColor ではなく selectedSegmentTintColor を指定することで、選択中のセグメントの背景色を設定できる。
また、文字色はデフォルトで黒色であるため、設定する背景色によっては文字色も変更したほうがベター。

func configure() {
  let color = UIColor.orange

  selectedSegmentTintColor = color
  setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: .selected)
  setTitleTextAttributes([NSAttributedString.Key.foregroundColor: color], for: .normal)
}
メリット

コード量も少なく、やっていることも明確である点。
基本的にはこの程度のカスタマイズで収めたい。

デメリット

この方法ではビューの背景にある薄いグレーはそのままなので、selectedSegmentTintColor の明度によってはコントラストが弱くなる恐れがある。
(View Hierarchyを確認したところ、SegmentControlのsubview内に半透明のグレー画像が設定されたUIImageViewがある模様)

コントラストに懸念があり、かつ色も変更できない場合は次の方法で薄いグレーの背景を除去するのがよさそう(ただし急場しのぎ。詳細は後述)。

setBackgroundImage() を使用する方法

func configure() {
  let color = UIColor.orange

  // 背景を単色画像に差し替え
  setBackgroundImage(UIColor.clear.toImage(), for: .normal, barMetrics: .default)
  setBackgroundImage(color.toImage(), for: .selected, barMetrics: .default)

  // ラベルのスタイルを設定(選択中のセグメントのラベルがBoldでなくなるので、合わせて設定)
  setTitleTextAttributes(
    [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 13.0),
     NSAttributedString.Key.foregroundColor: UIColor.white],
    for: .selected)
  setTitleTextAttributes([NSAttributedString.Key.foregroundColor: color], for: .normal)

 // グレー領域がなくなったことで境界がわかりづらくなるので、ボーダーを追加
 layer.borderColor = color.cgColor
 layer.borderWidth = 1.0
}

↑で使用している UIColor.toImage() は1x1の単色UIImageを生成する拡張メソッド。実装方法はこちらの記事等を参照。

メリット

背景のグレーがなくなるので、SegmentedControlの色をより自由に決められる点。

デメリット

ビューの構造も変わってしまうらしく、見た目はiOS12以前のSegmentedControlのようになってしまう。

また、たかだか6ステップ程度ではあるが、先の方法よりは回りくどい。
そうまでしてiOS12以前っぽい見た目にするのか?となるので、やはりiOS13標準に則ったUIに変更するのが真っ当だと思う。

参考

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

textのAttributedStringをClearする

AttributedStringは、文字に背景色を付けたりする際に用います。

AttributedStringはlabelに付けれるだけでなく、
TextViewやTextFieldなどの入力でも利用できます。

この 「AttributedStringをClearしたい」 とき、
例えば 「TextView Edit中は背景色を透明に戻したい」 とき、
処理を実現するのに少し苦労したので、備忘録として記載します。

TextViewの文字背景色を黄色に

まず、TextViewの文字背景色を黄色にするコード。

@IBOutlet weak var textView: TextView!

func fillIn(value: String) {
    let attributes: [NSAttributedString.Key: Any] = [
        .backgroundColor: UIColor.yellow,
        .font: UIFont.systemFont(ofSize: 20.0)
    ]
    textView.attributedText = NSAttributedString(string: value,
                                                 attributes: attributes)
}

これでtextViewに値をFillInした際に、文字背景色が黄色になります。

TextViewの文字背景色を透明に戻す

AttributedStringをClearする、みたいな方法は見つけられませんでした。
そこで .backgroundColor: UIColor.clear を設定した別のAttributedString
を用いることで、Clearを実現します。

「textViewの編集を開始したタイミング」でClearをします。
textViewのDelegateを設定しつつ、

textView.delegate = self

テキスト入力を始めた時 textViewDidBeginEditing に処理を書きます。

extension ViewController: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        let attributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: UIColor.black,
            .backgroundColor: UIColor.clear,
            .font : UIFont.systemFont(ofSize: 20.0)
        ]
        textView.attributedText = NSAttributedString(string: textView.text ?? "",
                                                     attributes: attributes)
    }
}

背景色を透明に設定したAttributedStringを利用することで、
文字背景色を透明に戻します。

これで目的達成!!

と思いきや!文字を追加してみたら

...目的達成ならず :cry:

他の設定が必要。 typingattributes なるものが。
https://developer.apple.com/documentation/uikit/uitextview/1618629-typingattributes

既に入力されたテキストだけでなく、
これから入力するテキストについても設定を追加する必要でした。

extension ViewController: UITextViewDelegate {
    func textViewDidBeginEditing(_ textView: UITextView) {
        let attributes: [NSAttributedString.Key: Any] = [
            .foregroundColor: UIColor.black,
            .backgroundColor: UIColor.clear,
            .font : UIFont.systemFont(ofSize: 20.0)
        ]
        textView.attributedText = NSAttributedString(string: textView.text ?? "",
                                                     attributes: attributes)
        textView.typingAttributes = attributes // New!!
    }
}

これで無事、目的達成。

まとめ

他に方法を知っている方がいればご教示頂けると幸いですm(__)m

参考

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

iOSで遅延処理

iOS(Swift4)以上で遅延処理を行う場合についてのまとめ。

単純に処理を遅延させたい

DispatchQueueのasyncAfterを利用。

DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    // 2.0秒後に実行したい処理
}

アニメーションが絡む処理を遅延させたい

UIView.animateを利用。

UIView.animate(withDuration: 1.0, delay: 2.0, options: .autoreverse, animations: {
    // 2.0秒後に実行したいアニメーション処理
}, completion: { _ in
    // アニメーション終了後に実行したい処理
})
  • withDuration: アニメーションの実行時間
  • delay: 遅延時間
  • option: アニメーション動作の種類 UIView.AnimationOptions
  • animations: アニメーション処理
  • completion: アニメーション完了後処理 (nilでもOK)

※UIView.AnimationOptionsの種類:
https://developer.apple.com/documentation/uikit/uiview/animationoptions

参考

公式ドキュメント

参考サイト

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