Efekt paralaksy iOS 7 w moim kontrolerze widoku

112

Rozwijam aplikację na iOS 7 w Objective-C. Mam ekran w mojej aplikacji z kilkoma przyciskami i ładnym obrazem tła. (To prosty xib z UIButtonsgórną częścią a UIImageView.)

Pomyślałem, że byłoby fajnie, gdyby te przyciski miały efekt paralaksy, który ma ekran główny iOS 7, więc jeśli przechylisz telefon, zobaczysz tło.

Jak mogę zaimplementować ten efekt we własnej aplikacji?

Dan Fabulich
źródło
1
Zajrzyj do tej biblioteki: github.com/Przytua/UIView-MWParallax
CRDave
Szybko
Arvind

Odpowiedzi:

273

W iOS 7 firma Apple wprowadziła UIMotionEffectdodawanie efektów ruchu, które są związane z orientacją urządzenia użytkownika. Na przykład, aby emulować efekt paralaksy na ekranie głównym, możesz użyć podklasy UIInterpolationMotionEffect, jak wyjaśniono tutaj i tutaj , z kilkoma wierszami kodu.

Cel-C :

// Set vertical effect
UIInterpolatingMotionEffect *verticalMotionEffect = 
  [[UIInterpolatingMotionEffect alloc] 
  initWithKeyPath:@"center.y"
             type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
verticalMotionEffect.minimumRelativeValue = @(-10);
verticalMotionEffect.maximumRelativeValue = @(10);

// Set horizontal effect 
UIInterpolatingMotionEffect *horizontalMotionEffect = 
  [[UIInterpolatingMotionEffect alloc] 
  initWithKeyPath:@"center.x"     
             type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
horizontalMotionEffect.minimumRelativeValue = @(-10);
horizontalMotionEffect.maximumRelativeValue = @(10);

// Create group to combine both
UIMotionEffectGroup *group = [UIMotionEffectGroup new];
group.motionEffects = @[horizontalMotionEffect, verticalMotionEffect];

// Add both effects to your view
[myBackgroundView addMotionEffect:group];

Swift (dzięki @Lucas):

// Set vertical effect
let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y",
type: .TiltAlongVerticalAxis)
verticalMotionEffect.minimumRelativeValue = -10
verticalMotionEffect.maximumRelativeValue = 10

// Set horizontal effect
let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x",
    type: .TiltAlongHorizontalAxis)
horizontalMotionEffect.minimumRelativeValue = -10
horizontalMotionEffect.maximumRelativeValue = 10

// Create group to combine both
let group = UIMotionEffectGroup()
group.motionEffects = [horizontalMotionEffect, verticalMotionEffect]

// Add both effects to your view
myBackgroundView.addMotionEffect(group)

Możesz również znaleźć kilka bibliotek, aby to zrobić łatwiej lub dodać tę funkcję do starszych wersji iOS:

veducm
źródło
2
NGAParallaxMotion jest przeznaczone dla systemu iOS 7+, a nie dla starszych wersji iOS. Udostępnia kategorię dla UIView, umożliwiając ustawienie wartości .parallaxIntensity, konfigurując parametry UIMotionEffect za pomocą jednej linii kodu.
Dan Fabulich,
2
Dzięki! Zaktualizowałem odpowiedź, aby uwzględnić obsługiwane wersje i wymagania.
veducm
1
Właściwie chcesz dodać efekt ruchu do widoku tła, a nie widoku z przodu. Ale poza tym ten kod działa świetnie! Dzięki.
gstroup
1
Czy ktoś wie, jak sprawdzić, czy użytkownik wyłączył efekty? stackoverflow.com/questions/19132000/…
Eric
3
@gstroup Właściwie na ekranie głównym porusza się zarówno pierwszy plan, jak i tło. Ale gdybyś wybrał tylko jedną warstwę, aby zastosować efekt paralaksy, zgadzam się, że tło byłoby prawdopodobnie najlepsze.
Rubberduck,
16

Przetłumaczone na szybkie na wypadek, gdyby ktoś był leniwy. Zagłosuj na odpowiedź @veducm , jeśli uznasz to za przydatne

@IBOutlet var background : UIImageView!

