热门搜索 :
考研考公
您的当前位置:首页正文

iOS项目开发实战经验总结

来源:东饰资讯网

前言

很久没有更新博客了,这期间一直忙着在公司做版本迭代更新,翻出很久前写过的个人项目,打算重新整理一下上架到App store。又重新收获了很多东西,在技术方面也得到了很大的提升,现将项目实际开发中需要用到的网站,工具,技术分享给大家,全是干货,欢迎点赞。


网站

    • 全世界技术大牛的聚集地,一个问问题的好地方
    • 使用前提
      • 有一定的英语基础,并且懂得怎么问问题
      • 具体怎么使用可以查看官网的帮助
    • 全球最大的同性交友网站(哈哈)
    • 找Demo的好地方
    • 同样需要有一定的英语基础
    • 作为程序员,有问题找谷歌
    • 不会翻墙的程序员不是好程序员
    • 简书上有时还是会有一些不错的教程分享
    • 好处是不需要英文基础
  • JSON 工具
  • 在线代码格式化
  • 一键生成多尺寸APP图标

工具

  • 抓包工具:
  • 取色工具:
  • 翻墙工具:

技术

引导页的设置

  • 创建一个继承于UIViewController的类,取名LeadingPageViewController
  • 在.m方法中的代码如下
#import "LeadingPageViewController.h"
#import "AppDelegate.h"

@interface LeadingPageViewController ()

<
    UIScrollViewDelegate
>

{
    // 创建页码控制器
    UIPageControl *pageControl;
    // 判断是否是第一次进入应用
    BOOL flag;
}

@end

@implementation LeadingPageViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建一个UIScrollVie
    UIScrollView *myScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, HEIGHT)];
    
    for (int i = 0; i <= 3; i++) {
        
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(WIDTH * i, 0, WIDTH, HEIGHT)];
        // 在最后一页设置按钮
        if (3 == i) {
            // 设置用户交互
            imageView.userInteractionEnabled = YES;
            
            // 创建Button
            UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
            button.frame = CGRectMake(WIDTH / 3, HEIGHT * 7 / 8, WIDTH / 3, HEIGHT / 20);
            [button setTitle:@"点击进入应用" forState:UIControlStateNormal];
            // 设置圆角
            button.layer.borderWidth = 1;
            button.layer.cornerRadius = 5;
            button.clipsToBounds = YES;
            // 设置button边框颜色
            button.layer.borderColor = [UIColor whiteColor].CGColor;
            [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
            button.titleLabel.font = [UIFont systemFontOfSize: 15];
            [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
            [imageView addSubview:button];
        }
        imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"guidePage%d", i]];

        [myScrollView addSubview:imageView];
    }
    // 关闭回弹效果
    myScrollView.bounces = NO;
    // 整页翻动
    myScrollView.pagingEnabled = YES;
    // 隐藏水平方向滑动条
    myScrollView.showsHorizontalScrollIndicator = NO;
    
    myScrollView.contentSize = CGSizeMake(WIDTH * 4, HEIGHT);
    myScrollView.delegate = self;
    [self.view addSubview:myScrollView];
    
    // pageControl
    pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(WIDTH / 3, HEIGHT * 15 / 16, WIDTH / 3, HEIGHT / 16)];
    pageControl.numberOfPages = 4;
    pageControl.pageIndicatorTintColor = [UIColor grayColor];
    pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
    [self.view addSubview:pageControl];
    
}

#pragma mark - UIScrollView协议方法

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
    // 计算当前在第几页
    pageControl.currentPage = (NSInteger)(scrollView.contentOffset.x / [UIScreen mainScreen].bounds.size.width);
}

#pragma mark - 点击按钮保存数据并切换根视图控制器

- (void)buttonAction:(UIButton *)button {
    
    flag = YES;
    NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
    // 保存用户数据
    [userDef setBool:flag forKey:@"notFirst"];
    [userDef synchronize];

    // 切换到根视图控制器
    AppDelegate *appDelegate =  (AppDelegate*)[UIApplication sharedApplication].delegate;
    self.view.window.rootViewController = appDelegate.rootTabBarController;

}

@end



  • 在AppDelegate.h中
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (nonatomic, retain) UITabBarController *rootTabBarController;

