2015-06-19 7 views
8

Im Moment bin ich einige Übergänge erstellen und transformieren über CGAffineTransform für eine Schwenkansicht und ich laufe in Schwierigkeiten wegen der Transformationsleistung unter iOS 7 und iPhone 4.CGAffineTransforms Leistung ist wirklich langsam auf iOS 7

Ich tauchte in Istruments und protokollierte das Zeug und das schwere Heben ist getan, wenn ich meine Transformationen auf die Ansicht anwende.

aktuelle Implementierung

func handlePan(recognizer : UIPanGestureRecognizer) { 

     let drawerLocation = recognizer.locationInView(drawerView!) 
     let locationInView = recognizer.locationInView(containerView!) 
     let progressMax = containerView!.frame.height - 40 - 20 

     if(recognizer.state == .Changed) { 

      let offsetDrag = dragStartPosition.y - locationInView.y 
      let progress = Float(offsetDrag/progressMax) 

      if(offsetDrag >= 0) { 

       let positionTransform = CGAffineTransformMakeTranslation(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(normalizedProgress))) 
       viewWithTransform.transform = positionTransform // really bad performance here 
      } else { 
       // reset the transition 
      }  
    } 
} 

Abhilfe für iOS 7

func handlePan(recognizer : UIPanGestureRecognizer) { 

     let drawerLocation = recognizer.locationInView(drawerView!) 
     let locationInView = recognizer.locationInView(containerView!) 
     let progressMax = containerView!.frame.height - 40 - 20 

     if(recognizer.state == .Changed) { 

      let offsetDrag = dragStartPosition.y - locationInView.y 
      let progress = Float(offsetDrag/progressMax) 

      if(offsetDrag >= 0) { 
       if UIDevice.currentDevice().systemMajorVersion() > 7 { 
        let positionTransform = CGAffineTransformMakeTranslation(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(progress))) 
        viewWithTransform.transform = positionTransform // really bad performance here 
       } else { 
        viewWithTransform.frame = CGRectMake(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(progress)), drawerView!.frame.size.width, drawerView!.frame.size.height); // works like a charm on iOS 7 
       } 

      } else { 
       // reset the transition 
      }  
    } 
} 

Frage

Warum ist die Leistung so schlecht auf iOS 7 und mein iPhone 4 mit CGAffineTransforms ? Weil es die gleiche Sache mit dem Offset tut, dann die Rahmeneinstellung in der Problemumgehung. Wenn ich UIView.animateWithDuration() mit transformiere, läuft es auf 60fps. Was kann ich nicht tun, um die gesamte Implementierung auf meiner iOS 7-Basis neu zu schreiben?

UPDATE 28. Juli herausgefunden, dass AutoLayout möglicherweise in diesem Problem beteiligt ist. Hier ist ein TimeProfiler Stapel von meinen aktuellen Anrufen:

Time Profiler

Jetzt bin ich ein großes Problem in meiner aktuellen Implementierung konfrontiert, weil ich auf Automatisches Layout verlassen. Was ist die einfachste Lösung, um dieses Problem auf iOS 7 zu lösen?

+0

Ist 'viewWithTransform.translate =' etwas Brauchbares oder soll es 'viewWithTransform.transform =' sein? Verwenden Sie (absichtlich oder unabsichtlich) Auto Layout? Profilieren Sie mit Release-Builds? –

Antwort

4

Während Sie Recht haben, dass sie das gleiche tun, unter der Haube, ist es nicht so einfach - es gibt Matrixmultiplikationen, die überall hin gehen. More on that can be found here.

Es ist seltsam, dass wenn Sie genau das tun, es Ihre Leistung beeinflusst - aber ich denke, Ihr Layout ist kompliziert und so dauert das erneute Rendern eine Menge Zeit; Ich hatte eigentlich das gleiche Problem wie vor einer Woche, hier so ist das, was mir geholfen:

  • Automatisches Layout aus irgendeinem Grund aus der jeweiligen Ansicht zu entfernen hilft viel
  • Manipulation mit Rahmen verwendet werden sollten, wenn Sie nicht drehen/Skalierungsansicht. Es wird wirklich notwendig, wenn Sie diese verwenden.
  • Schatten zu entfernen und halbtransparente Ansichten, vor allem, wenn es mehr von ihnen hintereinander, fällt wirklich die FPS, wenn sie

Auch verwendet Transformations, können Sie versuchen, dass in asynchronem Aufruf zuweisen zu transformieren und sehen wenn das hilft:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {() -> Void in 
     dispatch_async(dispatch_get_main_queue(), {() -> Void in 
      // Transform view 
     }) 
    }) 

Und wenn Sie wirklich sein Phantasie möchten, verwenden Sie POP Framework from Facebook. Es ist großartig für das, was Sie wollen, und es erlaubt Ihnen, einige ausgefallene Sachen wie Bounce zu tun, Sprungkraft usw. Hier ist, wie Sie es verwenden können:

// Create spring animation (there are more types, if you want) 
let animation = POPSpringAnimation(propertyNamed: kPOPViewCenter) 

// Configure it properly 
animation.autoreverses = false 
animation.removedOnCompletion = true 
animation.fromValue = view.center 
animation.toValue = finalPositionPoint 

// Add new animation to your view - animation key can be whatever you like, serves as reference 
view.pop_addAnimation(animation, forKey: "YourAnimationKey") 

Edit: Wenn Sie gerade Ansicht bewegt werden um die Verwendung .zentrale Eigenschaft, nicht der Rahmen. Es spart Ihnen die Notwendigkeit, Höhe/Breite wieder zu definieren und gibt Ihnen eine klarere Vorstellung von Ihren Absichten.

+0

Ich versuche die dispatch_async Sache. Es ist wirklich einfach. Ich bin auf der Hauptwarteschlange und sende die Transformation zum nächsten Runloop in der Hauptwarteschlange. Klingt einfach. Ich kenne POP von Facebook, aber gibt es die Möglichkeit, dies auf einer Pan-Geste zu verwenden? Ich meine, es heißt viel;) – mariusLAN

+0

Für Pan Geste, schlage ich vor, Sie erstellen eine Feder Animation für Ihr Element und setzen Eigenschaft removeOnCompletion = false. Auf diese Weise wird es unabhängig davon funktionieren, selbst nachdem es seinen "toValue" erreicht hat. Wenn Sie schwenken, ändern Sie einfach .toValue und es beginnt wieder zu animieren. –

+0

@mariusLAN Haben Sie Ihr Problem mit meiner Lösung gelöst? –