2011-03-09 16 views
13

UIViews'den oluşan ve iPad'de bir 3D Atlı Karınca ürününü uygulamaya çalışıyorum; bu, here üzerinde gösterilen gibi bir efekt.iPad'de 3D Atlıkarınca efekti

SO'yla ilgili birçok benzer soruyu inceledim, ancak hiç cevap vermedi ya da hiç cevap bulamadı.

Kapak akışı animasyonunu değiştirerek efekti elde etmeye çalışıyorum ancak bu kaygan efekti vermiyor.

kimse bulanıklığı yukarıdaki sakıncası varsayarak

cevap

65

Kuvars veya OpenGL dalmak gerek yok (kuvars ve openGL hem yoluyla öneriler için açık) bu? Uyguladı. Bağladığınız sayfa perspektifi yanlış yapar (bu yüzden arka plandaki görüntüler ön plandakilerden daha hızlı hareket eder), bu yüzden matematik biraz duman ve ayna olabilir.

Altta tam örnek kod var. Yaptığım şey, hakkında bazı görüşleri taşımak için sinüs ve kosinüs kullanıldı. Bunun arkasındaki temel teori, orijin üzerinde konumlandırılan bir yarıçapın (r) dış tarafında (a * sin (r), a * cos (r)) a noktasındaki açıdır. Bu, Kartezyen dönüşüm için basit bir kutuptur ve çoğu ülkenin gençlerine öğrettiği trigonometriden açıkça anlaşılmalıdır; bir hipotenüs uzunluğu ile dik açılı bir üçgeni düşünün a - diğer iki taraf hangi uzunluklardadır?

Daha sonra yapabileceğiniz şey, daireyi bir elipse dönüştürmek için y parçasının yarıçapını azaltmaktır. Ve bir elips, bir açıdan baktığınız bir daireye benziyor. Bu perspektif olasılığını göz ardı ediyor, ama onunla git.

Y koordinatına orantılı bir boyut kazandırarak perspektifi sahte yapıyorum. Ayrıca, aldığınız sitenin bulanıklaştırma yaptığı gibi alfa ayarlıyorum. Umarım uygulamalarınız için yeterlidir.

Değiştirmek istediğim UIView'lerin afin dönüşümünü ayarlayarak konumu ve ölçeği etkilerim. Alfa’yı doğrudan UIView’e ayarlıyorum. Ayrıca, görünüm katmanlarında zPosition'ı da ayarlıyorum (bu yüzden QuartzCore içe aktarılıyor). ZPpozisyon CSS z pozisyonu gibidir; ölçek etkilemez, sadece çizim sırası. Bu yüzden, hesapladığım ölçeğe eşit olarak ayarlayarak, sadece "küçük şeylerin üstünde daha büyük şeyler çizin" diyor, bize doğru çizim düzenini veriyor.

Parmak izlemesi, bir seferde bir UITouch takip edilerek yapılır: Dokuz/dokunuşlarıMoved/touchEnded döngüsü. Parmak izlenmezse ve bazı dokunuşlar başlarsa, bunlardan biri izlenen parmak olur. Hareket ederse, atlı karınca döndürülür.

Atalet oluşturmak için, bir zamanlayıcıya eklenen bir yöntem daha önce işaretlenmiş olan açıya karşı mevcut açıyı takip eder. Bu fark hız gibi kullanılır ve aynı anda atalet üretmek için aşağı doğru ölçeklendirilir.

Zamanlayıcı, parmak ucunda başlatılır, çünkü karusel kendi isteğinin dönüşüne başlamalıdır. Atlıkarınca durma noktasına gelirse veya yeni bir parmak aşağıya yerleştirilirse durdurulur. Boşlukları doldurmak için bırakmak

, benim kodudur:

#import <QuartzCore/QuartzCore.h> 

@implementation testCarouselViewController 

- (void)setCarouselAngle:(float)angle 
{ 
    // we want to step around the outside of a circle in 
    // linear steps; work out the distance from one step 
    // to the next 
    float angleToAdd = 360.0f/[carouselViews count]; 

    // apply positions to all carousel views 
    for(UIView *view in carouselViews) 
    { 
     float angleInRadians = angle * M_PI/180.0f; 

     // get a location based on the angle 
     float xPosition = (self.view.bounds.size.width * 0.5f) + 100.0f * sinf(angleInRadians); 
     float yPosition = (self.view.bounds.size.height * 0.5f) + 30.0f * cosf(angleInRadians); 

     // get a scale too; effectively we have: 
     // 
     // 0.75f the minimum scale 
     // 0.25f the amount by which the scale varies over half a circle 
     // 
     // so this will give scales between 0.75 and 1.25. Adjust to suit! 
     float scale = 0.75f + 0.25f * (cosf(angleInRadians) + 1.0); 

     // apply location and scale 
     view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(xPosition, yPosition), scale, scale); 

     // tweak alpha using the same system as applied for scale, this time 
     // with 0.3 the minimum and a semicircle range of 0.5 
     view.alpha = 0.3f + 0.5f * (cosf(angleInRadians) + 1.0); 

     // setting the z position on the layer has the effect of setting the 
     // draw order, without having to reorder our list of subviews 
     view.layer.zPosition = scale; 

     // work out what the next angle is going to be 
     angle += angleToAdd; 
    } 
} 