@end
  • 在AppDelegate.m中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    self.rootTabBarController = [[UITabBarController alloc] init];
    _rootTabBarController.viewControllers = @[
                                             UINavigationController1,
                                             UINavigationController2,
                                             UINavigationController3,
                                             ...
                                             ];

    NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
    // 判断是否第一次进入应用
    if (![userDef boolForKey:@"notFirst"]) {
        // 如果第一次,进入引导页
        self.window.rootViewController = [[LeadingPageViewController alloc] init];
    } else {
        // 否则直接进入应用
        self.window.rootViewController = _rootTabBarController;
    }
    return YES;
}
  • 效果图

APP应用名称,图标和启动页的设置

设置应用的名称

  • 选择你的工程,选中“Info”,再点击“Bundle Name”,在后面填上我们需要修改的应用名称即可。



设置应用的图标

  • 首先在工程中选中assets.xcassets,然后选择“AppIcon”,把预先准备好的各个尺寸的icon拖到相应的框中。


  • icon图标大小一定要正确,否则,图标显示不正常。
  • icon图标大小为:
    • 29 x 29
    • 57 x 57
    • 58 x 58
    • 80 x 80
    • 87 x 87
    • 120 x 120
    • 180 x 180

设置启动图

  • 首先仍然选择“assets.xcassets”,在“+”点击加号,选择“App Icon & Launch Images”中“New iOS Launch Image


  • 如果已经有了“LaunchImage”,把对应的启动图拖进来就可以了。


  • 选择你的工程名称,TARGETS的“General”,将“Launch Image Sources”选择对应的启动图的名称,“Launch Screen File”什么都不用选即可。

  • 对应图片尺寸

    • 1x:320 x 480
    • 2x:640 x 960
    • Retina 4:640 x 1136
    • Retina HD 4.7:750 x 1334
    • Retina HD 5.5:1242 x 2208
  • 效果图
  • 如果没有看到添加的启动图
    • 如果按照上面的步骤,且设置了Launch Images Srouce 为LaunchImage,但是启动图片仍然没有
    • 在工程Main.storyboard的下,看一看Use as launch Screen是否勾选了,如果勾选了,则取消勾选项,如图:
  • 最后,可能还是没有看到启动图,那只好将应用删除,重新运行该工程

3DTouch实现快速启动

  • 方法一:静态设置

    • 通过plist文件配置,配置信息如图
  • 方法二:动态设置

    • 在AppDelegate当中写
        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        //设置图标长按时,弹出的样式.
        //iconWithType:图标的类型
        UIApplicationShortcutIcon *icon0 =
        [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeSearch];
        UIApplicationShortcutIcon *icon1 =
        [UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeContact];
    
        //创建第一个标题
        UIApplicationShortcutItem *item0 = [[UIApplicationShortcutItem alloc] initWithType:@"Second"
            localizedTitle:@"搜索"
            icon:icon0
        ];
        //创建第二个标题
        UIApplicationShortcutItem *item1 = [[UIApplicationShortcutItem alloc] initWithType:@"Fourth"
            localizedTitle:@"主页"
            icon:icon0
        ];
        //创建第三个标题
        //...
        //创建第四个标题
        //...
    
        //设置shortcutItems
        application.shortcutItems = @[item0,item1];
        return YES;
    }
  • 效果图
  • 实现进入对应页面的功能
  • 同样在AppDelegate.m中实现如下方法
- (void)application:(UIApplication *)application performActionForShortcutItem:
(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler {
    
    UITabBarController *mytab = (UITabBarController*)self.window.rootViewController;
    if ([shortcutItem.type isEqual:@"First"])
    {
        mytab.selectedIndex = 1;
 
    }
    else if([shortcutItem.type isEqual:@"Second"])
    {
        SearchViewController *searchVC = [[SearchViewController alloc] init];
        searchVC.hidesBottomBarWhenPushed = YES;
        UINavigationController *myNAV = [mytab.viewControllers firstObject];
        [myNAV pushViewController:searchVC animated:YES];
       
    }else if([shortcutItem.type isEqual:@"Third"]){
        mytab.selectedIndex = 3;
    }else {
        mytab.selectedIndex = 4;
    }
}

3DTouch实现Peek和Pop

  • Peek专注于预览,Pop可以全面展现内容。
  • 步骤
    • 1.先签协议<UIViewControllerPreviewingDelegate>
    • 2.给tableView或者collectionView的cell注册3DTouch
    • 3.实现协议方法
  • 示例
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:collectionCellIdentifier forIndexPath:indexPath];
    
    if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) {
        //注册Cell支持3DTouch,并设置代理
        [self registerForPreviewingWithDelegate:self sourceView:cell];
    }
    
    UIImageView *imageView = [[UIImageView alloc] init];
    // 取出model
    UserAlbumModel *userAlbumModel = _userAlbumArray[indexPath.item];
    
    Photo *photo = userAlbumModel.photo;
    NSString *string = photo.path;
    NSArray *array = [string componentsSeparatedByString:@"_webp"];
    [imageView sd_setImageWithURL:[NSURL URLWithString:[array firstObject]]];
    // 为Cell赋值
    cell.backgroundView = imageView;
    
    return cell;
}
  • 实现协议方法
