スマート反転モード(Smart Invert)への対応について

iOS 11からスマート反転モードが搭載されました。アプリ側で対応させるには、反転させたくない写真や映像などのViewに対してaccessibilityIgnoresInvertColorsを設定すれば良いのですが、UISliderだけはデフォルトで反転しないようです。

UISliderの内部のsubviewに対してaccessibilityIgnoresInvertColors=NOを設定しても反転してくれません。取り合えず、つまみはそのままで、minimumとmaximumのtrackImage(or tintColor)を入れ替えると反転しても少しマシに見えます。暫定措置

// Subclass of UISlider
if (UIAccessibilityIsInvertColorsEnabled()) {
    UIImage *minImage = [self minimumTrackImageForState:UIControlStateNormal];
    UIImage *maxImage = [self maximumTrackImageForState:UIControlStateNormal];
    [self setMinimumTrackImage:maxImage forState:UIControlStateNormal];
    [self setMaximumTrackImage:minImage forState:UIControlStateNormal];
}

High volume warning

スライダーは様々な使い方ができますが、ボリューム用途に限っては、どうもヨーロッパでは音量を大きくした場合に右側を黄色点滅にして、聴覚への悪影響を警告しなければいけない決まりがあるそうです。黄色が指定されていて反転させられないようです。日本独自のカメラのシャッター音みたいなもんでしょうか。アップルのエンジニアは大変だわ。

iPhoneを売却する前に必要な作業

iPhoneを売却するには単純にデータを消しておくだけではダメで「初期化されたアクティベート済み端末」にしておく必要があります。面倒そうですが、本体のみで簡単にできますのでご紹介しましょう。

1. iPhoneを探すをオフにしてアカウントからログアウト

「iPhoneを探す」によって、端末が盗まれてしまった場合でも他人が勝手に初期化できないようになっていますので、売却前には必ず無効にしておきます。店舗買取では事前に教えてもらえますが、そのままオークションに出品してしまうとまずいです。

2. 必要ならPCでバックアップをとる

3. すべてのコンテンツと設定を消去

設定 → 一般 → リセット → すべてのコンテンツと設定を消去
確認のパスコードを入力すると、工場出荷状態に初期化されます。

パソコンを使って初期化した場合は、iOSは強制的に最新バージョンにアップデートされます。古いiOSのまま売却したい方は端末で初期化しましょう。

4. ネットワークにつないでアクティベート

初期化してもアクティベートだけは済ませておかないと、店舗では買い取ってもらえません。また、オークションでも落札者がすぐに動作確認できるようにアクティベートだけは済ませておく方が親切かと思います。

5. SIMを抜く

6. ネットワーク設定をリセット

設定 → 一般 → リセット → ネットワーク設定をリセット

これでもう安心です。個人情報が入っていない、初期化されたアクティベート済み端末が出来上がりました。お疲れ様でした。

意外?NSTimerの正しい使い方

iOSでタイマーを使う時、強い参照にしてる方って結構多いんじゃないでしょうか?私もそうだったんですが、先日タイマー関連を見直していて弱参照が使える事に気付きました。

自動的にcurrentRunLoop(for DefaultRunLoopMode)に追加されるscheduledTimerは、すでに強参照で保持されているので、弱参照にしておけばinvalidateで取り除かれた時に即解放となり、nil代入が不要になります。

@property (nonatomic, weak) NSTimer *timer;

- (void)startTimer {
    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(update) userInfo:nil repeats:YES];
    }
}

- (void)stopTimer {
    if (self.timer != nil) {
        [self.timer invalidate];
    }
}

Appleのサンプルでもweakを使っていましたが、assignを指定している方もおられますね。

Swift 3.0なら更に短く書けます。

private weak var timer: Timer?

func startTimer() {
    if timer == nil {
        timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(update), userInfo: nil, repeats: true)
    }
}

func stopTimer() {
    timer?.invalidate()
}

余談

NSTimerに似ているCADisplayLinkはどうなのか検証したところ、実機とシミュレータで異なった結果になりました。

@property (nonatomic, weak) CADisplayLink *link;

self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];

// 実機 iOS 10.0
// self.link is retained

// シミュレータ Xcode 8.3.3
// self.link is null

CADisplayLinkはaddToRunLoopしてから使うでしょうから、strongにしておいて自力で解放する方が良さそうです。

@property (nonatomic) CADisplayLink *link;

- (void)startDisplayLink {
    if (self.link == nil) {
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
        [self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
}

- (void)stopDisplayLink {
    if (self.link != nil) {
        [self.link invalidate];
        self.link = nil;
    }
}