2

プロジェクトの自動テストにUISpecを使用しています。Apple が iOS 6 SDK で xCode 4.5 をリリースするまでは、すべて問題ありませんでした。UITouch クラスのカテゴリにエラーのリストがあるため、UISpec プロジェクトをコンパイルできません。コードは次のとおりです。

//
//  TouchSynthesis.m
//  SelfTesting
//
//  Created by Matt Gallagher on 23/11/08.
//  Copyright 2008 Matt Gallagher. All rights reserved.
//
//  Permission is given to use this source code file, free of charge, in any
//  project, commercial or otherwise, entirely at your risk, with the condition
//  that any redistribution (in part or whole) of source code must retain
//  this copyright and permission notice. Attribution in compiled projects is
//  appreciated but not required.
//

@implementation UITouch (Synthesize)

//
// initInView:phase:
//
// Creats a UITouch, centered on the specified view, in the view's window.
// Sets the phase as specified.
//
- (id)initInView:(UIView *)view
{
    self = [super init];
    if (self != nil)
    {
        CGRect frameInWindow;
        if ([view isKindOfClass:[UIWindow class]])
        {
            frameInWindow = view.frame;
        }
        else
        {
            frameInWindow =
            [view.window convertRect:view.frame fromView:view.superview];
        }

        _tapCount = 1;
        _locationInWindow =
        CGPointMake(
                    frameInWindow.origin.x + 0.5 * frameInWindow.size.width,
                    frameInWindow.origin.y + 0.5 * frameInWindow.size.height);
        _previousLocationInWindow = _locationInWindow;

        UIView *target = [view.window hitTest:_locationInWindow withEvent:nil];

        _window = [view.window retain];
        _view = [target retain];
        _phase = UITouchPhaseBegan;
        _touchFlags._firstTouchForView = 1;
        _touchFlags._isTap = 1;
        _timestamp = [NSDate timeIntervalSinceReferenceDate];
    }
    return self;
}

//
// setPhase:
//
// Setter to allow access to the _phase member.
//
- (void)setPhase:(UITouchPhase)phase
{
    _phase = phase;
    _timestamp = [NSDate timeIntervalSinceReferenceDate];
}

//
// setPhase:
//
// Setter to allow access to the _locationInWindow member.
//
- (void)setLocationInWindow:(CGPoint)location
{
    _previousLocationInWindow = _locationInWindow;
    _locationInWindow = location;
    _timestamp = [NSDate timeIntervalSinceReferenceDate];
}

@end

コンパイラは、「_tapCount = 1;」のような行でエラーを出します。エラーは「不明な型名 "_tapCount"; ということでしたか...?」

私がしたこと:

  1. ランタイム関数「class_copyIvarList」を使用して UITouch クラスのインスタンス変数のリストを取得し、これらの変数がまだ存在するかどうかを確認します。彼らはそうします。
  2. 次のように、実行時に必要な値を変更しようとしました

    uint uPhase = UITouchPhaseBegin; object_setInstanceVariable(self, "_phase", &uPhase);

また

Ivar var = class_getInstanceVariable([self class], "_phase");
object_setIvar(self, var, [NSNumber numberWithInt:UITouchPhaseBegin]);

どちらの場合も、無効な値が「_phase」メンバーに書き込まれました。UITouchインスタンスの説明をコンソールに出力して確認しました。「フェーズ:不明」と書かれていました。

次に、キー値コーディングを試して、次のように実装することにしました。

[self setValue:[NSNumber numberWithInt:1] forKey:@"tapCount"];
[self setValue:[NSNumber numberWithInt:UITouchPhaseBegin] forKey:@"phase"];

今ではいくつかの結果が得られました。コンパイル エラーが解消され、UITouch インスタンスのメンバー値が変更されますが、それでも何も起こりません。適切なUIオブジェクト(私の場合はUIButton)の「touchesBegan:...」および「touchesEnded:...」メソッドにブレークポイントを設定し、デバッグしました。これらのメソッドが呼び出されますが、何も起こりません。ボタン ハンドラは呼び出されません。

また、KVC のために、UITouch カテゴリのメソッド「setPhase」と「setLocationInWindow」にコメントする必要がありました。そうしないと、無限の再帰が発生します。プロパティのセッターは自分自身を呼び出します。

今、私はアイデアがありません。このカテゴリは「2008 年 11 月 23 日に Matt Gallagher によって作成されました」であり、UISpec のサード パーティ コードであることを意味します。UISpec以外の場所で使用され、誰かが回避策を知っていることを願っています。

ありがとう。

PS私はこれがプライベートAPIであることを知っています。プロジェクトのリリース構成には含まれません。自動テストにのみ必要です

4

1 に答える 1

4

UIQuery.m ファイルを変更する必要はありません。次のコード行を UIQuery.h ファイルに追加してください。

#ifdef __IPHONE_6_0
@interface UITouch () {
    NSTimeInterval _timestamp;
    UITouchPhase _phase;
    UITouchPhase _savedPhase;
    NSUInteger _tapCount;

    UIWindow *_window;
    UIView *_view;
    UIView *_gestureView;
    UIView *_warpedIntoView;
    NSMutableArray *_gestureRecognizers;
    NSMutableArray *_forwardingRecord;

    CGPoint _locationInWindow;
    CGPoint _previousLocationInWindow;
    UInt8 _pathIndex;
    UInt8 _pathIdentity;
    float _pathMajorRadius;
    struct {
        unsigned int _firstTouchForView:1;
        unsigned int _isTap:1;
        unsigned int _isDelayed:1;
        unsigned int _sentTouchesEnded:1;
        unsigned int _abandonForwardingRecord:1;
    } _touchFlags;
}
@end
#endif

ありがとう。

于 2012-10-09T20:32:36.623 に答える