iOSはコサイン関数を利用してカード閲覧ツールを実現する。


本論文の例では、iOSはコサイン関数を利用してカード閲覧ツールの具体的なコードを共有しています。
一、効果を実現する
画面をドラッグすることでカードの移動ができ、左右のカードはドラッグで小さくなり、中間のカードは大きくなります。効果は以下の通りです

二、原理説明
1、上記のアニメーション効果はコサイン関数の曲線特性によって実現されます。まず関数曲線y=cos(x)を見て、区間-π/2から π/2の範囲では、yの値はxの0の後が一番大きく、左右が小さくなります。

2、スクロールされたカードの高さを0.0~1.0の割合で拡大縮小できます。効果は以下の通りです。

3、携帯電話の画面に置く効果は以下の通りです。

三、コード
各カードを封入するのはCard.hです。
カードはCard Switch View.hに表示されています。
コードの構想はコントロールの中心が原点であると仮定し、中軸がx軸とy軸であり、カードの中心がy軸に近いほどカード長が短くなる割合が1.0に近くなり、カードの中線距離がy軸に遠くなるほどカード長が短くなる割合が0に近くなる。
下の図のように、ブロックが位置1から位置2まで左に長さa(コードを書く時は角度と長さの変換が必要です)を移動したと仮定すると、曲線上のbの値はcos(a)となり、b=0.8と仮定すると、位置2の時に高さを元の0.8倍に短縮し、これらの推論でコントロールの軸に近い位置カードが長くなります(a)。ここでの角度と長さの変換倍数は状況によって異なります)

//
// CardSwitchView.m
// CardSwitchDemo
//
// Created by Apple on 2016/11/9.
// Copyright © 2016  Apple. All rights reserved.
//
 
#import "CardSwitchView.h"
#import "Card.h"
 
//              
static float viewScale = 0.70f;
 
@interface CardSwitchView ()<UIScrollViewDelegate>
{
 //     ScrollView
 UIScrollView *_scrollView;
 //        
 NSMutableArray *_cards;
 //       
 CGFloat _startPointX;
 //       
 CGFloat _endPointX;
 //       index
 NSInteger _currentIndex;
}
@end
 
@implementation CardSwitchView
 
-(instancetype)initWithFrame:(CGRect)frame
{
 if (self = [super initWithFrame:frame]) {
 [self buildLayout];
 }
 return self;
}
 
 
-(void)buildLayout
{
 //   ScrollView
 _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
 _scrollView.delegate = self;
 _scrollView.showsHorizontalScrollIndicator = false;
 [self addSubview:_scrollView];
 
 //       
 _cards = [[NSMutableArray alloc] init];
 _currentIndex = 0;
}
 
#pragma mark -
#pragma mark   Frame  
 
//    
-(float)cardWidth
{
 return viewScale*self.bounds.size.width;
}
 
//    
-(float)margin
{
 return (self.bounds.size.width - [self cardWidth])/4;
}
//      
-(float)startX
{
 return (self.bounds.size.width - [self cardWidth])/2.0f;
}
 
#pragma mark -
#pragma mark       
-(void)setCardNumber:(NSInteger)cardNumber
{
 _cardNumber = cardNumber;
 //          
 for (NSInteger i = 0; i<cardNumber; i++ ) {
 //     ScrollView     
 float viewX = [self startX] + i*([self cardWidth] + [self margin]);
 Card* card = [[Card alloc] initWithFrame:CGRectMake(viewX, 0, [self cardWidth], self.bounds.size.height)];
 card.layer.borderWidth = 1.0f;
 card.index = i;
 [_scrollView addSubview:card];
 [_cards addObject:card];
 [_scrollView setContentSize:CGSizeMake(card.frame.origin.x + [self cardWidth] + 2*[self margin], 0)];
 }
 //       
 [self updateCardTransform];
}
 
#pragma mark -
#pragma mark ScrollView    
//           
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
 _startPointX = scrollView.contentOffset.x;
}
 
// ScrollView        view   ,        view    
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
 [self updateCardTransform];
}
 
//    ,         
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
 //         
 dispatch_async(dispatch_get_main_queue(), ^{
 [self scrollToCurrentCard];
 });
}
 
//      
-(void)scrollToCurrentCard
{
 _endPointX = _scrollView.contentOffset.x;
 //          ,    scrollMiniDistance            
 float scrollMiniDistance = self.bounds.size.width/30.0f;
 if (_startPointX - _endPointX > scrollMiniDistance) {
 NSLog(@"      ");
 if (_currentIndex != 0) {
  _currentIndex -= 1;
 }
 }else if (_endPointX - _startPointX > scrollMiniDistance)
 {
 NSLog(@"      ");
 if (_currentIndex != _cards.count - 1) {
  _currentIndex += 1;
 }
 }
 float viewX = [self startX] + _currentIndex*([self cardWidth] + [self margin]);
 float needX = viewX - [self startX];
 [_scrollView setContentOffset:CGPointMake(needX, 0) animated:true];
}
 
 
//         
-(void)updateCardTransform
{
 for (Card *card in _cards) {
 //      index
 //  ScrollView     
 CGFloat scrollOffset = _scrollView.contentOffset.x;
 //               
 CGFloat cardCenterX = card.center.x - scrollOffset;
 //                   ,           
 CGFloat apartLength = fabs(self.bounds.size.width/2.0f - cardCenterX);
 //              
 CGFloat apartScale = apartLength/self.bounds.size.width;
 //           -π/4  +π/4      
 CGFloat scale = fabs(cos(apartScale * M_PI/4));
 //       
 card.transform = CGAffineTransformMakeScale(1.0, scale);
 }
}
 
@end
ダウンロード
GitHubプロジェクト
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。