Эффект параллакса iOS 7 в моем контроллере представления

112

Я разрабатываю приложение для iOS 7 на Objective-C. В моем приложении есть экран с несколькими кнопками и красивым фоновым изображением. (Это простая xib UIButtonsповерх a UIImageView.)

Я подумал, что было бы здорово, если бы эти кнопки имели эффект параллакса, который есть на домашнем экране iOS 7, поэтому, если вы наклоните телефон, вы можете увидеть фон.

Как я могу реализовать этот эффект в собственном приложении?

Дэн Фабулич
источник
1
Взгляните на эту библиотеку: github.com/Przytua/UIView-MWParallax
CRDave
Для быстроты попробуйте это: github.com/ArvindPrakashJoshi/AJParallaxEffect
Arvind

Ответы:

273

В iOS 7 Apple UIMotionEffectдобавила эффекты движения, связанные с ориентацией устройства пользователя. Например, чтобы имитировать эффект параллакса на главном экране, вы можете использовать подкласс UIInterpolationMotionEffect, как описано здесь и здесь , всего с несколькими строками кода.

Цель-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 (спасибо @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)

Кроме того, вы можете найти множество библиотек, чтобы сделать это проще или добавить эту функцию в более старые версии iOS:

veducm
источник
2
NGAParallaxMotion предназначен для iOS 7+, а не для более старых версий iOS. Он предоставляет категорию для UIView, позволяя вам установить значение .parallaxIntensity, установив параметры UIMotionEffect с помощью одной строки кода.
Дэн Фабулич
2
Спасибо! Я обновил ответ, включив в него поддерживаемые версии и требования.
veducm 01
1
Фактически, вы хотите добавить эффект движения к фоновому виду, а не к виду спереди. Но кроме этого, этот код отлично работает! Спасибо.
gstroup
1
Кто-нибудь знает, как проверить, отключил ли пользователь эффекты? stackoverflow.com/questions/19132000/…
Эрик
3
@gstroup На самом деле, на главном экране движутся и передний план, и фон. Но если бы вы выбрали только один слой для применения эффекта параллакса, я согласен, что фон, вероятно, был бы лучше всего.
Rubberduck 08
16

В переводе на быстрый, если кому-то лень. Пожалуйста, проголосуйте за ответ @veducm , если вы нашли это полезным

@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! Я обновил свой ответ, включив в него и быстрый образец. Он основан на этом. Не стесняйтесь просматривать его и вносить любые необходимые изменения.
veducm
7

Решение @veducm может быть немного короче. UIMotionEffectGroup для его движения по осям x и y устареет, если вы добавите эффекты движения по осям x и 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];
XOXO
источник
2
Так же , как руководители, если кто -то использует этот сокращенный вариант, первые потребности эффектого движения , чтобы иметь typeот UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxisи не UIInterpolatingMotionEffectTypeTiltAlongVerticalAxisтак как это в коде выложенных
Flexicoder
1
@BooHoo Добавить их в группу устарело? Где ты это видишь? На момент написания этой статьи в документации Apple ничего не говорится об устаревании. Вся цель их группировки - улучшить производительность. Все эффекты в группе визуализируются однократно. Когда вы применяете их по отдельности, они должны отображаться отдельно. Не стесняйтесь поправить меня, если я ошибаюсь (и укажите мне на документацию Apple по этому поводу.)
Дэвид Лари,
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
источник
1
@Rob согласно вашему предложению обновил ответ
damithH
5

Вот простая категория для интеграции эффекта на 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
источник
1
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
Maciej Swic
Будет сложно интегрировать здесь весь проект github
Беджил
1
Вам не нужно приносить весь проект, только существенную часть, как я продемонстрировал, редактируя ответ. Это сделано для того, чтобы код оставался на тот случай, если Github не работает, вы потянете репо или небо упадет.
Maciej Swic
3

Это поможет тем, кто хочет реализовать параллакс для tableView или collectionView.

  • прежде всего создайте ячейку для представления таблицы и поместите в нее представление изображения.

  • установите высоту изображения немного больше высоты ячейки. если высота ячейки = 160, пусть высота изображения будет 200 (для создания эффекта параллакса, и вы можете изменить его соответствующим образом)

  • поместите эти две переменные в свой viewController или любой класс, в котором ваш делегат tableView расширен

let imageHeight:CGFloat = 150.0
let OffsetSpeed: CGFloat = 25.0
  • добавьте следующий код в тот же класс
 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))
        }
    }
}
  • где seriesTabelView - мой UItableview

  • и теперь переходим к ячейке этого tableView и добавляем следующий код

func offset(offset: CGPoint) {
        posterImage.frame = CGRectOffset(self.posterImage.bounds, offset.x, offset.y)
    }
  • были posterImage - мой UIImageView

Если вы хотите реализовать это в collectionView, просто измените tableView vairable на вашу переменную collectionView.

и это все. я не уверен, что это лучший способ. но у меня это работает. надеюсь, что это сработает и для вас. и дайте мне знать, если возникнут проблемы

Muneef M
источник
2

Действительно легко сделать так, почему ссылается на сложный код. просто попробуйте. Работает

Взгляните на изображение точно по размеру просмотра изображения. по умолчанию альфа для набора просмотров 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;
}
Г-н Джавед Мултани
источник
-1

Быстрый перевод:

// 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)
мукаисси
источник