- (void)animateAngle 
{ 
    // work out the difference between the current angle and 
    // the last one, and add that again but made a bit smaller. 
    // This gives us inertial scrolling. 
    float angleNow = currentAngle; 
    currentAngle += (currentAngle - lastAngle) * 0.97f; 
    lastAngle = angleNow; 

    // push the new angle into the carousel 
    [self setCarouselAngle:currentAngle]; 

    // if the last angle and the current one are now 
    // really similar then cancel the animation timer 
    if(fabsf(lastAngle - currentAngle) < 0.001) 
    { 
     [animationTimer invalidate]; 
     animationTimer = nil; 
    } 
} 

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. 
- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // create views that are an 80x80 rect, centred on (0, 0) 
    CGRect frameForViews = CGRectMake(-40, -40, 80, 80); 

    // create six views, each with a different colour. 
    carouselViews = [[NSMutableArray alloc] initWithCapacity:6]; 
    int c = 6; 
    while(c--) 
    { 
     UIView *view = [[UIView alloc] initWithFrame:frameForViews]; 

     // We don't really care what the colours are as long as they're different, 
     // so just do anything 
     view.backgroundColor = [UIColor colorWithRed:(c&4) ? 1.0 : 0.0 green:(c&2) ? 1.0 : 0.0 blue:(c&1) ? 1.0 : 0.0 alpha:1.0]; 

     // make the view visible, also add it to our array of carousel views 
     [carouselViews addObject:view]; 
     [self.view addSubview:view]; 
    } 

    currentAngle = lastAngle = 0.0f; 
    [self setCarouselAngle:currentAngle]; 

    /* 
     Note: I've omitted viewDidUnload for brevity; remember to implement one and 
     clean up after all the objects created here 
    */ 
} 

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    // if we're not already tracking a touch then... 
    if(!trackingTouch) 
    { 
     // ... track any of the new touches, we don't care which ... 
     trackingTouch = [touches anyObject]; 

     // ... and cancel any animation that may be ongoing 
     [animationTimer invalidate]; 
     animationTimer = nil; 
     lastAngle = currentAngle; 
    } 
} 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    // if our touch moved then... 
    if([touches containsObject:trackingTouch]) 
    { 
     // use the movement of the touch to decide 
     // how much to rotate the carousel 
     CGPoint locationNow = [trackingTouch locationInView:self.view]; 
     CGPoint locationThen = [trackingTouch previousLocationInView:self.view]; 

     lastAngle = currentAngle; 
     currentAngle += (locationNow.x - locationThen.x) * 180.0f/self.view.bounds.size.width; 
     // the 180.0f/self.view.bounds.size.width just says "let a full width of my view 
     // be a 180 degree rotation" 

     // and update the view positions 
     [self setCarouselAngle:currentAngle]; 
    } 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    // if our touch ended then... 
    if([touches containsObject:trackingTouch]) 
    { 
     // make sure we're no longer tracking it 
     trackingTouch = nil; 

     // and kick off the inertial animation 
     animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(animateAngle) userInfo:nil repeats:YES]; 
    } 
} 

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 
{ 
    // treat cancelled touches exactly like ones that end naturally 
    [self touchesEnded:touches withEvent:event]; 
} 

@end 

Yani ilgili üye değişkenleri bir değişken dizisi olan 'carouselViews', bir zamanlayıcı, 'animationTimer', iki yüzer; 'currentAngle' ve 'lastAngle' ve bir UITouch, 'trackingTouch'. Açıkçası muhtemelen sadece renkli kareler olmayan görünümleri kullanmak isteyebilirsiniz ve konumlandırma için ince havadan çektiğim sayıları ayarlamak isteyebilirsiniz.Aksi takdirde, sadece çalışmalıdır.

DÜZENLEME: Xcode'ta iPhone'un 'Görünüm tabanlı uygulama' şablonunu kullanarak bu kodu yazıp test ettiğimi söylemeliyim. Bu şablonu oluşturun, öğeleri oluşturulmuş görüntü denetleyicisine dökün ve test etmek için gerekli üye değişkenlerini ekleyin. Ancak, dokunmatik izlemenin 180 derece olduğunu düşündüğünüzü fark ettim, ancak setCarouselAngle: yöntemi karuseli her zaman 280 noktaya zorlar (xPosition iki kere 100 çarpanı, artı görünüm). Bu yüzden bir iPad üzerinde çalıştırırsanız parmak izi çok yavaş görünür. Çözüm görünüşte, görünüm genişliğinin 180 derece olduğunu varsaymaz ama bu bir egzersiz olarak bırakılır!

dairesel bir olmak üzere
+0

teşekkürler Tommy! Bunu deneyeceğim. Bu arada sizin için bir +1 var: D – Vin

+1

Bu gerçekten harikaydı. Keşke daha fazla oy verebilirdim! – Vin

+0

Gerçekten güzel bir örnek. Paylaşım için teşekkürler. –