Desliza entre imágenes filtradas

Estoy intentando permitir que los usuarios pasen entre los filters en una image estática. La idea es que la image se mantenga en su lugar mientras el filter se desplaza por encima. Snapchat lanzó recientemente una versión que implementa esta function. Este video muestra exactamente lo que estoy tratando de lograr a la 1:05.

Hasta ahora, he intentado poner tres UIImageView en una vista de desplazamiento, a la izquierda y otra a la derecha de la image original, y ajustar sus frameworks origen.x y size.width con scrollView's contentOffset.x. Encontré esta idea en otra publicación aquí . Cambiar el modo de contenido de izquierda y derecha a UIViewContentModeLeft y UIViewContentModeRight no ayudó.

Luego traté de astackr los tres UIImageView's uno encima del otro. Hice dos máscaras de CALayer y las inserté en el scrollView de la izquierda y la derecha de la stack, de modo que cuando se desplaza la máscara se desvela la image filtrada. Esto no funcionó para mí. Cualquier ayuda sería muy apreciada.

Solo debe necesitar 2 vistas de image (la actual y la entrante, ya que se trata de un desplazamiento de estilo paginado), y cambian de rol después de cada cambio de filter. Y su enfoque de usar una máscara de capa debería funcionar, pero no en una vista de desplazamiento.

Por lo tanto, asegúrese de que su organización de visualización sea similar a:

 UIView // receives all gestures UIScrollView // handles the filter name display, touch disabled UIImageView // incoming in front, but masked out UIImageView // current behind 

Cada vista de image tiene una capa de máscara, es solo una capa simple y modifica la position de la capa de máscara para cambiar la cantidad de la image que es realmente visible.

Ahora, la vista principal maneja el gesto de paneo y usa la traducción del gesto para cambiar la position de la capa de máscara de vista de image entrante y el desplazamiento de contenido de la vista de desplazamiento.

Cuando se completa un cambio, la vista de image "actual" ya no se puede ver y la vista de image "entrante" toma toda la pantalla. La vista de image "actual" ahora se mueve al frente y se convierte en la vista incoming , su máscara se actualiza para que sea transparente. A medida que se inicia el siguiente gesto, su image se actualiza al siguiente filter y el process de cambio comienza de nuevo.

Siempre puede preparar las imágenes filtradas en segundo plano a medida que avanza el desplazamiento para que la image esté list para entrar en la vista a medida que cambia (para desplazarse rápidamente).

En mi primer bash cometí un error al tratar de enmascarar un UIImage en lugar de una vista UIImage, pero finalmente obtuve una solución de trabajo bastante decente (que usa una máscara UIImageView) a continuación. Si tiene preguntas no dude en preguntar.

Básicamente, creo la image actual y la image filtrada. Enmascaro la UIView (con un rectángulo) y luego ajuste la máscara según el slider.

Enlace al resultado: https://www.youtube.com/watch?v=k75nqVsPggY&list=UUIctdpq1Pzujc0u0ixMSeVw

