web-dev-qa-db-ja.com

パースビューで平面オブジェクトを中心を中心に回転させる方法は?

私がやろうとしていることは、とても簡単なはずです。古い蓄音機レコードの2Dトップダウンビューを作成しました。 X軸を中心に回転させてから、Z軸を中心に回転させます。

本文にCATransform3Dが含まれるすべての質問を読みました。SteveBakerの "Matrices can be your friends" の記事と、Bill Dudneyの本「Mac OS XのコアアニメーションとiPhone "Brad Larsonの " 3-D Rotation without a trackball " には正しいコードがあると思いますが、ユーザーが3つの軸すべてを調整することを許可しているため、コードを縮小するのに苦労しています1つの次元(回転したz軸)であると私が認識するものに変換します。

これが私がテストしている画像です。詳細が問題にとって重要であるということではありません。

Gold Record

私はそれを通常の方法で画面に表示します:(UIViewのサブクラスで)

- (void)awakeFromNib
{
    UIImage *recordImage = [UIImage imageNamed:@"3DgoldRecord"];
    if (recordImage) {
        recordLayer = [CALayer layer];
        [recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
        [recordLayer setContents:(id)[[UIImage imageNamed:@"3DgoldRecord"] CGImage]];
        [self.layer addSublayer:recordLayer];
    }
}

これが最初のテストで、画面に表示されます;-)

次に、「配置」するには、レイヤーのX軸を中心に回転する変換を適用し、レイヤーのコンテンツを画像に設定した後、サブレイヤーを追加する前にこのコードを挿入します。

    CATransform3D myRotationTransform = 
                    CATransform3DRotate(recordLayer.transform,
                                        (M_PI_2 * 0.85), //experiment with flatness
                                        1.0, // rotate only across the x-axis
                                        0.0, // no y-axis transform
                                        0.0); //  no z-axis transform
    recordLayer.transform = myRotationTransform;        

それは期待通りに機能しました:レコードはうまく戻っています。

次のステップでは、レコードをスピンさせるために、このアニメーションをtouchesEndedイベントに関連付けました。ただし、テスト/学習フェーズを終了すると、この回転はユーザーが制御できなくなります。

CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform = 
                CATransform3DRotate(currentTransform,
                                    1.0, // go all the way around once
                                    (M_PI_2 * 0.85),    // x-axis change
                                    1.00,    // y-axis change ?
                                    0.0);   // z-axis change ?
recordLayer.transform = myRotationTransform;        
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:@"transform.rotation"];

だから私は私がハングアップしているのはCATransform3DRotate呼び出しのベクトルであると確信しています(信頼してください:変更を監視するためにそのベクトルで簡単な変更を試みてきました...今リストされているのは単に最後の変更です私は試した)。私が理解しているように、変換のx、y、zの値は、本質的に、fromValueからtoValueの範囲のアニメーション中に渡される値のパーセンテージです。

私がこれを理解して正しい軌道に乗っている場合、これを単一の変換で表現することは可能ですか?または、アニメーションの有効なフレームごとに、元の直立した画像をz軸を中心に少し回転させてから、結果をx軸回転で下に置く必要がありますか?変換の組み合わせについての質問/回答を見ましたが、それはスケール変換とそれに続く回転変換でした。私は変換をいじくり回しましたが、私が得るべきだと思っていることを実行していません(つまり、1つの変換の結果を次の変換引数に渡すと、1つを完全に実行し、もう1つをアニメーション化するように見えました)。

30
tobinjim

これは、画像ビューのレイヤーの親としてCATransformLayerを使用する場合に最も簡単です。したがって、レイヤーとしてCATransformLayerを使用するカスタムビューサブクラスが必要になります。これは簡単です:

@interface TransformView : UIView

@end

@implementation TransformView

+ (Class)layerClass {
    return [CATransformLayer class];
}

@end

NibにTransformViewを置き、UIImageViewのサブビューとしてTransformViewを追加します。これらのビューを、transformViewおよびdiscViewというビューコントローラのアウトレットに接続します。

ビューコントローラーで、transformViewの変換を設定して遠近法を適用し(m34を設定して)、X軸の傾きを適用します。

- (void)viewDidLoad
{
    [super viewDidLoad];
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = -1 / 500.0;
    transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
    self.transformView.layer.transform = transform;
}

キーパスtransform.rotation.zのアニメーションをviewWillAppear:discViewに追加し、viewDidDisappear:で削除します。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.fromValue = [NSNumber numberWithFloat:0];
    animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
    animation.duration = 1.0;
    animation.repeatCount = HUGE_VALF;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [self.discView.layer addAnimation:animation forKey:@"transform.rotation.z"];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.discView.layer removeAllAnimations];
}

結果:

spinning disc

更新

これはSwift遊び場のデモです:

import UIKit
import XCPlayground

class TransformView: UIView {
    override class func layerClass() -> AnyClass {
        return CATransformLayer.self
    }
}

let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view

let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)

var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform

let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)

let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)

画像を質問からPlaygroundのResourcesフォルダーにコピーし、3DgoldRecord.pngという名前を付けます。結果:

playground result

63
rob mayoff

RecordLayerをスーパーレイヤーでラップすることを検討してください。スーパーレイヤーにx軸回転を適用し、recordLayerにz軸回転アニメーションを追加します。

0
tommyli