3D анимация куба для UIScrollView в target-c - PullRequest
0 голосов
/ 04 июня 2019

Я хочу сделать scrollView 3D-анимацию куба, чтобы воссоздать переход Instagram между историями.

Сначала я создал новый класс, который подклассов UIScrollView:

@implementation ScrollView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = UIColor.clearColor;
        [self setPagingEnabled:YES];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    return self;
}

@end 

Затем я добавил представления в наш источник данных

- (void)setDataSourceWith:(NSArray<UIView *>*)views
{
    self.views = views;
    for (int i = 0; i < self.views.count; i++) {
        UIView *view = self.views[i];
        CGFloat width = self.frame.size.width;
        CGFloat height = self.frame.size.height;
        CGFloat xOffset = width * (CGFloat)i;
        view.frame = CGRectMake(xOffset, 0, width, height);
        [self addSubview:view];
        self.contentSize = CGSizeMake(xOffset + width, height);
    }
}

Далее, в ViewController.swift, добавьте экземпляр scrollView и создайте следующую функцию для разметки представления прокрутки:

@interface ViewController () <UIScrollViewDelegate>

@property (strong, nonatomic) ScrollView *scrollView;

@end

- (void)layout
{
    self.scrollView = [ScrollView new];
    self.scrollView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    self.scrollView.delegate = self;
    [self.view addSubview:self.scrollView];

    UIView *view1 = [UIView new];
    view1.backgroundColor = UIColor.orangeColor;
    UIView *view2 = [UIView new];
    view1.backgroundColor = UIColor.redColor;
    UIView *view3 = [UIView new];
    view3.backgroundColor = UIColor.blueColor;
    UIView *view4 = [UIView new];;
    view4.backgroundColor = UIColor.greenColor;
    [self.scrollView setDataSourceWith:[NSArray arrayWithObjects:view1, view2, view3, view4, nil]];
}

Затем в реализации метода scrollViewDidScroll я добавил:

   - (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // 1 

    NSArray *visibleViews = [[self.scrollView visibleViews] sortedArrayUsingComparator:^NSComparisonResult(UIView *obj1, UIView *obj2) {
        return [@(obj1.frame.origin.x) compare:@(obj2.frame.origin.x)];
    }];

    // 2 
    CGFloat xOffset = self.scrollView.contentOffset.x;

    // 3 
    CGPoint rightAnchorPoint = CGPointMake(0, 0.5);
    CGPoint leftAnchorPoint = CGPointMake(1, 0.5);

    // 4 
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0 / 1000;

    // 5 
    CGFloat leftSideOriginalTransform = ((CGFloat)(90 * M_PI / 180.0));
    CGFloat rightSideCellOriginalTransform = -(CGFloat)(90 * M_PI / 180.0);

    if (visibleViews.firstObject)
        if (visibleViews.lastObject)
            if (xOffset > 0) {
                // 1 - Get the rightmost view and the leftmost view.
                UIView *viewFurthestRight = (id)visibleViews.firstObject;
                UIView *viewFurthestLeft = (id)visibleViews.lastObject;

                BOOL hasCompletedPaging = fmod(xOffset, scrollView.frame.size.width);

                double rightAnimationPercentComplete = hasCompletedPaging ? 0.0 : (1.0 - fmod(xOffset, scrollView.frame.size.width));

                if (xOffset < 0) {
                    rightAnimationPercentComplete -= 1;
                }

                [viewFurthestRight transformTo:rightSideCellOriginalTransform * rightAnimationPercentComplete
                                          with:transform];

                [viewFurthestRight setAnchorPoint:rightAnchorPoint];

                if (xOffset > 0) {
                    double leftAnimationPercentComplete = fmod(xOffset, scrollView.frame.size.width);
                    [viewFurthestRight transformTo:leftSideOriginalTransform * leftAnimationPercentComplete
                                              with:transform];

                    [viewFurthestLeft setAnchorPoint:leftAnchorPoint];
                }
            }
}
  1. Сортировать, чтобы поместить представление на первое место с наибольшим значением xOrigin

  2. Создать переменную для отслеживания текущего значения xOffset.Это будет использоваться в вычислениях углов.

  3. Создание 2 опорных точек.Чтобы повернуть наши точки зрения, нам нужно определить их опорную точку.

  4. Создать преобразование.Значение .m34 диктует точку зрения пользователя на сцене.

  5. Ссылка на исходные состояния вида слева и справа от экрана соответственно

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

- (NSArray<UIView *>*)visibleViews
{
    CGRect visibleRect = CGRectMake(self.contentOffset.x, 0, self.frame.size.width, self.frame.size.height);
    NSMutableArray <UIView*> *views = [NSMutableArray array];
    for (UIView *view in self.views) {
        if (CGRectIntersectsRect(view.frame, visibleRect)) {
            [views addObject:view];
        }
    }
    return views;
}

Наконец-то под дополнениями для UIView:

@implementation UIView (Additions)

- (void)transformTo:(CGFloat)radians with:(CATransform3D)transform
{
    self.layer.transform = CATransform3DRotate(transform, radians, 0, 1, 0.0);
}

- (void)setAnchorPoint:(CGPoint)point
{
    CGPoint newPoint = CGPointMake(self.bounds.size.width * point.x, self.bounds.size.height * point.y);
    CGPoint oldPoint = CGPointMake(self.bounds.size.width * self.layer.anchorPoint.x, self.bounds.size.height * self.layer.anchorPoint.y);

    newPoint = CGPointApplyAffineTransform(newPoint, self.transform);
    oldPoint = CGPointApplyAffineTransform(oldPoint, self.transform);

    CGPoint position = self.layer.position;
    position.x -= oldPoint.x;
    position.x += newPoint.x;

    position.y -= oldPoint.y;
    position.y += newPoint.y;

    self.layer.position = position;
    self.layer.anchorPoint = point;
}

У меня не может быть перехода между видами.Переход прерывистый, а координаты неправильные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...