zoukankan      html  css  js  c++  java
  • iOS 模仿一个小项目,总结一下里边的模块

      ManoBoo:  参考链接:http://www.jianshu.com/p/fd4c46c31508

     这个小的项目是参考ManoBoo的简书的,链接在上方,自己在仿做的过程中,也离不开ManoBoo的帮助

    一 配置项目的基本环境

        1 Pod AVFoundation框架,ReactiveCocoa框架

          -->从终端打开项目  cd+文件路径  

            

          -->创建Podfile文件   vim Podfile 

              

          -->安装第三方框架  pod install

            

             

      

        2 使用pch文件

          -->新建pch文件

            

          -->将需要用到的全局的头文件和宏添加进去

            

        3 导入ttf字体文件并使用

          -->获取字体文件ttf

          -->将ttf文件copy到项目中

          -->在Info.plist中添加项

            

          -->在Project--Build Phases--Copy Bundle Resoueces中添加资源

            

          -->使用(要使用字体的真实名称,而不是文件名)

    二 基础页面的搭建

        根据需要将项目分成相应的模块,在这里按照页面以及功能划分

            

        1 开始页面

                

        1-1 添加按钮

    - (void)setMainView
    {
        _startBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, SCREEN_WIDTH / 2, SCREEN_HEIGHT)];
        _startBtn.backgroundColor = [UIColor blackColor];
        _startBtn.titleLabel.font = [UIFont fontWithName:@"FZMiaoWuS-GB" size:36];
        [_startBtn setTitle:@"开始游戏 " forState:UIControlStateNormal];
    
        WeakSelf;
        [[_startBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
           
            [weakSelf buttonHideAnimation];
            
            [weakSelf presentViewController:[[GameSceneViewController alloc] init] animated:false completion:^{
                [SoundPlayer pauseBGMMusic];
            }];
            
        }];
    
        [self.view addSubview:_startBtn];

        1-2 给按钮添加点击事件

         [_startButton rac_signalForControlEvents:UIControlEventTouchUpInside]返回一个 RACSignal,可以理解为 一个信号,用于监听事件,后面 subscribeNext:^(id x) {} 用于订阅信号

          

        1-3 给按钮添加动画

    # pragma mark : 按钮消失/ 出现动画
    
    - (void)buttonHideAnimation
    {
        [UIView animateWithDuration:0.4 animations:^{
            _startBtn.center = CGPointMake(- _startBtn.center.x, _startBtn.center.y);
            _settingBtn.center = CGPointMake(_settingBtn.center.x + SCREEN_WIDTH, _settingBtn.center.y);
        }];
    }
    
    - (void)buttonShowAnimation
    {
        [UIView animateWithDuration:0.4 animations:^{
            _startBtn.center = CGPointMake(SCREEN_WIDTH / 4, _startBtn.center.y);
            _settingBtn.center = CGPointMake(SCREEN_WIDTH / 4 * 3, _settingBtn.center.y);
        }];
    }

          使用的是UIView的block动画

          

      2 倒计时动画页面    

    - (CAAnimationGroup *)setAnimationGroup
    {
        //缩放动画
        CABasicAnimation *animation1 = [CABasicAnimation animation];
        [animation1 setKeyPath:@"transform.scale"];
        [animation1 setFromValue:@1.0];
        [animation1 setToValue:@4.0];
        [animation1 setDuration:1.0];
        //透明度改变动画
        CABasicAnimation *animation2 = [CABasicAnimation animation];
        [animation2 setKeyPath:@"alpha"];
        [animation2 setFromValue:@1.0];
        [animation2 setToValue:@0.3];
        [animation2 setDuration:1.0];
        
        CAAnimationGroup *animationGroup = [[CAAnimationGroup alloc] init];
        animationGroup.animations = [NSArray arrayWithObjects:animation1, animation2, nil];
        [animationGroup setDuration:1.0];
        [animationGroup setDelegate:self];
        
        return animationGroup;
    }

        使用的是CABasicAnimation基础动画

        这个ManoBoo在简书中已经讲的很详细了,此处略去

      3 游戏界面以及点击按钮之后的闪烁动画效果

        如何搭建游戏界面,在简书中也有非常详细的介绍

        4 点击错误后弹出的失败页面

         

        4-1 此处重点在于如何在xib中使用位置的绑定

        4-2 点击结束按钮后会跳转回开始页面

          用 presentViewController 方法进入游戏页面,对应的,用 dismissViewControllerAnimated 方法可以返回

          用 pushViewController 方法进入的话,则需要调用 popViewControllerAnimated 方法返回

      5 设置页面的搭建

            

        5-1 与错误页面的搭建类似,区别在于其中加入了按钮动画

          5-1 水波纹动画按钮

             将CABasicAnimation基础动画和CAKeyframeAnimation帧动画结合使用  

    - (void)drawRect:(CGRect)rect{
        
        NSInteger pulsingCount = 6;//波纹的环数
        double animationDuration = 5;
        
        CALayer * animationLayer = [CALayer layer];//动画layer
        
        for (int i = 0; i < pulsingCount; i++) {
            //保证波纹是个圆形
            CALayer * pulsingLayer = [CALayer layer];
            pulsingLayer.frame = CGRectMake(0, 0, rect.size.width, rect.size.height);
            pulsingLayer.borderColor = [UIColor colorWithRed:54 green:83 blue:254 alpha:1].CGColor;
            pulsingLayer.borderWidth = 1;
            pulsingLayer.cornerRadius = rect.size.height / 2;
            
            //缩放动画
            CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
            scaleAnimation.fromValue = @0.8;
            scaleAnimation.toValue = @2.4;
            
            //关键帧动画  透明度
            CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
            opacityAnimation.values = @[@1, @0.9, @0.8, @0.7, @0.6, @0.5, @0.4, @0.3, @0.2, @0.1, @0];
            opacityAnimation.keyTimes = @[@0, @0.1, @0.2, @0.3, @0.4, @0.5, @0.6, @0.7, @0.8, @0.9, @1];
            
            //动画执行的速度 默认
            CAMediaTimingFunction * defaultCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
            
            CAAnimationGroup * animationGroup = [CAAnimationGroup animation];
            animationGroup.beginTime = CACurrentMediaTime() + (double)i * animationDuration / (double)pulsingCount;
            animationGroup.duration = animationDuration;
            animationGroup.repeatCount = HUGE;
            animationGroup.timingFunction = defaultCurve;//动画运行的节奏
            animationGroup.animations = @[scaleAnimation, opacityAnimation];
            
            [pulsingLayer addAnimation:animationGroup forKey:nil];
            [animationLayer addSublayer:pulsingLayer];
    
        }
        
        [self.layer addSublayer:animationLayer];
        
    }

        5-2 圆形按钮

          重绘drawRect方法    

    - (void)drawRect:(CGRect)rect {
       
        self.backgroundColor = [UIColor clearColor];
        self.layer.borderWidth = 1;
        self.layer.borderColor = [UIColor whiteColor].CGColor;
        self.layer.cornerRadius = rect.size.width / 2;
        
    }

        5-3 点击去简书按钮之后会跳转到其他页面    

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.jianshu.com/users/28aed6e79eac/timeline"]];

    三 相应的功能模块

      1 分数管理器

        1-1 使用 NSUserDefaults 本地存储,对全局变量bestScore进行更新,若currentScore小于bestScore,则bestScore不变,否则,将currentScore的值赋给bestScore 

    + (float)getBestScore{
        
        NSNumber *bestScore = [[NSUserDefaults standardUserDefaults]  objectForKey:BestScoreKey];
        if(bestScore == nil){
            [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:0.0] forKey:BestScoreKey];
            bestScore = [NSNumber numberWithFloat:0.0];
        }
        
        return bestScore.floatValue;
    }
    
    + (void)updateWithCurrentScore:(float)currentScore{
        
        float bestScore = [self getBestScore];
        bestScore = bestScore < currentScore ? currentScore : bestScore;
        [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:bestScore] forKey:BestScoreKey];
    }

          1-2 在失败页面中显示出来以及在控制台中输出   

        _failureView.currentScoreLab.text = [NSString stringWithFormat:@"%.1f",_currentScore];
        _failureView.historyScoreLab.text = [NSString stringWithFormat:@"最佳成绩:%.f",[ScoreManager getBestScore]];
        _failureView.tipLab.text = @"再接再励哦";
        
        NSLog(@"历史最佳:%.1f",[ScoreManager getBestScore]);
        NSLog(@"当前成绩:%.1f",_currentScore);

        1-3 使用_currentScore来计算当前点击正确的按钮数,即分数,若按钮的状态为胜利,则加一

        if (type == CompleteTypeVictory) {
            _currentScore += 1.0;
            
            [[SoundPlayer shareInstance] changeSound];
            [self updateClickOnTrue];
            [self updateGameSpeed];
            
        }

      2 速度控制器    

    - (void)updateGameSpeed{
        
        if (_currentScore < 200) {
            _gameSpeed = 2.5 * (1 + _currentScore / 200);
        }else{
            _gameSpeed = 5.0;
        }
        
        [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithFloat:_gameSpeed] forKey:GameSpeedKey];
    }

      3 音乐播放器

        使用AVAudioPlayer控制播放音乐    

    dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_async(dispatchQueue, ^{
                //获取音乐文件路径
            NSURL *soundUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"C-%d",i]  ofType:@"wav"]];
            if (!_audioPlayer) {
                _asset = [[AVURLAsset alloc] initWithURL:soundUrl options:nil];
                //实例化音乐播放控件
                _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:_asset.URL error:nil];
            }
            if (_audioPlayer != nil) {
                    
                _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"C-%d",i] ofType:@"wav"]] error:nil];
                _audioPlayer.delegate = self;
                [_audioPlayer play];
                
                if (i < 213) {
                    ++i;
                }else{
                    i = 1;
                }
    
            }else{
                NSLog(@"初始化失败");
            }
        });

    四 项目整体都使用的重点

      1 单例模式

        若希望一个类只有一个实例存在,同时可得到这个特定实例提供的服务入口,那么可以使用单例设计模式

          在整个项目中,这个累的对象只能被初始化一次,这种特性可以用在某些需要全局共享的资源中,比如管理类,引擎类

          UIApplication,NSUserDefaults等都是iOS的系统单例

    static SoundPlayer *instance;
    
    //单例模式,保证只有一个soundPlayer对象
    
    + (SoundPlayer *)shareInstance{
        @synchronized(instance) {
            if (!instance) {
                instance = [[SoundPlayer alloc] init];
            }
            return instance;
        }
    }

      2 多线程

        iOS中,只有主线程,才能立即刷新UI,如果是通过侦听异步消息,触发回调函数,或者调用异步方法,请求刷新UI,都会产生线程阻塞和延迟的问题

         多线程:让CPU在同一时间内运行两行以上代码

         iOS是一个支持多任务的操作系统,因此在编写iOS的多线程程序时,可分为两个部分:传统的多线程处理方式(NSThread)或者使用GCD派遣队列(Dispatch Queue)与block块来分派程序

         GCD提供了3中类型的队列:main,concurrent,serial

            main队列:程序代码默认运行的队列,iOS会将所有跟可视化组件有关的运行程序放在这个队列,main队列一定要使用异步模式,若使用同步模式,会造成app运行程序产生死结

            concurr队列:有4 种(优先权),高优先权,中优先权,低优先权,背景取得concurrent队列的进入点后,可以选择异步或同步运行

            serial队列:用户自己创建的队列,队列创建时徐给出唯一识别的名字(字符串型),serial队列一定是dispatch_sync()

         每个区块中程序代码在各种队列中运行有两种方式:同步,异步

            同步:(sync)同一队列,被设置为同步模式的线程一定要先运行晚才回轮到下一个线程运行:dispatch_sync()

            异步:(async)同一队列所有线程一起执行:dispatch_async()

        dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
        
        dispatch_async(dispatchQueue, ^{
    
        
        });                     

      3 weakSelf

         防止block的循环引用 

    #define WeakSelf __weak typeof(self) weakSelf = self
  • 相关阅读:
    又一道简单的题
    atoi函数的使用(将字符串转换成整型数)
    【贪心】Radar Installation(POJ1328)
    【BFS】鸣人与佐助
    谍报分析
    适配器模式(C++实现)
    策略模式(C++)
    工厂模式(C++实现)
    桥接模式(C++实现)
    关于getMemory函数的几点思考
  • 原文地址:https://www.cnblogs.com/roxy/p/5994562.html
Copyright © 2011-2022 走看看