独自のカスタム プロパティと UIKit のプライベート API を組み合わせて活用することをお勧めします。アプリ ストアでの拒否を回避するために、プライベート API はリリース ビルド用にコンパイルする必要があり、実装を確認するためだけに使用する必要があります。
まず、カスタム プロパティを のカテゴリに組み込みましょうUIViewController
。これにより、実装でいくつかの利点が得られ、カスタム View Controller サブクラスからすべてのクラスに戻って派生させる必要はありません。
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
@interface UIViewController (isPresentedInPopover)
@property (assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
@end
実装については、Objective C ランタイムの関連オブジェクト API を使用して、このプロパティのストレージを提供します。セレクターは、コンパイラーによって自動的に一意化され、この目的で他のクライアントによって使用される可能性が非常に低いため、オブジェクトを格納するために使用される一意のキーとして適切な選択であることに注意してください。
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
@implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
@selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, @selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
@end
したがって、これをカテゴリとして使用すると、便利な副作用があります。 を呼び出して、parentViewController
それがポップオーバーにも含まれているかどうかを確認できます。このようにして、たとえば a にプロパティを設定するUINavigationController
と、そのすべての子ビュー コントローラーが に正しく応答しisPresentedInPopover
ます。サブクラスでこれを実現するには、すべての新しい子ビュー コントローラーでこれを設定しようとするか、ナビゲーション コントローラーをサブクラス化するか、その他の恐ろしいことを行う必要があります。
その他のランタイム マジック
Objective C ランタイムがこの特定の問題に対して提供しなければならないことはまだあります。それらを使用して、Apple の非公開実装の詳細にジャンプし、それに対して独自のアプリをチェックできます。リリース ビルドの場合、この余分なコードはコンパイルされるため、ストアに送信する際にサウロンアップルのすべてを見張る目を気にする必要はありません。
with scopeとUIViewController.h
定義された ivar があることがわかります。幸いなことに、これはコンパイラによってのみ強制されます。ランタイムに関する限り、神聖なものは何もなく、どこからでもその ivar にアクセスするのは非常に簡単です。一貫性を保つために、プロパティへのアクセスごとにデバッグのみのランタイム チェックを追加します。UIPopoverController* _popoverController
@package
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
@implementation UIViewController (isPresentedInPopover)
- (void)setPresentedInPopover:(BOOL)presentedInPopover
{
objc_setAssociatedObject(self,
@selector(isPresentedInPopover),
[NSNumber numberWithBool:presentedInPopover],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)isPresentedInPopover
{
NSNumber *wrappedBool = objc_getAssociatedObject(self, @selector(isPresentedInPopover));
BOOL userValue = [wrappedBool boolValue];
#if DEBUG
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
if (userValue != privateAPIValue) {
[NSException raise:NSInternalInconsistencyException format:
@"-[%@ %@] "
"returning %@ "
"while private UIViewController API suggests %@. "
"Did you forget to set 'presentedInPopover'?",
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
userValue ? @"YES" : @"NO",
privateAPIValue ? @"YES" : @"NO"];
}
#endif
return userValue ?: [[self parentViewController] isPresentedInPopover];
}
@end
プロパティを誤って使用すると、コンソールに次のようなメッセージが表示されます。
2012-09-18 14:28:30.375 MyApp[41551:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Consistency error in -[UINavigationController isPresentedInPopover]: returning NO while private UIViewController API suggests YES. Did you forget to set 'presentedInPopover'?'
...しかし、DEBUG フラグをオフまたは 0 に設定してコンパイルすると、以前とまったく同じコードにコンパイルされます。
自由と愚か者のために
アドホック/エンタープライズ/個人向けのビルドを行っているか、Apple が App Store 向けのビルドについてどう考えているかを知る勇気があるかもしれません。いずれにせよ、これは現在のランタイムを使用して機能する実装であり、UIViewController
プロパティを設定する必要はありません!
// UIViewController+isPresentedInPopover.h
#import <UIKit/UIKit.h>
@interface UIViewController (isPresentedInPopover)
@property (readonly, assign, nonatomic, getter = isPresentedInPopover) BOOL presentedInPopover;
@end
// UIViewController+isPresentedInPopover.m
#import "UIViewController+isPresentedInPopover.h"
#import <objc/runtime.h>
@implementation UIViewController (isPresentedInPopover)
- (BOOL)isPresentedInPopover
{
Ivar privatePopoverIvar = class_getInstanceVariable([UIViewController class], "_popoverController");
UIPopoverController *popover = object_getIvar(self, privatePopoverIvar);
BOOL privateAPIValue = popover != nil;
return privateAPIValue ?: [[self parentViewController] isPresentedInPopover];
}
@end