#pragma mark - 3DTouch协议方法
- (nullable UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location{
    
    // 防止重复覆盖
    if (self.presentedViewController!=nil){
        return nil;
    }
    // 拿到索引值
    NSIndexPath *indexPath = [_collectionView indexPathForCell:(UICollectionViewCell *)[previewingContext sourceView]];
    // 从model里取值
    UserAlbumModel *userAlbum = _userAlbumArray[indexPath.item];
    // 设置Pop对应的页面
    DetailAlbumViewController *detaiAlbumVC = [[DetailAlbumViewController alloc] init];
    // 传值
    detaiAlbumVC.blog_id = userAlbum.ID;
    //设置弹出peek的高度(设置宽度是没有效果的)
    detaiAlbumVC.preferredContentSize = CGSizeMake(0, 500);
    //在这里想弹一个带有导航条的控制器,控制器里面包装一个导航条.直接返回导航控制器.那么就会peek出一个导航控制器.
    return [[UINavigationController alloc] initWithRootViewController:detaiAlbumVC];
    
}

//弹框出现后,继续重按时调用
//viewControllerToCommit:就是上面传入的DetailAlbumViewController的控制器.
//commitViewController默认是UIViewController,因为peek时返回的控制器是一个导航控制器.那么在这里面自己手动改成的导航控制器.
 
- (void)previewingContext:(id <UIViewControllerPreviewing>)previewingContext commitViewController:(UINavigationController *)viewControllerToCommit{
    //获取导航控制器的根控制器.因为当前已经是一个导航控制器了,不能再继续push一个导航控制器,所以要先获取peek的导航控制器里面的根控制器.
    //然后再拿当前的控制器把获取的控制器push进去.
    DetailAlbumViewController *detaiAlbumVC = viewControllerToCommit.childViewControllers.lastObject;
    [self.navigationController pushViewController:detaiAlbumVC animated:YES];
    
    //使用show和push是一样的效果
    //[self showViewController:viewControllerToCommit sender:self];
    
}

  • Peek效果
  • 重压后Pop效果

Touch ID的使用

  • 现在为了安全,对于指纹解锁的应用越来越多.苹果有完善的 API, 只要简单的调用就可以实现.下面看一下怎么使用.

  • 第一步:导入需要的框架

#import <LocalAuthentication/LocalAuthentication.h>
  • 第二步:初始化LAContext对象

LAContext *context = [[LAContext alloc] init];
/*
 * 这个属性用来设置指纹错误后的弹出框的按钮文字
 * 不设置默认文字为“输入密码”
 * 设置@""将不会显示指纹错误后的弹出框
*/

context.localizedFallbackTitle = @"";
BOOL isSupport = [context canEvaluatePolicy:kLAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
  • 第三步:处理业务逻辑
            if (isSupport) {
                [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                        localizedReason:@"指纹验证"
                                  reply:^(BOOL success, NSError * _Nullable error) {
                                      if (success) {
                                          //验证成功执行
                                          NSLog(@"指纹识别成功");
                                          //在主线程刷新view,不然会有卡顿
                                          dispatch_async(dispatch_get_main_queue(), ^{
                                              MyCollectionViewController *myCollecVC = [[MyCollectionViewController alloc] init];
                                              myCollecVC.hidesBottomBarWhenPushed = YES;
                                              myCollecVC.userID = _user.ID;
                                              [self.navigationController pushViewController:myCollecVC animated:YES];
                                          });
                                      } else {
                                          if (error.code == kLAErrorUserFallback) {
                                              //Fallback按钮被点击执行
                                              NSLog(@"Fallback按钮被点击");
                                          } else if (error.code == kLAErrorUserCancel) {
                                              //取消按钮被点击执行
                                              NSLog(@"取消按钮被点击");
                                          } else {
                                              //指纹识别失败执行
                                              NSLog(@"指纹识别失败");
                                          }
                                          dispatch_async(dispatch_get_main_queue(), ^{
                                              
                                          });
                                      }
                                  }];

}else {
        // 如果设置不支持指纹解锁,跳过验证环节直接跳转
        MyCollectionViewController *myCollecVC = [[MyCollectionViewController alloc] init];
        myCollecVC.hidesBottomBarWhenPushed = YES;
        myCollecVC.userID = _user.ID;
        [self.navigationController pushViewController:myCollecVC animated:YES];
}
  • 效果图

清除缓存

  • 计算缓存
- (CGFloat)getCacheSize {
    
    CGFloat sdSize = [[SDImageCache sharedImageCache] getSize];
    NSString *myPath = [NSHomeDirectory()stringByAppendingPathComponent:@"Library/Caches/MyCaches"];
    // 获取文件夹中的所有文件
    NSArray *arr = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:myPath error:nil];
    unsigned long long size = 0;
    for (NSString *fileName in arr) {
        NSString *filePath = [myPath stringByAppendingPathComponent:fileName];
        NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
        size += dict.fileSize;
    }
    // 1M = 1024K = 1024 * 1024 字节
    CGFloat totalSize = (sdSize + size) / 1024.0 / 1024.0;
    return totalSize;
}
  • 在tableView:cellForRowAtIndexPath:方法中
// 自定义一个cell,用于显示当前缓存大小
    ClearCacheCell *cell = [tableView dequeueReusableCellWithIdentifier:clearCacheIdentifier];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.cacheLabel.text = [NSString stringWithFormat:@"%.2fM", [self getCacheSize]];
    return cell;
  • 在tableView的点击方法中
    if (indexPath.row == 5) {
        NSString *sizeStr = [NSString stringWithFormat:@"%.2fM",[self getCacheSize]];
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"清除缓存" message:sizeStr preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"清除" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
            
            //1.删除SDWebimage缓存        
            [[SDImageCache sharedImageCache]clearMemory];//清除内存缓存
            [[SDImageCache sharedImageCache]clearDisk];//清除磁盘
            
            //2.界面下载的缓存
            NSString *myPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches/MyCaches"];
            
            // 删除文件
            [[NSFileManager defaultManager]removeItemAtPath:myPath error:nil];
            
            ClearCacheCell *cell = (ClearCacheCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:0]];
            cell.cacheLabel.text = [NSString stringWithFormat:@"0M"];
            
            // 刷新指定行cell
            NSIndexPath *indexPath=[NSIndexPath indexPathForRow:5 inSection:0];
            [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
            
            
        }];
        UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
        [alert addAction:action1];
        [alert addAction:action2];
        [self presentViewController:alert animated:YES completion:nil];
        
}
  • 效果图 缓存计算前
  • 点击cell后弹框效果图
  • 点击清除后 刷新cell的显示