func parallaxEffectOnBackground() {
    let relativeMotionValue = 50
    var verticalMotionEffect : UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y",
        type: .TiltAlongVerticalAxis)
    verticalMotionEffect.minimumRelativeValue = -relativeMotionValue
    verticalMotionEffect.maximumRelativeValue = relativeMotionValue

    var horizontalMotionEffect : UIInterpolatingMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x",
        type: .TiltAlongHorizontalAxis)
    horizontalMotionEffect.minimumRelativeValue = -relativeMotionValue
    horizontalMotionEffect.maximumRelativeValue = relativeMotionValue

    var group : UIMotionEffectGroup = UIMotionEffectGroup()
    group.motionEffects = [horizontalMotionEffect, verticalMotionEffect]

    self.background.addMotionEffect(group)
}
Lucas
źródło
Dzięki @Lucas! Zaktualizowałem swoją odpowiedź, aby uwzględnić również szybką próbkę. Opiera się na tym. Zapraszam do przejrzenia go i wprowadzenia potrzebnych zmian.
veducm
7

Rozwiązanie @ veducm może być trochę krótsze. Grupa UIMotionEffectGroup dla jej ruchu xiy jest przestarzała, jeśli dodasz osobno elementy motionEffects na osi xi y.

UIInterpolatingMotionEffect *motionEffect;
motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x"
                                                               type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[bgView addMotionEffect:motionEffect];

motionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y"
                                                               type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
motionEffect.minimumRelativeValue = @(-25);
motionEffect.maximumRelativeValue = @(25);
[bgView addMotionEffect:motionEffect];
buźiaki
źródło
2
Podobnie jak heads-up, jeśli ktoś używa tej skróconej wersji, pierwsze potrzeby ruchu skutku mieć typeod UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxisa nie UIInterpolatingMotionEffectTypeTiltAlongVerticalAxisjak to jest w kodzie określonymi
Flexicoder
1
@BooHoo Dodanie ich jako grupy jest przestarzałe? Gdzie to widzisz? W chwili pisania tego tekstu dokumentacja Apple nie zawiera informacji o tym, że jest przestarzała. Celem ich grupowania jest poprawa wydajności. Wszystkie efekty w grupie są renderowane raz. Kiedy nakładasz je oddzielnie, należy je renderować oddzielnie. Zapraszam do poprawienia mnie, jeśli się mylę (i wskaż mi dokumentację Apple na ten temat).
David Lari
6
const static CGFloat kCustomIOS7MotionEffectExtent = 10.0; 

- (void)applyMotionEffects:(UIView *YOUR_VIEW) {
     if (NSClassFromString(@"UIInterpolatingMotionEffect")) {
         UIInterpolatingMotionEffect *horizontalEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x"
                                                                                                        type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
         horizontalEffect.minimumRelativeValue = @(-kCustomIOS7MotionEffectExtent);
         horizontalEffect.maximumRelativeValue = @( kCustomIOS7MotionEffectExtent);
         UIInterpolatingMotionEffect *verticalEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y"
                                                                                                      type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
         verticalEffect.minimumRelativeValue = @(-kCustomIOS7MotionEffectExtent);
         verticalEffect.maximumRelativeValue = @( kCustomIOS7MotionEffectExtent);
         UIMotionEffectGroup *motionEffectGroup = [[UIMotionEffectGroup alloc] init];
         motionEffectGroup.motionEffects = @[horizontalEffect, verticalEffect]; 
         [YOUR_VIEW addMotionEffect:motionEffectGroup];
     }
}
damithH
źródło
1
@Rob zgodnie z twoją sugestią zaktualizował odpowiedź
damithH
5

Oto prosta kategoria do zintegrowania wpływu na iOs7 +:

NSString *const centerX = @"center.x";
NSString *const centerY = @"center.y";

//#define IS_OS_7_OR_LATER    ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

@implementation UIView (TLMotionEffect)

