Swiftgram/third-party/RMIntro/platform/ios/RMIntroViewController.m
2017-02-11 17:05:23 +03:00

618 lines
19 KiB
Objective-C

//
// RMIntroViewController.m
// IntroOpenGL
//
// Created by Ilya Rimchikov on 19/01/14.
//
//
#import "RMGeometry.h"
#import "RMIntroViewController.h"
#import "RMIntroPageView.h"
#include "animations.h"
#include "objects.h"
#include "texture_helper.h"
static UIColor *TGColorWithHex(int hex)
{
return [[UIColor alloc] initWithRed:(((hex >> 16) & 0xff) / 255.0f) green:(((hex >> 8) & 0xff) / 255.0f) blue:(((hex) & 0xff) / 255.0f) alpha:1.0f];
}
#define TGLog NSLog
#define TGLocalized(x) NSLocalizedString(x, @"")
static UIColor *TGAccentColor() {
return TGColorWithHex(0x007ee5);
}
static void TGDispatchOnMainThread(dispatch_block_t block) {
if ([NSThread isMainThread]) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
@interface UIScrollView (CurrentPage)
- (int)currentPage;
- (void)setPage:(NSInteger)page;
- (int)currentPageMin;
- (int)currentPageMax;
@end
@implementation UIScrollView (CurrentPage)
- (int)currentPage
{
CGFloat pageWidth = self.frame.size.width;
return (int)floor((self.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
}
- (int)currentPageMin
{
CGFloat pageWidth = self.frame.size.width;
return (int)floor((self.contentOffset.x - pageWidth / 2 - pageWidth / 2) / pageWidth) + 1;
}
- (int)currentPageMax
{
CGFloat pageWidth = self.frame.size.width;
return (int)floor((self.contentOffset.x - pageWidth / 2 + pageWidth / 2 ) / pageWidth) + 1;
}
- (void)setPage:(NSInteger)page
{
self.contentOffset = CGPointMake(self.frame.size.width*page, 0);
}
@end
@interface RMIntroViewController () <UIGestureRecognizerDelegate>
{
id _didEnterBackgroundObserver;
id _willEnterBackgroundObserver;
UIImageView *_stillLogoView;
bool _displayedStillLogo;
}
@end
@implementation RMIntroViewController
- (instancetype)init
{
self = [super init];
if (self != nil)
{
self.automaticallyAdjustsScrollViewInsets = false;
_headlines = @[ TGLocalized(@"Tour.Title1"), TGLocalized(@"Tour.Title2"), TGLocalized(@"Tour.Title6"), TGLocalized(@"Tour.Title3"), TGLocalized(@"Tour.Title4"), TGLocalized(@"Tour.Title5")];
_descriptions = @[TGLocalized(@"Tour.Text1"), TGLocalized(@"Tour.Text2"), TGLocalized(@"Tour.Text6"), TGLocalized(@"Tour.Text3"), TGLocalized(@"Tour.Text4"), TGLocalized(@"Tour.Text5")];
__weak RMIntroViewController *weakSelf = self;
_didEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(__unused NSNotification *notification)
{
__strong RMIntroViewController *strongSelf = weakSelf;
[strongSelf stopTimer];
}];
_willEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification object:nil queue:nil usingBlock:^(__unused NSNotification *notification)
{
__strong RMIntroViewController *strongSelf = weakSelf;
[strongSelf loadGL];
[strongSelf startTimer];
}];
}
return self;
}
- (void)startTimer
{
if (_updateAndRenderTimer == nil)
{
_updateAndRenderTimer = [NSTimer timerWithTimeInterval:1.0f / 60.0f target:self selector:@selector(updateAndRender) userInfo:nil repeats:true];
[[NSRunLoop mainRunLoop] addTimer:_updateAndRenderTimer forMode:NSRunLoopCommonModes];
}
}
- (void)stopTimer
{
if (_updateAndRenderTimer != nil)
{
[_updateAndRenderTimer invalidate];
_updateAndRenderTimer = nil;
}
}
- (void)loadView
{
[super loadView];
}
- (void)loadGL
{
if (/*[[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground*/true && !_isOpenGLLoaded)
{
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context)
NSLog(@"Failed to create ES context");
bool isIpad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
CGFloat size = 200;
if (isIpad)
size *= 1.2;
int height = 50;
if (isIpad)
height += 138 / 2;
_glkView = [[GLKView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width / 2 - size / 2, height, size, size) context:context];
_glkView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
_glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
_glkView.drawableMultisample = GLKViewDrawableMultisample4X;
_glkView.enableSetNeedsDisplay = false;
_glkView.userInteractionEnabled = false;
_glkView.delegate = self;
int patchHalfWidth = 1;
UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(-patchHalfWidth, -patchHalfWidth, _glkView.frame.size.width + patchHalfWidth * 2, patchHalfWidth * 2)];
UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(-patchHalfWidth, -patchHalfWidth, patchHalfWidth * 2, _glkView.frame.size.height + patchHalfWidth * 2)];
UIView *v3 = [[UIView alloc] initWithFrame:CGRectMake(-patchHalfWidth, -patchHalfWidth + _glkView.frame.size.height, _glkView.frame.size.width + patchHalfWidth * 2, patchHalfWidth * 2)];
UIView *v4 = [[UIView alloc] initWithFrame:CGRectMake(-patchHalfWidth + _glkView.frame.size.width, -patchHalfWidth, patchHalfWidth * 2, _glkView.frame.size.height + patchHalfWidth * 2)];
v1.backgroundColor = v2.backgroundColor = v3.backgroundColor = v4.backgroundColor = [UIColor whiteColor];
[_glkView addSubview:v1];
[_glkView addSubview:v2];
[_glkView addSubview:v3];
[_glkView addSubview:v4];
[self setupGL];
[self.view addSubview:_glkView];
[self startTimer];
_isOpenGLLoaded = true;
}
}
- (void)freeGL
{
if (!_isOpenGLLoaded)
return;
[self stopTimer];
if ([EAGLContext currentContext] == _glkView.context)
[EAGLContext setCurrentContext:nil];
_glkView.context = nil;
context = nil;
[_glkView removeFromSuperview];
_glkView = nil;
_isOpenGLLoaded = false;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self loadGL];
bool isIpad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
_pageScrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];
_pageScrollView.clipsToBounds = true;
_pageScrollView.opaque = true;
_pageScrollView.clearsContextBeforeDrawing = false;
[_pageScrollView setShowsHorizontalScrollIndicator:false];
[_pageScrollView setShowsVerticalScrollIndicator:false];
_pageScrollView.pagingEnabled = true;
_pageScrollView.contentSize = CGSizeMake(_headlines.count * self.view.bounds.size.width, self.view.bounds.size.height);
_pageScrollView.delegate = self;
[self.view addSubview:_pageScrollView];
_pageViews = [NSMutableArray array];
for (NSUInteger i = 0; i < _headlines.count; i++)
{
RMIntroPageView *p = [[RMIntroPageView alloc]initWithFrame:CGRectMake(i * self.view.bounds.size.width, 0, self.view.bounds.size.width, 0) headline:[_headlines objectAtIndex:i] description:[_descriptions objectAtIndex:i]];
p.opaque = true;
p.clearsContextBeforeDrawing = false;
[_pageViews addObject:p];
[_pageScrollView addSubview:p];
}
[_pageScrollView setPage:0];
_startButton = [[UIButton alloc] init];
[_startButton setTitle:TGLocalized(@"Tour.StartButton") forState:UIControlStateNormal];
[_startButton.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:isIpad ? 55 / 2.0f : 21]];
[_startButton setTitleColor:TGAccentColor() forState:UIControlStateNormal];
_startArrow = [[UIImageView alloc]initWithImage:[UIImage imageNamed:isIpad ? @"start_arrow_ipad.png" : @"start_arrow.png"]];
_startButton.titleLabel.clipsToBounds = false;
_startArrow.frame = CGRectChangedOrigin(_startArrow.frame, CGPointMake([_startButton.titleLabel.text sizeWithFont:_startButton.titleLabel.font].width + (isIpad ? 7 : 6), isIpad ? 6.5f : 4.5f));
[_startButton.titleLabel addSubview:_startArrow];
[self.view addSubview:_startButton];
_pageControl = [[UIPageControl alloc] init];
_pageControl.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin;
_pageControl.userInteractionEnabled = false;
[_pageControl setPageIndicatorTintColor:[UIColor colorWithWhite:.85 alpha:1]];
[_pageControl setCurrentPageIndicatorTintColor:[UIColor colorWithWhite:.2 alpha:1]];
[_pageControl setNumberOfPages:6];
[self.view addSubview:_pageControl];
}
- (BOOL)shouldAutorotate
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
return true;
return false;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
return UIInterfaceOrientationMaskAll;
return UIInterfaceOrientationMaskPortrait;
}
- (DeviceScreen)deviceScreen
{
CGSize viewSize = self.view.frame.size;
int max = (int)MAX(viewSize.width, viewSize.height);
DeviceScreen deviceScreen = Inch55;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
switch (max)
{
case 1366:
deviceScreen = iPadPro;
break;
default:
deviceScreen = iPad;
break;
}
}
else
{
switch (max)
{
case 480:
deviceScreen = Inch35;
break;
case 568:
deviceScreen = Inch4;
break;
case 667:
deviceScreen = Inch47;
break;
default:
deviceScreen = Inch55;
break;
}
}
return deviceScreen;
}
- (void)viewWillLayoutSubviews
{
UIInterfaceOrientation isVertical = (self.view.bounds.size.height / self.view.bounds.size.width > 1.0f);
CGFloat statusBarHeight = 0.0f;
CGFloat pageControlY = 0;
CGFloat glViewY = 0;
CGFloat startButtonY = 0;
CGFloat pageY = 0;
DeviceScreen deviceScreen = [self deviceScreen];
bool landscape = self.view.bounds.size.width > self.view.bounds.size.height;
switch (deviceScreen)
{
case iPad:
pageControlY = 386 / 2;
glViewY = isVertical ? 121 + 90 : 121;
startButtonY = 120;
pageY = isVertical ? 485 : 335;
break;
case iPadPro:
pageControlY = 386 / 2;
glViewY = isVertical ? 221 + 110 : 221;
startButtonY = 120;
pageY = isVertical ? 605 : 435;
break;
case Inch35:
if (landscape) {
pageControlY = 80;
glViewY = -200;
startButtonY = 75;
pageY = 38;
} else {
pageControlY = 162 / 2;
glViewY = 62 - 20;
startButtonY = 75;
pageY = 215;
}
break;
case Inch4:
if (landscape) {
pageControlY = 80;
glViewY = -200;
startButtonY = 75;
pageY = 38;
} else {
pageControlY = 162 / 2;
glViewY = 62;
startButtonY = 75;
pageY = 245;
}
break;
case Inch47:
if (landscape) {
pageControlY = 162 / 2 + 10;
glViewY = -200;
startButtonY = 75 + 5;
pageY = 70;
} else {
pageControlY = 162 / 2 + 10;
glViewY = 62 + 25;
startButtonY = 75 + 5;
pageY = 245 + 50;
}
break;
case Inch55:
if (landscape) {
pageControlY = 162 / 2 + 20;
glViewY = -200;
startButtonY = 75 + 20;
pageY = 76;
} else {
pageControlY = 162 / 2 + 20;
glViewY = 62 + 45;
startButtonY = 75 + 20;
pageY = 245 + 85;
}
break;
default:
break;
}
_pageControl.frame = CGRectMake(0, self.view.bounds.size.height - pageControlY - statusBarHeight, self.view.bounds.size.width, 7);
_glkView.frame = CGRectChangedOriginY(_glkView.frame, glViewY - statusBarHeight);
_startButton.frame = CGRectMake(-9, self.view.bounds.size.height - startButtonY - statusBarHeight, self.view.bounds.size.width, startButtonY - 4);
[_startButton addTarget:self action:@selector(startButtonPress) forControlEvents:UIControlEventTouchUpInside];
_pageScrollView.frame=CGRectMake(0, 20, self.view.bounds.size.width, self.view.bounds.size.height - 20);
_pageScrollView.contentSize=CGSizeMake(_headlines.count * self.view.bounds.size.width, 150);
_pageScrollView.contentOffset = CGPointMake(_currentPage * self.view.bounds.size.width, 0);
[_pageViews enumerateObjectsUsingBlock:^(UIView *pageView, NSUInteger index, __unused BOOL *stop)
{
pageView.frame = CGRectMake(index * self.view.bounds.size.width, (pageY - statusBarHeight), self.view.bounds.size.width, 150);
}];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_stillLogoView == nil && !_displayedStillLogo)
{
_displayedStillLogo = true;
_stillLogoView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"telegram_logo_still.png"]];
_stillLogoView.contentMode = UIViewContentModeCenter;
_stillLogoView.bounds = CGRectMake(0, 0, 200, 200);
UIInterfaceOrientation isVertical = (self.view.bounds.size.height / self.view.bounds.size.width > 1.0f);
CGFloat statusBarHeight = 0.0f;
CGFloat glViewY = 0;
DeviceScreen deviceScreen = [self deviceScreen];
switch (deviceScreen)
{
case iPad:
glViewY = isVertical ? 121 + 90 : 121;
break;
case iPadPro:
glViewY = isVertical ? 221 + 110 : 221;
break;
case Inch35:
glViewY = 62 - 20;
break;
case Inch4:
glViewY = 62;
break;
case Inch47:
glViewY = 62 + 25;
break;
case Inch55:
glViewY = 62 + 45;
break;
default:
break;
}
_stillLogoView.frame = CGRectChangedOriginY(_glkView.frame, glViewY - statusBarHeight);
[self.view addSubview:_stillLogoView];
}
[self loadGL];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (_stillLogoView != nil)
{
[_stillLogoView removeFromSuperview];
_stillLogoView = nil;
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self freeGL];
}
- (void)startButtonPress
{
if (_startMessaging) {
_startMessaging();
}
}
- (void)updateAndRender
{
[_glkView display];
TGDispatchOnMainThread(^
{
if (_stillLogoView != nil)
{
[_stillLogoView removeFromSuperview];
_stillLogoView = nil;
}
});
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:_didEnterBackgroundObserver];
[[NSNotificationCenter defaultCenter] removeObserver:_willEnterBackgroundObserver];
[self freeGL];
}
- (void)setupGL
{
[EAGLContext setCurrentContext:_glkView.context];
set_telegram_textures(setup_texture(@"telegram_sphere.png"), setup_texture(@"telegram_plane.png"));
set_ic_textures(setup_texture(@"ic_bubble_dot.png"), setup_texture(@"ic_bubble.png"), setup_texture(@"ic_cam_lens.png"), setup_texture(@"ic_cam.png"), setup_texture(@"ic_pencil.png"), setup_texture(@"ic_pin.png"), setup_texture(@"ic_smile_eye.png"), setup_texture(@"ic_smile.png"), setup_texture(@"ic_videocam.png"));
set_fast_textures(setup_texture(@"fast_body.png"), setup_texture(@"fast_spiral.png"), setup_texture(@"fast_arrow.png"), setup_texture(@"fast_arrow_shadow.png"));
set_free_textures(setup_texture(@"knot_up.png"), setup_texture(@"knot_down.png"));
set_powerful_textures(setup_texture(@"powerful_mask.png"), setup_texture(@"powerful_star.png"), setup_texture(@"powerful_infinity.png"), setup_texture(@"powerful_infinity_white.png"));
set_private_textures(setup_texture(@"private_door.png"), setup_texture(@"private_screw.png"));
set_need_pages(0);
on_surface_created();
on_surface_changed(200, 200, 1, 0,0,0,0,0);
}
#pragma mark - GLKView delegate methods
- (void)glkView:(GLKView *)__unused view drawInRect:(CGRect)__unused rect
{
double time = CFAbsoluteTimeGetCurrent();
set_page((int)_currentPage);
set_date(time);
on_draw_frame();
}
static CGFloat x;
static bool justEndDragging;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)__unused decelerate
{
x = scrollView.contentOffset.x;
justEndDragging = true;
}
NSInteger _current_page_end;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offset = (scrollView.contentOffset.x - _currentPage * scrollView.frame.size.width) / self.view.frame.size.width;
set_scroll_offset((float)offset);
if (justEndDragging)
{
justEndDragging = false;
CGFloat page = scrollView.contentOffset.x / scrollView.frame.size.width;
CGFloat sign = scrollView.contentOffset.x - x;
if (sign > 0)
{
if (page > _currentPage)
_currentPage++;
}
if (sign < 0)
{
if (page < _currentPage)
_currentPage--;
}
_currentPage = MAX(0, MIN(5, _currentPage));
_current_page_end = _currentPage;
}
else
{
if (_pageScrollView.contentOffset.x > _current_page_end*_pageScrollView.frame.size.width)
{
if (_pageScrollView.currentPageMin > _current_page_end) {
_currentPage = [_pageScrollView currentPage];
_current_page_end = _currentPage;
}
}
else
{
if (_pageScrollView.currentPageMax < _current_page_end)
{
_currentPage = [_pageScrollView currentPage];
_current_page_end = _currentPage;
}
}
}
[_pageControl setCurrentPage:_currentPage];
}
@end