13

ユーザーが静止画像のフィルター間をスワイプできるようにしようとしています。アイデアは、フィルターがその上をスクロールしている間、画像が所定の位置に留まるということです。Snapchat は最近、この機能を実装したバージョンをリリースしました。このビデオの 1:05 で、私が達成しようとしていることを正確に示しています。

これまでのところ、元の画像の左側と右側に 3 つの UIImageView をスクロールビューに配置し、フレームの origin.x と size.width を scrollView の contentOffset.x で調整しようとしました。このアイデアは、別の投稿で見つけまし。左と右のコンテンツ モードを UIViewContentModeLeft と UIViewContentModeRight に変更しても解決しませんでした。

次に、3 つの UIImageView をすべて積み重ねてみました。2 つの CALayer マスクを作成し、それらをスタックの左右の scrollView に挿入して、マスクをスクロールするとフィルター処理された画像が表示されるようにしました。これは私にはうまくいきませんでした。どんな助けでも大歓迎です。

4

4 に答える 4

11

必要な画像ビューは 2 つだけです (これはページ分割されたスタイルのスクロールであるため、現在のビューと次のビューです)。フィルターが変更されるたびに役割が切り替わります。また、レイヤーマスクを使用するアプローチは機能するはずですが、スクロールビューでは機能しません。

したがって、ビューの構成が次のようになっていることを確認してください。

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

各画像ビューにはマスク レイヤーがあります。これは単純なレイヤーであり、マスク レイヤーの位置を変更して、実際に表示される画像の量を変更します。

これで、メイン ビューがパン ジェスチャを処理し、ジェスチャの変換を使用して、受信イメージ ビューのマスク レイヤーの位置とスクロール ビューのコンテンツ オフセットを変更します。

変更が完了すると、「現在の」画像ビューは表示されなくなり、「次の」画像ビューが画面全体に表示されます。「現在の」画像ビューが前面に移動してincomingビューになり、そのマスクが更新されて透明になります。次のジェスチャが開始されると、その画像が次のフィルタに更新され、変更プロセスが最初からやり直されます。

スクロールが行われている間、フィルター処理された画像をバックグラウンドでいつでも準備できるため、切り替えるときに画像をビューにプッシュする準備ができています (高速スクロール用)。

于 2014-05-02T13:19:26.627 に答える
4

私の最初の試みでは、UIImageビューの代わりにUIImageをマスクしようとして間違いを犯しましたが、最終的にはかなりまともな解決策(UIImageViewマスクを使用)を以下に示しました. ご不明な点がございましたら、お気軽にお問い合わせください。

基本的に現在の画像とフィルターをかけた画像を作成します。UIView を (四角形で) マスクしてから、スワイプに基づいてマスクを調整します。

結果へのリンク: https://www.youtube.com/watch?v=k75nqVsPggY&list=UUIctdpq1Pzujc0u0ixMSeVw

マスク クレジット: https://stackoverflow.com/a/11391478/3324388

@interface FilterTestsViewController ()

@end

@implementation FilterTestsViewController

NSArray *_pictureFilters;
NSNumber* _pictureFilterIterator;
UIImage* _originalImage;
UIImage* _currentImage;
UIImage* _filterImage;
UIImageView* _uiImageViewCurrentImage;
UIImageView* _uiImageViewNewlyFilteredImage;
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
    _uiImageViewNewlyFilteredImage = [[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:_uiImageViewNewlyFilteredImage];

    //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]];
    _uiImageViewNewlyFilteredImage.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 covered by ARC.
    CGPathRelease(path);

    // Set the mask of the view.
    _uiImageViewNewlyFilteredImage.layer.mask = maskLayer;
}


-(void)clearIncomingImage
{
    _filterImage = nil;
    _uiImageViewNewlyFilteredImage.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
于 2014-09-26T20:51:07.240 に答える
3

CIImage のページング スクロールを実行するスクロール ビューでスワイプ可能なフィルターを作成できます。

また

これを使用できます: https://github.com/pauljeannot/SnapSliderFilters

于 2016-09-20T14:05:23.700 に答える
3

Aggressor のソリューションを部分的に使用して、最小限のコード行でセットアップする最も簡単な方法と思われるものを思いつきました。

@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))
}

次に、画像を設定し、imageViewsの上にscrollViewがあることを確認してください。要求どおりの動作 (スナップチャットなど) については、スクロール ビューでページングが有効に設定されていることを確認し、背景色が明確であることを確認してください。このメソッドの利点は、すべての scrollView 動作を無料で取得できることです... scrollView を使用するためです

于 2015-04-23T22:46:03.350 に答える