Я хочу сделать 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];
}
}
}
Сортировать, чтобы поместить представление на первое место с наибольшим значением xOrigin
Создать переменную для отслеживания текущего значения xOffset.Это будет использоваться в вычислениях углов.
Создание 2 опорных точек.Чтобы повернуть наши точки зрения, нам нужно определить их опорную точку.
Создать преобразование.Значение .m34 диктует точку зрения пользователя на сцене.
Ссылка на исходные состояния вида слева и справа от экрана соответственно
В 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;
}
У меня не может быть перехода между видами.Переход прерывистый, а координаты неправильные.