夜间模式

  • 首先导入头文件

#import <DKNightVersion/DKNightVersion.h>
// 设置导航栏颜色
self.navigationController.navigationBar.dk_backgroundColorPicker = DKColorPickerWithRGB(0xFF0000, 0, 1);

// 设置页面的颜色
self.view.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa);

// 页面所有控件都要设置dk背景色
tableView.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa);

// 通常情况下要为所有cell设置背景色和cell里的字体颜色
cell.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa);
cell.titleLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT);
  • 模式的切换
//
    if ([self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]) {
        [self.dk_manager dawnComing];
    } else {
        [self.dk_manager nightFalling];
    }
  • 效果图

数据刷新

  • 首先导入头文件

#import "MJRefresh.h"
  • 设置tableView
_tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(pullDownToRefresh)];
[_tableView.mj_header beginRefreshing];
_tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(pullUpToLoad)];
  • 下拉刷新对应的方法
- (void)pullDownToRefresh {
    
// 做相应的网络请求

// 数据请求回来后停止刷新
[_tableView.mj_header endRefreshing];
    
}

  • 上滑加载对应的方法
- (void)pullUpToLoad {
    
// 做相应的网络请求

// 数据请求回来后停止刷新
[_tableView.mj_footer endRefreshing];

    
}

数据加载

  • 首先导入头文件

#import "AFNetworking.h"
  • 在pullDownToRefresh方法中