Crédito de máscara: https://stackoverflow.com/a/11391478/3324388

 @interface FilterTestsViewController () @end @implementation FilterTestsViewController NSArray *_pictureFilters; NSNumber* _pictureFilterIterator; UIImage* _originalImage; UIImage* _currentImage; UIImage* _filterImage; UIImageView* _uiImageViewCurrentImage; UIImageView* _uiImageViewNewlyFiltenetworkingImage; CGPoint _startLocation; BOOL _directionAssigned = NO; enum direction {LEFT,RIGHT}; enum direction _direction; BOOL _reassignIncomingImage = YES; - (void)viewDidLoad { [super viewDidLoad]; [self initializeFiltering]; } //set it up for video feed -(void)initializeVideoFeed { } -(void)initializeFiltering { //create filters _pictureFilters = @[@"CISepiaTone",@"CIColorInvert",@"CIColorCube",@"CIFalseColor",@"CIPhotoEffectNoir"]; _pictureFilterIterator = 0; //create initial image and current image _originalImage = [UIImage imageNamed:@"ja.jpg"]; //creates image from file, this will result in a nil CIImage but a valid CGImage; _currentImage = [UIImage imageNamed:@"ja.jpg"]; //create the UIImageViews for the current and filter object _uiImageViewCurrentImage = [[UIImageView alloc] initWithImage:_currentImage]; //creates a UIImageView with the UIImage _uiImageViewNewlyFiltenetworkingImage = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height)];//need to set its size to full since it doesn't have a filter yet //add UIImageViews to view [self.view addSubview:_uiImageViewCurrentImage]; //adds the UIImageView to view; [self.view addSubview:_uiImageViewNewlyFiltenetworkingImage]; //add gesture UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(swipeRecognized:)]; [self.view addGestureRecognizer:pan]; } -(void)swipeRecognized:(UIPanGestureRecognizer *)swipe { CGFloat distance = 0; CGPoint stopLocation; if (swipe.state == UIGestureRecognizerStateBegan) { _directionAssigned = NO; _startLocation = [swipe locationInView:self.view]; }else { stopLocation = [swipe locationInView:self.view]; CGFloat dx = stopLocation.x - _startLocation.x; CGFloat dy = stopLocation.y - _startLocation.y; distance = sqrt(dx*dx + dy*dy); } if(swipe.state == UIGestureRecognizerStateEnded) { if(_direction == LEFT && (([UIScreen mainScreen].bounds.size.width - _startLocation.x) + distance) > [UIScreen mainScreen].bounds.size.width/2) { [self reassignCurrentImage]; }else if(_direction == RIGHT && _startLocation.x + distance > [UIScreen mainScreen].bounds.size.width/2) { [self reassignCurrentImage]; }else { //since no filter applied roll it back if(_direction == LEFT) { _pictureFilterIterator = [NSNumber numberWithInt:[_pictureFilterIterator intValue]-1]; }else { _pictureFilterIterator = [NSNumber numberWithInt:[_pictureFilterIterator intValue]+1]; } } [self clearIncomingImage]; _reassignIncomingImage = YES; return; } CGPoint velocity = [swipe velocityInView:self.view]; if(velocity.x > 0)//right { if(!_directionAssigned) { _directionAssigned = YES; _direction = RIGHT; } if(_reassignIncomingImage && !_filterImage) { _reassignIncomingImage = false; [self reassignIncomingImageLeft:NO]; } } else//left { if(!_directionAssigned) { _directionAssigned = YES; _direction = LEFT; } if(_reassignIncomingImage && !_filterImage) { _reassignIncomingImage = false; [self reassignIncomingImageLeft:YES]; } } if(_direction == LEFT) { if(stopLocation.x > _startLocation.x -5) //adjust to avoid snapping { distance = -distance; } }else { if(stopLocation.x < _startLocation.x +5) //adjust to avoid snapping { distance = -distance; } } [self slideIncomingImageDistance:distance]; } -(void)slideIncomingImageDistance:(float)distance { CGRect incomingImageCrop; if(_direction == LEFT) //start on the right side { incomingImageCrop = CGRectMake(_startLocation.x - distance,0, [UIScreen mainScreen].bounds.size.width - _startLocation.x + distance, [UIScreen mainScreen].bounds.size.height); }else//start on the left side { incomingImageCrop = CGRectMake(0,0, _startLocation.x + distance, [UIScreen mainScreen].bounds.size.height); } [self applyMask:incomingImageCrop]; } -(void)reassignCurrentImage { if(!_filterImage)//if you go fast this is null sometimes { [self reassignIncomingImageLeft:YES]; } _uiImageViewCurrentImage.image = _filterImage; self.view.frame = [[UIScreen mainScreen] bounds]; } //left is forward right is back -(void)reassignIncomingImageLeft:(BOOL)left { if(left == YES) { _pictureFilterIterator = [NSNumber numberWithInt:[_pictureFilterIterator intValue]+1]; }else { _pictureFilterIterator = [NSNumber numberWithInt:[_pictureFilterIterator intValue]-1]; } NSNumber* arrayCount = [NSNumber numberWithInt:(int)_pictureFilters.count]; if([_pictureFilterIterator integerValue]>=[arrayCount integerValue]) { _pictureFilterIterator = 0; } if([_pictureFilterIterator integerValue]< 0) { _pictureFilterIterator = [NSNumber numberWithInt:(int)_pictureFilters.count-1]; } CIImage* ciImage = [CIImage imageWithCGImage:_originalImage.CGImage]; CIFilter* filter = [CIFilter filterWithName:_pictureFilters[[_pictureFilterIterator integerValue]] keysAndValues:kCIInputImageKey,ciImage, nil]; _filterImage = [UIImage imageWithCIImage:[filter outputImage]]; _uiImageViewNewlyFiltenetworkingImage.image = _filterImage; CGRect maskRect = CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height); [self applyMask:maskRect]; } //apply mask to filter UIImageView -(void)applyMask:(CGRect)maskRect { // Create a mask layer and the frame to determine what will be visible in the view. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; // Create a path with the rectangle in it. CGPathRef path = CGPathCreateWithRect(maskRect, NULL); // Set the path to the mask layer. maskLayer.path = path; // Release the path since it's not covenetworking by ARC. CGPathRelease(path); // Set the mask of the view. _uiImageViewNewlyFiltenetworkingImage.layer.mask = maskLayer; } -(void)clearIncomingImage { _filterImage = nil; _uiImageViewNewlyFiltenetworkingImage.image = nil; //mask current image view fully again [self applyMask:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width,[UIScreen mainScreen].bounds.size.height)]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end 

Usando la solución de Aggressor en parte, se me ocurrió lo que veo como la forma más simple de configurarlo, con las líneas mínimas de código.

 @IBOutlet weak var topImage: UIImageView! @IBOutlet weak var bottomImage: UIImageView! @IBOutlet weak var scrollview: UIScrollView! override func viewDidLoad() { super.viewDidLoad() scrollview.delegate=self scrollview.contentSize=CGSizeMake(2*self.view.bounds.width, self.view.bounds.height) applyMask(CGRectMake(self.view.bounds.width-scrollview.contentOffset.x, scrollview.contentOffset.y, scrollview.contentSize.width, scrollview.contentSize.height)) } func applyMask(maskRect: CGRect!){ var maskLayer: CAShapeLayer = CAShapeLayer() var path: CGPathRef = CGPathCreateWithRect(maskRect, nil) maskLayer.path=path topImage.layer.mask = maskLayer } func scrollViewDidScroll(scrollView: UIScrollView) { println(scrollView.contentOffset.x) applyMask(CGRectMake(self.view.bounds.width-scrollView.contentOffset.x, scrollView.contentOffset.y, scrollView.contentSize.width, scrollView.contentSize.height)) } 

Luego, simplemente configure las imágenes y asegúrese de tener el scrollView por encima de imageViews. Para el comportamiento solicitado (como snapchat), asegúrese de que la vista de desplazamiento tenga habilitado el establecimiento de pagination en true y asegúrese de que el color de background sea claro. El beneficio de este método es get todo el comportamiento de scrollView de forma gratuita … porque usa un scrollView

puede crear filters deslizables con vista de desplazamiento que lleva el desplazamiento de pagination de CIImage.

o

Puede usar esto: https://github.com/pauljeannot/SnapSliderFilters