- (void)addCenterMotionEffectsXYWithOffset:(CGFloat)offset {

//    if(!IS_OS_7_OR_LATER) return;
    if(floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) return;

    UIInterpolatingMotionEffect *xAxis;
    UIInterpolatingMotionEffect *yAxis;

    xAxis = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:centerX type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
    xAxis.maximumRelativeValue = @(offset);
    xAxis.minimumRelativeValue = @(-offset);

    yAxis = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:centerY type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
    yAxis.minimumRelativeValue = @(-offset);
    yAxis.maximumRelativeValue = @(offset);

    UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init];
    group.motionEffects = @[xAxis, yAxis];

    [self addMotionEffect:group];
}

@end

https://github.com/jvenegas/TLMotionEffect

Bejil
źródło
1
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Maciej Swic
Trudno będzie tutaj zintegrować cały projekt github
Bejil
1
Nie musisz przynosić całego projektu, tylko istotną część, tak jak pokazałem, edytując odpowiedź. Dzieje się tak, aby kod pozostawał na wypadek awarii Githuba, ściągnięcia repozytorium lub upadku nieba.
Maciej Swic
3

Pomoże to komuś, kto chce zaimplementować paralaksę dla tableView lub collectionView.

  • przede wszystkim utwórz komórkę dla widoku tabeli i umieść w niej widok obrazu.

  • ustawić wysokość obrazu nieco większą niż wysokość komórki. jeśli wysokość komórki = 160, niech wysokość obrazu wynosi 200 (aby uzyskać efekt paralaksy i można go odpowiednio zmienić)

  • umieść te dwie zmienne w swoim viewController lub dowolnej klasie, w której delegat tableView jest rozszerzony

let imageHeight:CGFloat = 150.0
let OffsetSpeed: CGFloat = 25.0
  • dodaj następujący kod w tej samej klasie
 func scrollViewDidScroll(scrollView: UIScrollView) {
    //  print("inside scroll")

    if let visibleCells = seriesTabelView.visibleCells as? [SeriesTableViewCell] {
        for parallaxCell in visibleCells {
            var yOffset = ((seriesTabelView.contentOffset.y - parallaxCell.frame.origin.y) / imageHeight) * OffsetSpeedTwo
            parallaxCell.offset(CGPointMake(0.0, yOffset))
        }
    }
}
  • gdzie seriesTabelView jest moim UItableview

  • a teraz przejdźmy do komórki tego tableView i dodajmy następujący kod

func offset(offset: CGPoint) {
        posterImage.frame = CGRectOffset(self.posterImage.bounds, offset.x, offset.y)
    }
  • were posterImage jest moim UIImageView

Jeśli chcesz zaimplementować to do collectionView, po prostu zmień tableView vairable na zmienną collectionView

i to wszystko. nie jestem pewien, czy to najlepszy sposób. ale to działa dla mnie. mam nadzieję, że to też działa dla Ciebie. i daj mi znać, jeśli jest jakiś problem

Muneef M
źródło
2

Naprawdę bardzo łatwo to zrobić, dlaczego odsyła złożony kod. po prostu spróbuj. Pracujący

Przyjrzyj się widokowi obrazu dokładnie wielkości widoku obrazu. domyślnie alfa dla widoku zestawu 0.

//MARK: Scroll View Delegate methods
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{

NSLog(@"X: %f Y: %f",scrollView.contentOffset.x,scrollView.contentOffset.y);

CGFloat scrollY = _mainScrollView.contentOffset.y;
CGFloat height = _alphaView.frame.size.height;

CGFloat alphaMonitor = scrollY/height;

_alphaView.alpha = alphaMonitor;
}
Panie Javed Multani
źródło
-1

Szybkie tłumaczenie:

// Set vertical effect
let verticalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.y", type: .TiltAlongVerticalAxis)
verticalMotionEffect.minimumRelativeValue = -value
verticalMotionEffect.maximumRelativeValue = value

// Set vertical effect
let horizontalMotionEffect = UIInterpolatingMotionEffect(keyPath: "center.x", type: .TiltAlongHorizontalAxis)
verticalMotionEffect.minimumRelativeValue = -value
verticalMotionEffect.maximumRelativeValue = value

let group = UIMotionEffectGroup()
group.motionEffects = [horizontalMotionEffect, verticalMotionEffect]
self.motionEffect = group
self.addMotionEffect(group)
mukaissi
źródło