- (void)pullDownToRefresh {
    
// 创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
[mgr GET:@"请求的地址" parameters:用字典的形式接受请求参数 progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 请求成功的操作
        // 将数据存入model
        // 数据请求回来后停止刷新
        [_tableView.mj_header endRefreshing];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        // 请求失败的操作
}];
        
}

模型的处理

  • 首先导入头文件

#import "MJExtension.h"
  • 在pullDownToRefresh方法中
- (void)pullDownToRefresh {
    
// 创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
[mgr GET:@"请求的地址" parameters:用字典的形式接受请求参数 progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 请求成功的操作
        // 将数据存入model
        NSArray *dicArray = [[responseObject objectForKey:@"data"] objectForKey:@"object_list"];
            for (NSDictionary *dic in dicArray) {
                ArticleModel *articleModel = [ArticleModel mj_objectWithKeyValues:dic];
                [self.dataArray addObject:articleModel];
                
            }
        // 数据请求回来后停止刷新
        [_tableView.mj_header endRefreshing];
        // 回到主线程刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            [_tableView reloadData];
        });
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        // 请求失败的操作
}];
        
}

网络图片的处理

  • 首先导入头文件

#import <SDWebImage/UIImageView+WebCache.h>
[imageView sd_setImageWithURL:[NSURL 
             placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

pch的使用

  • pch文件是一个标准的预编译头文件( Pre-Compiled Header). 这个文件会被编译存储在一个缓冲空间里并且自动有且只有一次包含到每个文件里。它能够提高编译速度,让你不用import任何代码到你的代码文件里就能添加了该引用。
  • 给你的PCH文件起名字projectName-Prefix.pch
  • 例如你的项目工程名为iOSSample然而你的PCH 文件的名字应该为 iOSSample-Prefix.pch然后点击创建按钮.
#ifndef PrefixHeader_pch
#define PrefixHeader_pch

#define SCREEN_RECT         [UIScreen mainScreen].bounds
#define SCREEN_SIZE         [UIScreen mainScreen].bounds.size
#define WIDTH        SCREEN_SIZE.width
#define HEIGHT       SCREEN_SIZE.height

#import "Masonry.h"
#import "AFNetworking.h"
#import "MJRefresh.h"
#import "MJExtension.h"
#import <SDWebImage/UIImageView+WebCache.h>
#import "UIButton+Block.h"
#import <DKNightVersion/DKNightVersion.h>
#import "MBProgressHUD.h"

#endif /* PrefixHeader_pch */
  • 设置路径
  • 输入
YourProjectName/YourProject-Prefix.pch 或

$(SRCROOT)/YourProject-Prefix.pch
  • 如果将pch文件拖到Suppoting Files文件夹中
$(SRCROOT)/Supporting Files/YourProject-Prefix.pch

小技巧

  • 创建一个RootViewController,让之后创建的所有ViewController继承于它
- (void)viewDidLoad {
    
    [super viewDidLoad];
    // 关闭自动矫正
    self.automaticallyAdjustsScrollViewInsets = NO;
    self.edgesForExtendedLayout = UIRectEdgeNone;

}
  • 创建一个BaseModel,让之后创建的所有model继承于它
BaseModel.h

#import <Foundation/Foundation.h>

@interface BaseModel : NSObject
- (instancetype)initWithDic:(NSDictionary *)dic;
+ (instancetype)modelWithDic:(NSDictionary *)dic;

@end
BaseModel.m

#import "BaseModel.h"

@implementation BaseModel

- (instancetype)initWithDic:(NSDictionary *)dic {
    self = [super init];
    if (self) {
        [self setValuesForKeysWithDictionary:dic];
    }
    return self;
}

+ (instancetype)modelWithDic:(NSDictionary *)dic {
    return [[self alloc] initWithDic:dic];
}

// 防止程序crash
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    
}

// 防止程序crash
- (id)valueForUndefinedKey:(NSString *)key {
    return nil;
}

@end

添加类目

  • NSString+Regex.h
#import <Foundation/Foundation.h>

@interface NSString(Regex)

/**
 *  匹配邮箱
 */
- (BOOL)isEmail:(NSString *)str;

/**
 *  匹配手机号码
 */
- (BOOL)isPhoneNumber:(NSString *)str;

/**
 *  匹配中国移动手机号码
 */
- (BOOL)isCMPhoneNumber:(NSString *)str;

/**
 *  匹配中国联通手机号码
 */
- (BOOL)isCUPhoneNumber:(NSString *)str;

/**
 *  匹配中国电信手机号码
 */
- (BOOL)isCTPhoneNumber:(NSString *)str;

/**
 *  匹配国内电话号码
 */
- (BOOL)isCallNumber:(NSString *)str;

/**
 *  匹配QQ号码
 */
- (BOOL)isQQ:(NSString *)str;

/**
 *  匹配网址URL的正则表达式
 */
- (BOOL)isURL:(NSString *)str;

/**
 *  匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
 */
- (BOOL)isLegalCount:(NSString *)str;

/**
 *  匹配中国邮政编码
 */
- (BOOL)isPostcode:(NSString *)str;

/**
 *  匹配身份证
 */
- (BOOL)isIDCardNumber:(NSString *)str;

/**
 *  匹配ip地址
 */
-(BOOL)isIpAddress:(NSString *)str;

@end

  • NSString+Regex.m
#import "NSString+Regex.h"

@implementation NSString(Regex)

/**
 *  匹配邮箱
 */
- (BOOL)isEmail:(NSString *)str {
    NSString * regex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\\\\\\\.[A-Za-z]{2,4}";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配手机号码
 */
- (BOOL)isPhoneNumber:(NSString *)str {
    /**
     * 手机号码
     * 移动:134[0-8],135,136,137,138,139,150,151,157,158,159,182,187,188
     * 联通:130,131,132,152,155,156,185,186
     * 电信:133,1349,153,180,189
     */
    NSString * regex = @"^1(3[0-9]|5[0-35-9]|8[025-9])\\\\\\\\d{8}$";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配中国移动手机号码
 */
- (BOOL)isCMPhoneNumber:(NSString *)str {
    NSString * regex = @"^1(34[0-8]|(3[5-9]|5[017-9]|8[278])\\\\\\\\d)\\\\\\\\d{7}$";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配中国联通手机号码
 */
- (BOOL)isCUPhoneNumber:(NSString *)str {
    NSString * regex = @"^1(3[0-2]|5[256]|8[56])\\\\\\\\d{8}$";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配中国电信手机号码
 */
- (BOOL)isCTPhoneNumber:(NSString *)str {
    NSString * regex = @"^1((33|53|8[09])[0-9]|349)\\\\\\\\d{7}$";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配国内电话号码
 */
- (BOOL)isCallNumber:(NSString *)str {
    NSString * regex = @"d{3}-d{8}|d{4}-d{7}";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配QQ号码
 */
- (BOOL)isQQ:(NSString *)str {
    NSString * regex = @"[1-9][0-9]{4,}";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配网址URL
 */
- (BOOL)isURL:(NSString *)str {
    NSString * regex = @"[a-zA-z]+://[^s]*";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
 */
- (BOOL)isLegalCount:(NSString *)str {
    NSString * regex = @"^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配中国邮政编码
 */
- (BOOL)isPostcode:(NSString *)str {
    NSString * regex = @"[1-9]d{5}(?!d)";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配身份证
 */
- (BOOL)isIDCardNumber:(NSString *)str {
    NSString * regex = @"d{15}|d{18}";
    return [self checkStringWithRegex:regex checkString:str];
}

/**
 *  匹配ip地址
 */
-(BOOL)isIpAddress:(NSString *)str {
    NSString * regex = @"d+.d+.d+.d+";
    return [self checkStringWithRegex:regex checkString:str];
}

#pragma mark - private

- (BOOL)checkStringWithRegex:(NSString *)regex checkString:(NSString *)checkString {
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    return [pred evaluateWithObject:checkString];
}

@end


  • UIView+ShowMessage.h
  • 实现简单的HUD效果
#import <UIKit/UIKit.h>

@interface UIView (ShowMessage)

+ (void)showMessage:(NSString *)message;

@end
  • UIView+ShowMessage.m
#import "UIView+ShowMessage.h"
#import "UILabel+SizeToHeight.h"

#define SCREEN_W [UIScreen mainScreen].bounds.size.width
#define SCREEN_H [UIScreen mainScreen].bounds.size.height

@implementation UIView (ShowMessage)

+ (void)showMessage:(NSString *)message {
    
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    UIView *view = [[UIView alloc] init];
    view.backgroundColor = [UIColor colorWithWhite:0.323 alpha:0.050];
    view.frame = CGRectMake(1, 1, 1, 1);
    view.alpha = 1.0;
    [window addSubview:view];
    
    UILabel *label = [[UILabel alloc] init];
    label.font = [UIFont systemFontOfSize:15];
    label.textColor = [UIColor whiteColor];
    label.textAlignment = NSTextAlignmentCenter;
    label.text = message;
    CGFloat width = [UILabel getWidthWithTitle:message Font:label.font];
    label.frame = CGRectMake(10, 5, width, 18);
    [view addSubview:label];
    
    view.frame = CGRectMake((SCREEN_W - width - 20) / 2, 64, width + 20, 30);
    [UIView animateWithDuration:2.0f animations:^{
        view.frame = CGRectMake((SCREEN_W - width - 20) / 2, 94, width + 20, 30);
        view.alpha = 0;
    } completion:^(BOOL finished) {
        [view removeFromSuperview];
    }];
}

@end


验证码倒计时

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行z
    dispatch_source_set_event_handler(_timer, ^{
        if(time <= 0){ //倒计时结束,关闭
            dispatch_source_cancel(_timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                //设置按钮的样式
                [self.verificationButton setTitle:@"重新发送" forState:UIControlStateNormal];
                [self.verificationButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
                self.verificationButton.userInteractionEnabled = YES;
            });
            
        }else{
            int seconds = time % 60;
            dispatch_async(dispatch_get_main_queue(), ^{
                
                //设置按钮显示读秒效果
                [self.verificationButton setTitle:[NSString stringWithFormat:@"%.2d秒后重新发送", seconds] forState:UIControlStateNormal];
                [self.verificationButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
                self.verificationButton.userInteractionEnabled = NO;
            });
            time--;
        }
    });
    dispatch_resume(_timer);

获取时间戳

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval interval=[date timeIntervalSince1970];
NSString *timeString = [NSString stringWithFormat:@"%.0lf", interval];


使用xib自定义view

  • 首先创建一个自定义view
  • 然后创建一个和自定义view同名的xib
  • 在自定义view的 .m方法中重写init方法
- (instancetype)init
{
    self = [super init];
    if (self) {
        self = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil].lastObject;
    }
    return self;
}
  • 将xib和view绑定
  • 使用自定义view
ArticleCommentView *commentView = [[ArticleCommentView alloc] init];

// 设置frame
commentView.frame = CGRectMake(0, 0, WIDTH, HEIGHT);
// 添加到视图上
[self.view addSubview:commentView];


为webView注入js代码

  • 实现更改网页字体颜色,背景色,获取网络图片链接

  • webView首先签协议

UIWebViewDelegate
  • 实现协议方法
- (void)webViewDidFinishLoad:(UIWebView *)webView {

    //js方法遍历图片添加点击事件 返回图片个数
    static  NSString * const jsGetImages =
    @"function getImages(){\\\\
    var objs = document.getElementsByTagName(\\\\"img\\\\");\\\\
    for(var i=0;i<objs.length;i++){\\\\
    objs[i].onclick=function(){\\\\
    document.location=\\\\"myweb:imageClick:\\\\"+this.src;\\\\
    };\\\\
    };\\\\
    return objs.length;\\\\
    };";
    [webView stringByEvaluatingJavaScriptFromString:jsGetImages];//注入js方法
    // 注入自定义的js方法后别忘了调用 否则不会生效
    [webView stringByEvaluatingJavaScriptFromString:@"getImages()"];
    
    // 夜间模式的判断 
    if ([self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]) {
        //字体颜色
        [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= 'gray'"];
        //页面背景色
        [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('body')[0].style.background='#2E2E2E'"];
    }

}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *requestString = [[request URL] absoluteString];
    if ([requestString hasPrefix:@"myweb:imageClick:"]) {
        // 这一步可以拿到图片的url
        NSString *imageUrl = [requestString substringFromIndex:@"myweb:imageClick:".length];
        // 拿到图片链接后的操作
        // ...
        return NO;
    }
    return YES;
}
  • 使用webView直接加载htmlString页面字体太小,图片太大的解决方法
// 将下面这个字符串拼接到htmlString的最前面
NSString *str = [NSString stringWithFormat:@"<head><style>img{max-width:%fpx !important;}</style></head>", self.view.frame.size.width - 20];
_htmlString = [NSMutableString stringWithFormat:@"%@%@",str,_htmlString];
       
[_webView loadHTMLString:_htmlString baseURL:nil];

最后

项目期间,虽然时间紧,任务重,但也不要久坐,适当的休息,放松,才能事半功倍。

Top