-
-
Save ZhukV/1f29fefa84a13f971ce1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BookView.m | |
// Tale | |
// | |
// Created by Vitaliy Zhuk on 15.10.14. | |
// Copyright (c) 2014 ZhukV. All rights reserved. | |
// | |
CGFloat const kBookViewPageChangeTimeout = 1.4f; | |
typedef NS_ENUM(short int, BookViewPageAnimate) { | |
BookViewPageAnimateLeftUp, | |
BookViewPageAnimateLeftDown, | |
BookViewPageAnimateRightUp, | |
BookViewPageAnimateRightDown | |
}; | |
typedef NS_ENUM(short int, BookViewTouchOperation) { | |
BookViewTouchOperationNone, | |
BookViewTouchOperationPrevPage, | |
BookViewTouchOperationNextPage | |
}; | |
typedef NS_ENUM(short int, BookViewButtonPage) { | |
BookViewButtonPageFirst, | |
BookViewButtonPageLast | |
}; | |
#import "BookView.h" | |
#import "BookAudioNextPage.h" | |
@interface BookView() | |
@property (nonatomic) CGPoint startLocation; | |
@property (nonatomic, strong) UIView *bookPagesView; | |
@property (nonatomic, strong) UIView *pageLeftView; | |
@property (nonatomic, strong) UIView *pageRightView; | |
@property (nonatomic, strong) UIView *nextPageLeftView; | |
@property (nonatomic, strong) UIView *nextPageRightView; | |
@property (nonatomic, strong) UIView *pageCommonView; | |
@property (nonatomic, strong) UIImageView *pageCommonLeftView; | |
@property (nonatomic, strong) UIImageView *pageCommonRightView; | |
@property (nonatomic, strong) AVAudioPlayer *audio; | |
@property (nonatomic, strong) NSTimer *presentationTimer; | |
@property (nonatomic) NSTimeInterval presentationPauseOn; | |
@property (nonatomic) float pageCenterWidth; | |
@property (nonatomic) float indexCenterOffset; | |
@property (nonatomic) CGSize pageSize; | |
- (UIImage *)coverImage; | |
- (UIView *)createPageNative:(BookViewPage)page; | |
- (UIView *)createPageView:(NSInteger)index; | |
- (UIView *)createPageView:(NSInteger)index page:(BookViewPage)page; | |
- (void)renderCommonPages; | |
- (void)renderCommonPagesLeft; | |
- (void)renderCommonPagesRight; | |
- (void)hideCommonPages:(NSInteger)index; | |
- (void)hideCommonPages; | |
- (NSUInteger)countCommonPages:(BookViewPage)page viewIndex:(NSInteger)index; | |
- (NSUInteger)countCommonPages:(BookViewPage)page; | |
- (float)bookPagesViewOffset:(NSInteger)index; | |
- (float)bookPagesViewOffset; | |
- (void)renderButtonsOnLastPage; | |
- (void)renderButtonsOnFirstPage; | |
- (UIView *)createCommonButtons:(NSArray *)buttonModes page:(BookViewButtonPage)page; | |
- (UIButton *)createCommonButton:(BookViewActionButton)mode page:(BookViewButtonPage)page; | |
- (void)clickCommonButtonOnFirstPage:(UIButton *)button; | |
- (void)clickCommonButtonOnLastPage:(UIButton *)button; | |
- (void)presentationStart; | |
- (void)presentationUpdate; | |
- (void)presentationTimerTick; | |
- (int)presentationIndexByTime; | |
- (void)animatePagesViewOffset:(NSInteger)index; | |
- (void)animatePagesViewOffset; | |
- (void)animatePageWithMode:(BookViewPageAnimate)animate | |
oldView:(UIView *)oldView | |
newView:(UIView *)newView | |
completion:(void (^)(void))completion; | |
@end | |
#pragma mark - | |
@implementation BookView | |
#pragma mark Initialize | |
- (instancetype)initWithBook:(BookModel *)book delegate:(id<BookViewDelegate>)delegate | |
{ | |
self = [super init]; | |
if (!self) { | |
return nil; | |
} | |
_book = book; | |
_delegate = delegate; | |
_processed = NO; | |
CGRect bookPagesFrame, pageCommonFrame, pageCommonLeftFrame, pageCommonRightFrame; | |
UIView *bookPagesRootView = [UIView new]; | |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { | |
self.frame = CGRectMake(0, 0, 399, 291); | |
self.pageCenterWidth = 27; | |
self.pageSize = CGSizeMake(183, 284); | |
bookPagesFrame = CGRectMake(3.f, 4, 366, 274); | |
pageCommonFrame = CGRectMake(3.f, 4, 389, 281); | |
pageCommonLeftFrame = CGRectMake(0, 0, 100, 284); | |
pageCommonRightFrame = CGRectMake(293, 0, 100, 284); | |
} else { | |
self.frame = CGRectMake(0, 0, 960, 700); | |
self.pageCenterWidth = 64; | |
self.pageSize = CGSizeMake(438, 680); | |
bookPagesFrame = CGRectMake(10, 10, 876, 660); | |
pageCommonFrame = CGRectMake(10, 10, 940, 680); | |
pageCommonLeftFrame = CGRectMake(0, 0, 300, 680); | |
pageCommonRightFrame = CGRectMake(641.f, 0, 300, 680); | |
} | |
// Create cover mode | |
self.backgroundColor = [UIColor colorWithPatternImage:[self coverImage]]; | |
// Create page root view | |
bookPagesRootView.frame = bookPagesFrame; | |
self.bookPagesView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, bookPagesFrame.size.width, bookPagesFrame.size.height)]; | |
[bookPagesRootView addSubview:self.bookPagesView]; | |
// Create left and right common view of pages | |
self.pageCommonView = [[UIView alloc] initWithFrame:pageCommonFrame]; | |
self.pageCommonLeftView = [[UIImageView alloc] initWithFrame:pageCommonLeftFrame]; | |
self.pageCommonLeftView.clipsToBounds = YES; | |
self.pageCommonRightView = [[UIImageView alloc] initWithFrame:pageCommonRightFrame]; | |
self.pageCommonRightView.clipsToBounds = YES; | |
[self.pageCommonView addSubview:self.pageCommonLeftView]; | |
[self.pageCommonView addSubview:self.pageCommonRightView]; | |
// Create pager | |
int indexes = floor((float)[self.book.pages count] / 2) + 1; | |
_pager.indexes = indexes; // Because first and last page view with one image | |
self.indexCenterOffset = self.pageCenterWidth / (indexes - 3); | |
// View page with first page (Zero view index) | |
[self addSubview:self.pageCommonView]; | |
[self addSubview:bookPagesRootView]; | |
// Render puzzle button | |
[self renderButtonsOnLastPage]; | |
[self renderButtonsOnFirstPage]; | |
// Render common pages | |
[self renderCommonPages]; | |
return self; | |
} | |
+ (instancetype)withBook:(BookModel *)book delegate:(id<BookViewDelegate>)delegate | |
{ | |
return [[self alloc] initWithBook:book delegate:delegate]; | |
} | |
#pragma mark - | |
#pragma mark Create views and helpers for view | |
- (UIImage *)coverImage | |
{ | |
return [UIImage imageNamedWithoutCache:self.book.cover]; | |
} | |
- (UIView *)createPageNative:(BookViewPage)page | |
{ | |
NSString *imagePath; | |
if (page == BookViewPageLeft) { | |
imagePath = self.book.pageLeft; | |
} else { | |
imagePath = self.book.pageRight; | |
} | |
UIImage *image = [UIImage imageNamedWithoutCache:imagePath]; | |
UIImageView *view = [[UIImageView alloc] initWithImage:image]; | |
view.frame = CGRectMake(0, 0, self.pageSize.width, self.pageSize.height); | |
return view; | |
} | |
- (UIView *)createPageView:(NSInteger)index | |
{ | |
if (!fmodf(index, 2)) { | |
return [self createPageView:index page:BookViewPageRight]; | |
} else { | |
return [self createPageView:index page:BookViewPageLeft]; | |
} | |
} | |
- (UIView *)createPageView:(NSInteger)index page:(BookViewPage)page | |
{ | |
NSString *path = [self.book.pages objectAtIndex:index]; | |
UIImage *image = [UIImage imageNamedWithoutCache:path]; | |
UIImageView *view = [[UIImageView alloc] initWithImage:image]; | |
CGRect viewFrame = view.frame; | |
if (page == BookViewPageLeft) { | |
viewFrame.origin = CGPointMake(0, 0); | |
view.layer.anchorPoint = CGPointMake(1, 0.5); | |
} else { | |
viewFrame.origin = CGPointMake(self.pageSize.width, 0); | |
view.layer.anchorPoint = CGPointMake(0, 0.5); | |
} | |
viewFrame.size = self.pageSize; | |
view.frame = viewFrame; | |
return view; | |
} | |
- (void)renderCommonPages | |
{ | |
[self renderCommonPagesLeft]; | |
[self renderCommonPagesRight]; | |
} | |
- (void)renderCommonPagesLeft | |
{ | |
int i; | |
UIView *page; | |
CGRect pageFrame; | |
UIView *commonLeftView = [UIView new]; | |
commonLeftView.frame = self.pageCommonLeftView.frame; | |
commonLeftView.backgroundColor = [UIColor clearColor]; | |
commonLeftView.opaque = NO; | |
for (i = 0; i <= self.pager.indexes / 2 + 2; i++) { | |
page = [self createPageNative:BookViewPageLeft]; | |
pageFrame = page.frame; | |
pageFrame.origin.x = self.indexCenterOffset * i; | |
page.frame = pageFrame; | |
[commonLeftView addSubview:page]; | |
} | |
UIImage *imageLeftCommon = [UIImage imageWithView:commonLeftView]; | |
[self.pageCommonLeftView setImage:imageLeftCommon]; | |
} | |
- (void)renderCommonPagesRight | |
{ | |
int i; | |
UIView *page; | |
CGRect pageFrame; | |
UIView *commonRightView = [UIView new]; | |
commonRightView.frame = self.pageCommonRightView.frame; | |
commonRightView.backgroundColor = [UIColor clearColor]; | |
commonRightView.opaque = NO; | |
for (i = 0; i < self.pager.indexes / 2 + 2; i++) { | |
page = [self createPageNative:BookViewPageRight]; | |
pageFrame = page.frame; | |
pageFrame.origin.x = -(self.pageSize.width - self.pageCommonRightView.frame.size.width) - (i * self.indexCenterOffset); | |
page.frame = pageFrame; | |
[commonRightView addSubview:page]; | |
} | |
UIImage *imageRightCommon = [UIImage imageWithView:commonRightView]; | |
[self.pageCommonRightView setImage:imageRightCommon]; | |
} | |
- (void)hideCommonPages:(NSInteger)index | |
{ | |
if (index == 0) { | |
self.pageCommonLeftView.hidden = YES; | |
} else { | |
self.pageCommonLeftView.hidden = NO; | |
} | |
if (index + 1 == self.pager.indexes) { | |
self.pageCommonRightView.hidden = YES; | |
} else { | |
self.pageCommonRightView.hidden = NO; | |
} | |
} | |
- (void)hideCommonPages | |
{ | |
[self hideCommonPages:self.pager.index]; | |
} | |
- (NSUInteger)countCommonPages:(BookViewPage)page viewIndex:(NSInteger)index | |
{ | |
if (page == BookViewPageLeft) { | |
if (index <= 1) { | |
return 0; | |
} | |
return index - 1; | |
} else { | |
if (index >= self.pager.indexes - 1) { | |
return 0; | |
} | |
return self.pager.index - index - 1; | |
} | |
} | |
- (NSUInteger)countCommonPages:(BookViewPage)page | |
{ | |
return [self countCommonPages:page viewIndex:self.pager.index]; | |
} | |
- (float)bookPagesViewOffset:(NSInteger)index | |
{ | |
if (index == 0 || index == 1) { | |
return 0; | |
} else if (index == self.pager.indexes - 2 || index == self.pager.indexes - 1) { | |
return self.pageCenterWidth; | |
} | |
return self.indexCenterOffset * [self countCommonPages:BookViewPageLeft viewIndex:index]; | |
} | |
- (float)bookPagesViewOffset | |
{ | |
return [self bookPagesViewOffset:self.pager.index]; | |
} | |
#pragma mark - | |
#pragma mark Render buttons on first and last page | |
- (void)renderButtonsOnLastPage | |
{ | |
NSMutableArray *buttons = [NSMutableArray new]; | |
if (self.book.puzzle != nil) { | |
[buttons addObject:[NSNumber numberWithShort:BookViewActionButtonPuzzle]]; | |
} | |
[buttons addObject:[NSNumber numberWithShort:BookViewActionButtonMoreTales]]; | |
UIView *buttonsView = [self createCommonButtons:buttons page:BookViewButtonPageLast]; | |
CGRect buttonsViewFrame = buttonsView.frame; | |
buttonsViewFrame.origin.x = self.pageSize.width + (self.pageSize.width - buttonsViewFrame.size.width) / 2; | |
buttonsViewFrame.origin.y = (self.pageSize.height - buttonsViewFrame.size.height) / 2; | |
buttonsView.frame = buttonsViewFrame; | |
buttonsView.layer.zPosition = -1000; | |
[self.bookPagesView addSubview:buttonsView]; | |
} | |
- (void)renderButtonsOnFirstPage | |
{ | |
NSMutableArray *buttons = [NSMutableArray new]; | |
if (self.book.audioFile != nil) { | |
[buttons addObject:[NSNumber numberWithShort:BookViewActionButtonReadMe]]; | |
} | |
if (self.book.puzzle != nil) { | |
[buttons addObject:[NSNumber numberWithShort:BookViewActionButtonPuzzle]]; | |
} | |
UIView *buttonsView = [self createCommonButtons:buttons page:BookViewButtonPageFirst]; | |
CGRect buttonsViewFrame = buttonsView.frame; | |
buttonsViewFrame.origin.x = (self.pageSize.width - buttonsViewFrame.size.width) / 2; | |
buttonsViewFrame.origin.y = (self.pageSize.height - buttonsViewFrame.size.height) / 2; | |
buttonsView.frame = buttonsViewFrame; | |
buttonsView.layer.zPosition = -1000; | |
[self.bookPagesView addSubview:buttonsView]; | |
} | |
- (UIView *)createCommonButtons:(NSArray *)buttonModes page:(BookViewButtonPage)page | |
{ | |
NSMutableArray *buttons = [NSMutableArray new]; | |
for (NSNumber *buttonMode in buttonModes) { | |
[buttons addObject:[self createCommonButton:[buttonMode shortValue] page:page]]; | |
} | |
float maxWidth = 0, fullHeight = 0; | |
int separated = 15; | |
UIView *buttonsView = [UIView new]; | |
CGRect buttonFrame; | |
// First iteration: Get max width. | |
for (UIButton *button in buttons) { | |
if (button.frame.size.width > maxWidth) { | |
maxWidth = button.frame.size.width; | |
} | |
} | |
// Second iteration: Add buttons to buttons view | |
for (int i = 0; i < [buttons count]; i++) { | |
UIButton *button = [buttons objectAtIndex:i]; | |
if (button.frame.size.width > maxWidth) { | |
maxWidth = button.frame.size.width; | |
} | |
buttonFrame = button.frame; | |
buttonFrame.origin.y = fullHeight; | |
buttonFrame.origin.x = (maxWidth - buttonFrame.size.width) / 2; | |
fullHeight += buttonFrame.size.height + separated; | |
button.frame = buttonFrame; | |
[buttonsView addSubview:button]; | |
} | |
CGRect buttonsViewFrame = buttonsView.frame; | |
buttonsViewFrame.size.width = maxWidth; | |
buttonsViewFrame.size.height = fullHeight - separated; | |
buttonsView.frame = buttonsViewFrame; | |
return buttonsView; | |
} | |
- (UIButton *)createCommonButton:(BookViewActionButton)mode page:(BookViewButtonPage)page | |
{ | |
NSString *imagePath; | |
switch (mode) { | |
case BookViewActionButtonPuzzle: | |
imagePath = @"button-puzzle-%@-%@@2x.png"; | |
break; | |
case BookViewActionButtonMoreTales: | |
imagePath = @"button-more-tales-%@-%@@2x.png"; | |
break; | |
case BookViewActionButtonReadMe: | |
imagePath = @"button-read-me-%@-%@@2x.png"; | |
break; | |
default: { | |
NSString *reason = [NSString stringWithFormat:@"Undefined button book mode: %d", mode]; | |
@throw [NSException exceptionWithName:@"InvalidArgument" reason:reason userInfo:@{@"mode": [NSNumber numberWithShort:mode]}]; | |
} | |
} | |
imagePath = [NSString stringWithFormat:imagePath, TALE_LOCALE, UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? @"ipad" : @"iphone"]; | |
UIImage *image = [UIImage imageNamedWithoutCache:imagePath]; | |
UIButton *button = [[UIButton alloc] init]; | |
button.backgroundColor = [UIColor colorWithPatternImage:image]; | |
CGRect buttonFrame = button.frame; | |
buttonFrame.size = image.size; | |
button.frame = buttonFrame; | |
button.tag = mode; | |
if (page == BookViewButtonPageFirst) { | |
[button addTarget:self action:@selector(clickCommonButtonOnFirstPage:) forControlEvents:UIControlEventTouchUpInside]; | |
} else if (page == BookViewButtonPageLast) { | |
[button addTarget:self action:@selector(clickCommonButtonOnLastPage:) forControlEvents:UIControlEventTouchUpInside]; | |
} else { | |
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Invalid page mode." userInfo:@{@"page": [NSNumber numberWithInt:(int)page]}]; | |
} | |
return button; | |
} | |
- (void)clickCommonButtonOnFirstPage:(UIButton *)button | |
{ | |
if (self.pager.index == 0) { | |
BookViewActionButton action = button.tag; | |
[self.delegate clickButtonAction:action]; | |
} | |
} | |
- (void)clickCommonButtonOnLastPage:(UIButton *)button | |
{ | |
if (self.pager.index + 1 == self.pager.indexes) { | |
BookViewActionButton action = button.tag; | |
[self.delegate clickButtonAction:action]; | |
} | |
} | |
#pragma mark - | |
#pragma mark Set pages | |
- (void)viewPagesAtIndex:(NSUInteger)index | |
changeTo:(BookViewPageChange)changeTo | |
animate:(BOOL)animate | |
{ | |
[self viewPagesAtIndex:index changeTo:changeTo animate:YES completion:nil pauseAudio:YES]; | |
} | |
- (void)viewPagesAtIndex:(NSUInteger)index | |
changeTo:(BookViewPageChange)changeTo | |
animate:(BOOL)animate | |
completion:(void (^)(void))completion | |
pauseAudio:(BOOL)pauseAudio | |
{ | |
if (self.processed) { | |
return; | |
} | |
_processed = YES; | |
self.presentationPauseOn = 0; | |
if (self.delegate) { | |
[self.delegate pageAnimationStart]; | |
} | |
if (index == 0) { | |
self.nextPageLeftView = nil; | |
self.nextPageRightView = [self createPageView:0]; | |
} else if (index + 1 == self.pager.indexes) { | |
self.nextPageLeftView = [self createPageView:[self.book.pages count] - 1]; | |
self.nextPageRightView = nil; | |
} else { | |
self.nextPageLeftView = [self createPageView:index * 2 - 1]; | |
self.nextPageRightView = [self createPageView:index * 2]; | |
} | |
BOOL runInPresentation = NO; | |
if (animate && changeTo != BookViewPageChangeToNone) { | |
_pager.index = index; | |
[self animatePagesViewOffset]; | |
// Hide next page views | |
self.nextPageLeftView.hidden = YES; | |
self.nextPageRightView.hidden = YES; | |
// Add views to root book view | |
[self.bookPagesView addSubview:self.nextPageLeftView]; | |
[self.bookPagesView addSubview:self.nextPageRightView]; | |
if (pauseAudio && self.presentation) { | |
[self presentationStop]; | |
runInPresentation = YES; | |
} | |
// Run next page audio | |
[[BookAudioNextPage sharedInstance] play]; | |
if (changeTo == BookViewPageChangeToLeft) { | |
if (index == 0) { | |
self.pageCommonLeftView.hidden = YES; | |
} else { | |
self.pageCommonLeftView.hidden = NO; | |
} | |
// First step of animation. Up page | |
[self animatePageWithMode:BookViewPageAnimateRightUp oldView:self.pageLeftView newView:self.nextPageLeftView completion:^{ | |
// Set the really view and clear temporary | |
self.pageLeftView = self.nextPageLeftView; | |
self.nextPageLeftView = nil; | |
// Second step of animation. Down page. | |
[self animatePageWithMode:BookViewPageAnimateLeftDown oldView:self.pageRightView newView:self.nextPageRightView completion:^{ | |
self.pageRightView = self.nextPageRightView; | |
self.nextPageRightView = nil; | |
self.pageCommonRightView.hidden = NO; | |
_processed = NO; | |
[self.delegate pageAnimationComplete]; | |
if (pauseAudio && runInPresentation) { | |
[self presentationStart]; | |
} | |
if (completion) { | |
completion(); | |
} | |
}]; | |
}]; | |
} else if (changeTo == BookViewPageChangeToRight) { | |
if (index + 1 == self.pager.indexes) { | |
self.pageCommonRightView.hidden = YES; | |
} else { | |
self.pageCommonRightView.hidden = NO; | |
} | |
// First step of animation. Up page | |
[self animatePageWithMode:BookViewPageAnimateLeftUp oldView:self.pageRightView newView:self.nextPageRightView completion:^{ | |
// Set the really view and clear temporary | |
self.pageRightView = self.nextPageRightView; | |
self.nextPageRightView = nil; | |
// Second step of animation. Down page. | |
[self animatePageWithMode:BookViewPageAnimateRightDown oldView:self.pageLeftView newView:self.nextPageLeftView completion:^{ | |
self.pageLeftView = self.nextPageLeftView; | |
self.nextPageLeftView = nil; | |
self.pageCommonLeftView.hidden = NO; | |
_processed = NO; | |
[self.delegate pageAnimationComplete]; | |
if (pauseAudio && runInPresentation) { | |
[self presentationStart]; | |
} | |
if (completion) { | |
completion(); | |
} | |
}]; | |
}]; | |
} | |
} else { | |
if (self.pageLeftView) { | |
[self.pageLeftView removeFromSuperview]; | |
} | |
if (self.pageRightView) { | |
[self.pageRightView removeFromSuperview]; | |
} | |
if (self.nextPageLeftView) { | |
self.pageLeftView = self.nextPageLeftView; | |
self.nextPageLeftView = nil; | |
[self.bookPagesView addSubview:self.pageLeftView]; | |
} | |
if (self.nextPageRightView) { | |
self.pageRightView = self.nextPageRightView; | |
self.nextPageRightView = nil; | |
[self.bookPagesView addSubview:self.pageRightView]; | |
} | |
_pager.index = index; | |
[self hideCommonPages]; | |
CGRect bookPagesFrame = self.bookPagesView.frame; | |
bookPagesFrame.origin.x = [self bookPagesViewOffset]; | |
self.bookPagesView.frame = bookPagesFrame; | |
_processed = NO; | |
[self.delegate pageAnimationComplete]; | |
if (completion) { | |
completion(); | |
} | |
} | |
[self.delegate viewPageAtIndex:index]; | |
} | |
- (void)viewNextPages | |
{ | |
NSUInteger nextIndex = self.pager.index + 1; | |
if (nextIndex > (self.pager.indexes - 1)) { | |
return; | |
} | |
[self viewPagesAtIndex:nextIndex changeTo:BookViewPageChangeToRight animate:YES]; | |
} | |
- (void)viewPrevPages | |
{ | |
if (self.pager.index < 1) { | |
return; | |
} | |
NSUInteger prevIndex = self.pager.index - 1; | |
[self viewPagesAtIndex:prevIndex changeTo:BookViewPageChangeToLeft animate:YES]; | |
} | |
#pragma mark - | |
#pragma mark Presentation | |
- (void)presentationStart | |
{ | |
if (self.pager.indexes == self.pager.index + 1) { | |
// Not run on last page | |
return; | |
} | |
if (_processed) { | |
// Now processed. Not run | |
return; | |
} | |
if (nil == self.audio) { | |
NSURL *path = [[NSBundle mainBundle] URLForResource:self.book.audioFile withExtension:nil]; | |
NSError *error = nil; | |
self.audio = [[AVAudioPlayer alloc] initWithContentsOfURL:path error:&error]; | |
#ifdef DEBUG | |
if (error) { | |
NSLog(@"Error with loading mp3 file %@: %@", path, error); | |
} | |
#endif | |
[self.audio setVolume:0.7]; | |
[self.audio setNumberOfLoops:0]; | |
} | |
NSUInteger activeIndex = self.pager.index; | |
if (activeIndex == 0) { | |
[self.delegate presentationStart]; | |
[self viewPagesAtIndex:1 changeTo:BookViewPageChangeToRight animate:YES completion:^{ | |
self.audio.currentTime = 0; | |
[self.audio prepareToPlay]; | |
[self.audio play]; | |
[self performSelector:@selector(presentationTimerInitialize) withObject:self afterDelay:0.03f]; | |
_presentation = YES; | |
} pauseAudio:NO]; | |
} else if (activeIndex + 1 != self.pager.indexes) { | |
_presentation = YES; | |
[self presentationUpdate]; | |
[self performSelector:@selector(presentationTimerInitialize) withObject:self afterDelay:0.03f]; | |
} | |
} | |
- (void)presentationUpdate | |
{ | |
if (!self.presentation) { | |
return; | |
} | |
[self.delegate presentationStart]; | |
NSUInteger activeIndex = self.pager.index; | |
if (activeIndex == 0) { | |
[self presentationStop]; | |
} else { | |
NSTimeInterval playFromTime; | |
if (self.presentationPauseOn > 0) { | |
playFromTime = self.presentationPauseOn; | |
self.presentationPauseOn = 0; | |
} else { | |
NSNumber *timeMap; | |
timeMap = [self.book.audioPageTimeMap objectAtIndex:activeIndex - 1]; | |
if (!timeMap) { | |
[self presentationStop]; | |
return; | |
} | |
playFromTime = [timeMap doubleValue]; | |
} | |
self.audio.currentTime = playFromTime; | |
[self.audio prepareToPlay]; | |
[self.audio play]; | |
} | |
} | |
- (void)presentationPause | |
{ | |
_presentation = NO; | |
[self presentationTimerDeinitialize]; | |
[self.audio pause]; | |
self.presentationPauseOn = self.audio.currentTime; | |
[self.delegate presentationStop]; | |
} | |
- (void)presentationStop | |
{ | |
_presentation = NO; | |
self.presentationPauseOn = 0; | |
if (self.audio && [self.audio isPlaying]) { | |
[self.audio stop]; | |
} | |
// Clear presentation timer | |
[self presentationTimerDeinitialize]; | |
[self.delegate presentationStop]; | |
} | |
- (void)presentationTimerInitialize | |
{ | |
if (nil == self.presentationTimer) { | |
self.presentationTimer = [NSTimer scheduledTimerWithTimeInterval:0.2f | |
target:self | |
selector:@selector(presentationTimerTick) | |
userInfo:nil | |
repeats:YES]; | |
} | |
} | |
- (void)presentationTimerDeinitialize | |
{ | |
if (self.presentationTimer && [self.presentationTimer isValid]) { | |
[self.presentationTimer invalidate]; | |
self.presentationTimer = nil; | |
} | |
} | |
- (void)presentationTimerTick | |
{ | |
if (self.processed) { | |
return; | |
} | |
int index = [self presentationIndexByTime]; | |
if (![self.audio isPlaying]) { | |
[self presentationStop]; | |
return; | |
} | |
if (index != -1 && index != self.pager.index) { | |
if (index + 1 <= self.pager.indexes) { | |
[self viewPagesAtIndex:index changeTo:BookViewPageChangeToRight animate:YES completion:nil pauseAudio:NO]; | |
} | |
} | |
} | |
- (int)presentationIndexByTime | |
{ | |
NSTimeInterval time = self.audio.currentTime; | |
for (int i = 0; i < [self.book.audioTimeMap count]; i++) { | |
NSNumber *lowMapElement = [self.book.audioTimeMap objectAtIndex:i]; | |
NSTimeInterval low = [lowMapElement doubleValue]; | |
if (i + 1 == [self.book.audioTimeMap count]) { | |
if (time > low) { | |
return i + 1; | |
} | |
} else { | |
NSNumber *hightMapElement = [self.book.audioTimeMap objectAtIndex:i + 1]; | |
NSTimeInterval hight = [hightMapElement doubleValue]; | |
if (time >= low && time < hight) { | |
return i + 1; | |
} | |
} | |
} | |
return -1; | |
} | |
#pragma mark - | |
#pragma mark Animate action | |
- (void)animatePagesViewOffset:(NSInteger)index | |
{ | |
CGRect frame = self.bookPagesView.frame; | |
frame.origin.x = [self bookPagesViewOffset:index]; | |
[UIView animateWithDuration:kBookViewPageChangeTimeout animations:^{ | |
self.bookPagesView.frame = frame; | |
}]; | |
} | |
- (void)animatePagesViewOffset | |
{ | |
[self animatePagesViewOffset:self.pager.index]; | |
} | |
- (void)animatePageWithMode:(BookViewPageAnimate)animate | |
oldView:(UIView *)oldView | |
newView:(UIView *)newView | |
completion:(void (^)(void))completion | |
{ | |
CATransform3D hidesTransform, viewedTransform = CATransform3DIdentity; | |
float factor = M_PI / 180; | |
float m14 = 1.f / (2.5f * (self.pageSize.width * 2)); | |
if (animate == BookViewPageAnimateLeftUp || animate == BookViewPageAnimateLeftDown) { | |
hidesTransform = CATransform3DMakeRotation(-(90 * factor), 0, 1, 0); | |
hidesTransform.m14 = -m14; | |
} else { | |
hidesTransform = CATransform3DMakeRotation(90 * factor, 0, 1, 0); | |
hidesTransform.m14 = m14; | |
} | |
if (animate == BookViewPageAnimateRightDown || animate == BookViewPageAnimateLeftDown) { | |
oldView.layer.zPosition = -1.f; | |
newView.layer.transform = hidesTransform; | |
newView.hidden = NO; | |
[UIView animateWithDuration:kBookViewPageChangeTimeout / 2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ | |
newView.layer.transform = viewedTransform; | |
} completion:^(BOOL finished) { | |
[oldView removeFromSuperview]; | |
completion(); | |
}]; | |
} else if (animate == BookViewPageAnimateRightUp || animate == BookViewPageAnimateLeftUp) { | |
oldView.layer.zPosition = 1.f; | |
newView.hidden = NO; | |
[UIView animateWithDuration:kBookViewPageChangeTimeout / 2 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ | |
oldView.layer.transform = hidesTransform; | |
} completion:^(BOOL finished) { | |
[oldView removeFromSuperview]; | |
completion(); | |
}]; | |
} | |
#ifdef DEBUG | |
else { | |
NSString *reason = [NSString stringWithFormat:@"Invalid animate mode \"%d\".", animate]; | |
@throw [NSException exceptionWithName:@"InvalidArgument" reason:reason userInfo:@{@"mode": [NSNumber numberWithInt:animate]}]; | |
} | |
#endif | |
} | |
#pragma mark - | |
#pragma mark Touch events | |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
UITouch *touch = [touches anyObject]; | |
self.startLocation = [touch locationInView:self.window]; | |
} | |
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event | |
{ | |
UITouch *touch = [touches anyObject]; | |
CGPoint startLocation, endLocation; | |
startLocation = self.startLocation; | |
endLocation = [touch locationInView:self.window]; | |
// Attention: the app work with only landscape orientation, but we must use "y" os. | |
int diffWidth = 0; | |
BookViewTouchOperation operation = BookViewTouchOperationNone; | |
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; | |
switch (orientation) { | |
case UIDeviceOrientationLandscapeLeft: | |
case UIDeviceOrientationLandscapeRight: | |
case UIDeviceOrientationPortrait: | |
case UIDeviceOrientationPortraitUpsideDown: | |
case UIDeviceOrientationFaceUp: | |
case UIDeviceOrientationFaceDown: | |
diffWidth = startLocation.x - endLocation.x; | |
break; | |
default: | |
break; | |
} | |
// Check for pager | |
if (abs(diffWidth) > 50) { | |
if (diffWidth > 0) { | |
operation = BookViewTouchOperationNextPage; | |
} else { | |
operation = BookViewTouchOperationPrevPage; | |
} | |
} | |
// Call method by operation | |
switch (operation) { | |
case BookViewTouchOperationPrevPage: | |
[self viewPrevPages]; | |
break; | |
case BookViewTouchOperationNextPage: | |
[self viewNextPages]; | |
break; | |
default: | |
break; | |
} | |
} | |
#pragma mark - | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment