Limit

YYModel

// 将属性和json关联,将属性和json中对应的key映射
+(NSDictionary *)modelCustomPropertyMapper
{
    return @{@"desc" : @"description"};
}

// 将属性和类关联,特别数组、字典
+(NSDictionary *)modelContainerPropertyGenericClass
{
    return @{@"photos" : [PhotosModel class]};
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"description"]) {
        self.desc = value;
    }
}

- (id)valueForUndefinedKey:(NSString *)key
{
    if ([key isEqualToString:@"description"]) {
        return self.desc;
    }
    return nil;
}

DataBaseManager

#import "DataBaseManager.h"
#import "FMDB.h"

@implementation DataBaseManager
{
    // 数据库管理对象
    FMDatabase * _fmdb;
}

// 单例方法
+ (instancetype)sharedManager
{
    static dispatch_once_t onceToken;
    static DataBaseManager * dbManager = nil;
    dispatch_once(&onceToken, ^{
        if (!dbManager) {
            dbManager = [[DataBaseManager alloc] initPrivate];
        }
    });
    return dbManager;
    
//    @synchronized(self) {
//        if (!dbManager) {
//            dbManager = [[DataBaseManager alloc] initPrivate];
//        }
//    }
}

// 重写初始化方法
- (instancetype)init
{
    // 1. 抛出异常方式
    @throw [NSException exceptionWithName:@"DataBaseManager init" reason:@"不能调用init方法" userInfo:nil];
    // 2. 断言,判定言论,程序崩溃
//    NSAssert(NO, @"DataBaseManager无法调用该方法");
    return self;
}

// 重新实现初始化方法
- (instancetype)initPrivate
{
    if (self = [super init]) {
        [self createDataBase];
    }
    return self;
}

// 初始化数据库
- (void)createDataBase
{
    // 获取沙盒路径下的documents路径
    NSArray * documentsPath =  NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString * dbPath = [[documentsPath firstObject] stringByAppendingPathComponent:@"LimitFree.db"];
    NSLog(@"DBPath = %@", dbPath);
    if (!_fmdb) {
        // 创建数据库管理对象
        _fmdb = [[FMDatabase alloc] initWithPath:dbPath];
    }
    // 打开数据库
    if ([_fmdb open]) {
        // 创建数据库表
        [_fmdb executeUpdate:@"CREATE TABLE if not exists Collection (appId, appName, appImage);"];
    }
}

#pragma mark - 增删查
// 判断APPID对应的数据是否存在
- (BOOL)isExistsAppId:(NSString *) appId
{
    FMResultSet * rs =  [_fmdb executeQuery:@"SELECT * FROM Collection WHERE appId=?", appId];
    // 判断结果集是否存在
    return [rs next];
}

// 添加数据
- (BOOL)insertAppId:(NSString *) appId appName:(NSString *) appName appImage:(NSString *) appImage
{
    // 判断appId在数据库中是否存在
    if (![self isExistsAppId:appId]) {
        // 如果不存在
        BOOL success = [_fmdb executeUpdate:@"INSERT INTO Collection VALUES (?,?,?)", appId, appName, appImage];
        return success;
    }
    return YES;
}

// 获取数据库中所有的记录
- (NSArray *)selectAllApps
{
    // 从表Collection中获取所有的数据
    FMResultSet * rs = [_fmdb executeQuery:@"SELECT * FROM Collection"];
    // 遍历结果集
    NSMutableArray * apps = [NSMutableArray array];
    while ([rs next]) {
        AppDetailModel * detailModel = [[AppDetailModel alloc] init];
        detailModel.applicationId = [rs stringForColumn:@"appId"];
        detailModel.name = [rs stringForColumn:@"appName"];
        detailModel.iconUrl = [rs stringForColumn:@"appImage"];
        [apps addObject:detailModel];
    }
    return [apps copy];
}

// 删除数据库中指定appId数据
- (BOOL)deleteAppId:(NSString *) appId
{
    BOOL sucess = [_fmdb executeUpdate:@"DELETE FROM Collection WHERE appId=?", appId];
    return sucess;
}

@end

BaseViewController

// 定制导航项的标题
- (void)customNaviItemTitle:(NSString *)title
{
    // 定制UINavigationItem的titleView
    UILabel * titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 44)];
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.font = [UIFont systemFontOfSize:20];
    titleLabel.textColor = [UIColor purpleColor];
    // 设置文字
    titleLabel.text = title;
    
    // 设置导航项的标题视图
    self.navigationItem.titleView = titleLabel;
}

// 定制导航项的按钮
- (void)customTabBarButtonTitle:(NSString *)title image:(NSString *)imageName target:(id)target action:(SEL)selector isLeft:(BOOL)isLeft
{
    UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setBackgroundImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    [button setTitle:title forState:UIControlStateNormal];
    [button addTarget:target action:selector forControlEvents:UIControlEventTouchDown];
    button.frame = CGRectMake(0, 0, 44, 30);
    button.titleLabel.font = [UIFont systemFontOfSize:16];
    
    // 判断是否为左侧按钮
    UIBarButtonItem * buttonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
    if (isLeft) {
        self.navigationItem.leftBarButtonItem = buttonItem;
    }
    else {
        self.navigationItem.rightBarButtonItem = buttonItem;
    }
}```
##AppListViewController

// 左侧按钮点击事件响应

  • (void)onLeftClicked:(UIButton *) sender
    {
    CategoryViewController * categoryVC = [[CategoryViewController alloc] init];
    categoryVC.hidesBottomBarWhenPushed = YES;
    // 设置分类对应的类
    categoryVC.objClass = [self class];

    // 设置block
    __weak typeof(self) weakSelf = self;
    categoryVC.block = ^(NSString * cateID) {
    weakSelf.categoryID = cateID;
    [weakSelf.appListTableView.mj_header beginRefreshing];
    };

    [self.navigationController pushViewController:categoryVC animated:YES];
    }
    // 创建视图

  • (void)createTableView
    {
    self.appListTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenSize.width, kScreenSize.height - 64)];
    // 添加到视图控制器上
    [self.view addSubview:self.appListTableView];
    // 设置委托和数据源
    self.appListTableView.dataSource = self;
    self.appListTableView.delegate = self;
    // 设置行高
    self.appListTableView.rowHeight = 130;

    // 设置下拉刷新和上拉加载更多
    __weak typeof(self) weakSelf = self;
    self.appListTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
    weakSelf.currentPage = 1;
    [weakSelf requestDataWithPage:1];
    }];

    self.appListTableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
    weakSelf.currentPage++;
    [weakSelf requestDataWithPage:weakSelf.currentPage];
    }];

    // 注册单元格,通过xib方式
    [self.appListTableView registerNib:[UINib nibWithNibName:@"AppListCell" bundle:nil] forCellReuseIdentifier:@"AppListCell"];

    // 添加搜索框
    self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 200, 40)];
    self.appListTableView.tableHeaderView = self.searchBar;
    self.searchBar.placeholder = @"百万应用等你来搜索";
    self.searchBar.showsCancelButton = YES;
    // 设置委托
    self.searchBar.delegate = self;
    }
    // 请求数据

  • (void)requestDataWithPage:(NSInteger) page
    {
    // 使用弱引用指针
    __weak typeof(self) weakSelf = self;
    // 拼接URL
    NSString * url = [NSString stringWithFormat:self.requestURL, page, self.searchStr ? : @""];
    if (self.categoryID) {
    // 分类ID不为零时,才追加参数
    if (![self.categoryID isEqualToString:@"0"]) {
    url = [url stringByAppendingString:[NSString stringWithFormat:@"&cate_id=%@", self.categoryID]];
    }
    }
    // 将URL转换为百分号编码
    url = [url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    // 获取NSURLSession单例
    NSURLSession * urlSession = [NSURLSession sharedSession];
    // 创建数据请求任务
    NSURLSessionDataTask * task = [urlSession dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
    // 判断是否请求到数据
    if (!error) {
    // 解析JSON数据
    NSError * err;
    NSDictionary * responseDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&err];
    // 判断是否解析成功
    if (err == nil) {

              // 判断是否是首页数据
              if (page == 1) {
                  // 清空之前的数据
                  [weakSelf.appListArray removeAllObjects];
              }
              
              NSArray * appArray = responseDict[@"applications"];
              // 遍历数组将字典装换为模型数据
              for (NSDictionary * appDict in appArray) {
                  AppListAppModel * appModel = [[AppListAppModel alloc] init];
                  [appModel setValuesForKeysWithDictionary:appDict];
                  [weakSelf.appListArray addObject:appModel];
              }
              // 回到主线程中刷新视图
              dispatch_async(dispatch_get_main_queue(), ^{
                  [weakSelf.appListTableView reloadData];
              });
          }
      }
      else {
          // 如果请求数据失败,需要将数据回归
          if (weakSelf.currentPage > 1) {
              weakSelf.currentPage --;
          }
      }
      
      dispatch_async(dispatch_get_main_queue(), ^{
          // 停止刷新
          [weakSelf.appListTableView.mj_header endRefreshing];
          [weakSelf.appListTableView.mj_footer endRefreshing];
      });
    

    }];
    // 启动任务
    [task resume];
    }

##CustomTabBarController

// 创建视图控制器

  • (void)createViewControllers
    {
    // 读取Controllers.plist文件内容
    NSString * plistPath = [[NSBundle mainBundle] pathForResource:@"Controllers" ofType:@"plist"];
    NSArray * plistArray = [NSArray arrayWithContentsOfFile:plistPath];

    // 遍历数组,创建视图控制器
    NSMutableArray * viewControllers = [NSMutableArray array];
    for (NSDictionary * dict in plistArray) {
    NSString * title = dict[@"title"];
    NSString * iconName = dict[@"iconName"];
    NSString * className = dict[@"className"];
    // 创建视图控制器
    // 1. 类名对应的类
    Class class = NSClassFromString(className);
    // 2. 创建对象
    AppListViewController * appListVC = [[class alloc] init];
    appListVC.title = title;

      // 3. 创建导航控制器
      UINavigationController * navi = [[UINavigationController alloc] initWithRootViewController:appListVC];
      // 4. 设置UITabBarItem
      UIImage * normalImage = [[UIImage imageNamed:iconName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
      UIImage * selectedImage = [[UIImage imageNamed:[NSString stringWithFormat:@"%@_press", iconName]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
      navi.tabBarItem = [[UITabBarItem alloc] initWithTitle:title image:normalImage selectedImage:selectedImage];
      // 5. 将导航控制器添加到数组中
      [viewControllers addObject:navi];
    

    }
    // 6. 设置UITabBarController
    self.viewControllers = viewControllers;
    }

// 自定义UITabBar

  • (void)customTabBar
    {
    // 获取UITabBar
    UITabBar * tabBar = self.tabBar;
    // 设置背景图片
    [tabBar setBackgroundImage:[UIImage imageNamed:@"tabbar_bg"]];
    }

// 自定义UINavigationBar

  • (void)customNaviBar
    {
    // 获取所有的导航控制器
    NSArray * naviControllers = self.viewControllers;
    for (UINavigationController * navi in naviControllers) {
    // 获取UINavigationBar
    UINavigationBar * naviBar = navi.navigationBar;
    // 设置背景图
    [naviBar setBackgroundImage:[UIImage imageNamed:@"navigationbar"] forBarMetrics:UIBarMetricsDefault];
    }
    }
##AppListCell

// 重写appModel的setter方法

  • (void)setAppModel:(AppListAppModel *)appModel
    {
    // 修改实例变量
    _appModel = appModel;

    // 修改视图
    [self.appImageView sd_setImageWithURL:[NSURL URLWithString:_appModel.iconUrl] placeholderImage:[UIImage imageNamed:@"appproduct_appdefault"]];
    self.appNameLabel.text = _appModel.name;
    self.updateTimeLabel.text = _appModel.updateDate;
    // 实现多样式文本需要使用NSAttributedString
    // 参数1:多样式的文本
    // 参数2:文本样式定义
    // 删除符高度,删除符颜色
    NSDictionary * attrDict = @{NSStrikethroughStyleAttributeName : @4, NSStrikethroughColorAttributeName : [UIColor orangeColor]};
    NSAttributedString * lastPriceAttr = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"¥%@", _appModel.lastPrice] attributes:attrDict];
    self.lastPriceLabel.attributedText = lastPriceAttr;
    self.categoryNameLabel.text = _appModel.categoryName;
    NSString * countStr = [NSString stringWithFormat:@"分享:%@次 收藏:%@次 下载:%@次", _appModel.shares, _appModel.favorites, _appModel.downloads];
    self.countLabel.text = countStr;

    // 设置星标值
    self.starCurrentView.value = [_appModel.starCurrent floatValue];
    }
    @implementation StarView
    {
    UIImageView * _foregroudImageView; // 前景图
    UIImageView * _backgroudImageView; // 背景图
    }

// 重写初始化方法

  • (instancetype)initWithFrame:(CGRect)frame
    {
    if (self = [super initWithFrame:frame]) {
    [self createImageViews];
    }
    return self;
    }

// 当使用xib或者Storyboard时,其中视图设置为自定义的类,需要重写该方法。
// 在xib或者Storyboard加载时或者解冻时,会调用此方法创建视图

  • (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
    if (self = [super initWithCoder:aDecoder]) {
    [self createImageViews];
    }
    return self;
    }

// 创建图片视图

  • (void)createImageViews
    {
    // 创建背景图
    _backgroudImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"StarsBackground"]];
    [self addSubview:_backgroudImageView];
    // 设置内容模式
    _backgroudImageView.contentMode = UIViewContentModeLeft;

    // 创建前景图
    _foregroudImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"StarsForeground"]];
    [self addSubview:_foregroudImageView];
    // 设置内容模式
    _foregroudImageView.contentMode = UIViewContentModeLeft;
    // 裁剪
    _foregroudImageView.clipsToBounds = YES;
    }

// 设置星标视图的值

  • (void)setValue:(CGFloat)value
    {
    _value = value;
    if (_value <= 0 || _value > 5) {
    return;
    }
    // 修改前景图的frame
    CGRect rect = _backgroudImageView.frame;
    rect.size.width *= (value / 5.f);
    _foregroudImageView.frame = rect;
    }
    @end
##SettingViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self customNaviItem];
    // 注册单元格
    [self.settingCollectionView registerNib:[UINib nibWithNibName:@"SettingCell" bundle:nil] forCellWithReuseIdentifier:@"SettingCell"];
    // 设置单元格的大小
    UICollectionViewFlowLayout * flowLayout = (UICollectionViewFlowLayout *)self.settingCollectionView.collectionViewLayout;
    flowLayout.itemSize = CGSizeMake(57, 57 + 30);
    // 设置间距
    // 设置行间距
    flowLayout.minimumLineSpacing = 20;
    // 设置单元格间距
    flowLayout.minimumInteritemSpacing = 40;
    // 设置内间距
    flowLayout.sectionInset = UIEdgeInsetsMake(30, 30, 30, 30);

    // 设置数据源和委托
    self.settingCollectionView.dataSource = self;
    self.settingCollectionView.delegate = self;
    self.settingCollectionView.backgroundColor = [UIColor whiteColor];

    // 赋值
    self.namesArray = @[@"我的设置", @"我的关注", @"我的账号", @"我的收藏", @"我的下载", @"我的评论", @"我的帮助", @"蚕豆应用"];
    self.picsArray = @[@"setting", @"favorite", @"user", @"collect", @"download", @"comment", @"help", @"candou"];

    // 刷新UICollectionView
    [self.settingCollectionView reloadData];
    }

pragma mark - UICollectionViewDelegate

  • (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    {
    switch (indexPath.row) {
    case 0:
    {
    // 获取UIStroyboard对象
    // 参数1:storyboard的名称,不带后缀名
    UIStoryboard * sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    // 实例化视图控制器
    MySettingViewController * mySettingVC = [sb instantiateViewControllerWithIdentifier:@"MySettingViewController"];
    [self.navigationController pushViewController:mySettingVC animated:YES];
    }
    break;
    case 3:
    {
    CollectionViewController * collectionVC = [[CollectionViewController alloc] init];
    [self.navigationController pushViewController:collectionVC animated:YES];
    }
    break;

      default:
          break;
    

    }
    }```

CollectionViewController

// 单元格
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 复用
    CollectionCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath];
    // 取出对应的模型
    AppDetailModel * model = self.collectionArray[indexPath.row];
    cell.appNameLabel.text = model.name;
    [cell.appImageView sd_setImageWithURL:[NSURL URLWithString:model.iconUrl] placeholderImage:[UIImage imageNamed:@"appproduct_appdefault"]];
    
    // 判断是否处于编辑状态
    if (self.isEditing) {
        cell.deleteButton.hidden = NO;
        // 动画效果
        // 参数1:动画持续时间
        // 参数2:动画延迟时间
        cell.transform = CGAffineTransformMakeRotation(-0.05);
        [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction  animations:^{
            cell.transform = CGAffineTransformMakeRotation(0.05);
        } completion:nil];
    }
    else {
        cell.deleteButton.hidden = YES;
    }
    
    [cell.deleteButton addTarget:self action:@selector(onDeleteAction:) forControlEvents:UIControlEventTouchUpInside];
    
    return cell;
}

// 删除按钮点击方法
- (void)onDeleteAction:(UIButton *) sender
{
    // 找到单元格
    CollectionCell * cell = (CollectionCell *)sender.superview.superview;
    // 获取单元格对应的indexPath
    NSIndexPath * indexPath = [self.collectionView indexPathForCell:cell];
    // 先删除单元格还是数据?
    // 先删除数据
    // 先删除数据库数据
    AppDetailModel * detailModel = self.collectionArray[indexPath.row];
    [[DataBaseManager sharedManager] deleteAppId:detailModel.applicationId];
    // 再删除数据源数据
    [self.collectionArray removeObject:detailModel];
    // 最后才删除单元格
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
    
    // 延迟重新刷新UICollectionView
    // 参数1:表示延迟时间
    // 参数2:延迟执行操作的队列
    // 参数3:执行的操作
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.35 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.collectionView reloadData];
    });
}

AppDetailViewController

// 重写Getter方法
- (CLLocationManager *)locationManager
{
    // 1.
    if (!_locationManager) {
        _locationManager = [[CLLocationManager alloc] init];
        // 设置精度
        _locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        // 设置更新的距离
        _locationManager.distanceFilter = 100.0;
        // 设置委托
        _locationManager.delegate = self;
    }
    return _locationManager;
}
// 请求定位
- (void)reqeustLocation
{
    // 2. 请求用户权限开启定位
    // 判断当前APP是否开启定位服务
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    // 判断是否已授权
    if (status == kCLAuthorizationStatusNotDetermined) {
        // 请求When-In-Use授权
        [self.locationManager requestWhenInUseAuthorization];
    }
    else if (status == kCLAuthorizationStatusDenied)
    {
        UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"定位服务已关闭,请在设置中找到爱限免APP,开启定位服务" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
        [alertView show];
    }
    else {
        // 启动定位服务
        [self.locationManager startUpdatingLocation];
    }
    // 3. 在plist文件中添加对应定位描述信息
    
}
// 分享按钮点击
- (IBAction)onShareAction:(UIButton *)sender {
    //注意:分享到微信好友、微信朋友圈、微信收藏、QQ空间、QQ好友、来往好友、来往朋友圈、易信好友、易信朋友圈、Facebook、Twitter、Instagram等平台需要参考各自的集成方法
    
    // 分享文字
    NSString * shortDesc = self.detailModel.description.length > 100 ? [self.detailModel.desc substringToIndex:100] : self.detailModel.desc;
    NSString * shareText = [NSString stringWithFormat:@"【%@】%@ %@", self.detailModel.name, shortDesc, self.detailModel.itunesUrl];
    UIImage * shareImage = self.appImageView.image;
    
    [UMSocialSnsService presentSnsIconSheetView:self
                                         appKey:nil
                                      shareText:shareText
                                     shareImage:shareImage
                                shareToSnsNames:nil
                                       delegate:nil];
}
- (IBAction)onFavouriteAction:(UIButton *)sender {
    if (self.detailModel) {
        // 添加数据到数据库中
        BOOL success = [[DataBaseManager sharedManager] insertAppId:self.detailModel.applicationId appName:self.detailModel.name appImage:self.detailModel.iconUrl];
        if (success) {
            self.favouriteButton.enabled = NO;
            [KVNProgress showSuccessWithStatus:@"收藏成功"];
        }
        else {
            [KVNProgress showErrorWithStatus:@"收藏失败"];
        }
    }    
}
- (IBAction)onDownloadAction:(UIButton *)sender {
    // 打开一个URL地址
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.detailModel.itunesUrl]];
//    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    // 发送短信
//    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms://10086:3123"]];
    // 打电话、发邮件、打开某个应用程序
}
// 刷新界面显示数据
- (void)loadDetailData
{
    // 判断当前AppId是否在数据库中存在
    if ([[DataBaseManager sharedManager] isExistsAppId:self.appId]) {
        // 禁用按钮
        self.favouriteButton.enabled = NO;
    }
    
    [self.appImageView sd_setImageWithURL:[NSURL URLWithString:self.detailModel.iconUrl] placeholderImage:[UIImage imageNamed:@"appproduct_appdefault"]];
    self.appNameLabel.text = self.detailModel.name;
    self.appInfoLabel.text = [NSString stringWithFormat:@"原价:¥%@ 限免中 文件大小:%@MB", self.detailModel.lastPrice, self.detailModel.fileSize];
    self.appCateLabel.text = [NSString stringWithFormat:@"类型:%@ 评分:%@", self.detailModel.categoryName, self.detailModel.starCurrent];
    
    // 滚动图片部分
    CGFloat photoHeight = CGRectGetHeight(self.appImageScrollView.frame);
    CGFloat photoWidth = photoHeight * 9 / 5;
    for (int index = 0; index < self.detailModel.photos.count; index++) {
        // 获取模型数据
        PhotosModel * photoModel = self.detailModel.photos[index];
        UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(index*photoWidth, 0, photoWidth, photoHeight)];
        [self.appImageScrollView addSubview:imageView];
        // 设置图片
        [imageView sd_setImageWithURL:[NSURL URLWithString:photoModel.smallUrl] placeholderImage:[UIImage imageNamed:@"appproduct_appdefault"]];
        // 开启用户交互
        imageView.userInteractionEnabled = YES;
        // 设置标记值
        imageView.tag = SMALL_PCITURE_BEGIN_TAG + index;
        // 100 ~ n
        // 添加手势识别器
        UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSmallPicTap:)];
        [imageView addGestureRecognizer:tapGesture];
    }
    // 设置内容尺寸
    self.appImageScrollView.contentSize = CGSizeMake(self.detailModel.photos.count * photoWidth, photoHeight);
    
    self.appDescTextView.text = self.detailModel.desc;
}
// 点击手势方法
- (void)onSmallPicTap:(UITapGestureRecognizer *) sender
{
    NSInteger selectedIndex = sender.view.tag - SMALL_PCITURE_BEGIN_TAG;
    BigPictureViewController * bigPicVC = [[BigPictureViewController alloc] init];
    bigPicVC.selectedIndex = selectedIndex;
    bigPicVC.photosArray = self.detailModel.photos;
    // 设置转场动画样式
    bigPicVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    [self presentViewController:bigPicVC animated:YES completion:nil];
}
#pragma mark - 数据相关
// 请求详情数据
- (void)requestAppDetail
{
    [KVNProgress showWithStatus:@"应用详情加载中..." onView:self.view];
    __weak typeof(self) weakSelf = self;
    // 拼接URL
    NSString * url = [NSString stringWithFormat:kDetailUrl, self.appId];
    // 请求数据
    NSURLSession * session = [NSURLSession sharedSession];
    NSURLSessionDataTask * dataTask = [session dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
        // 判断是否请求成功
        if (!error) {
            weakSelf.detailModel = [AppDetailModel yy_modelWithJSON:data];
            // 回到主线程刷新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf loadDetailData];
                [KVNProgress showSuccessWithStatus:@"请求成功"];
            });
        }
        else {
            [KVNProgress showErrorWithStatus:error.localizedDescription];
        }
    }];
    // 启动任务
    [dataTask resume];
}
// 请求附近APP数据
- (void)requestNearApp:(CLLocationCoordinate2D) coordinate
{
    __weak typeof(self) weakSelf = self;
    // 拼接URL
    NSString * url = [NSString stringWithFormat:kNearAppUrl, coordinate.longitude, coordinate.latitude];
    // 请求数据
    NSURLSession * session = [NSURLSession sharedSession];
    // 创建任务
    NSURLSessionDataTask * dataTask = [session dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
        if (!error) {
            NSDictionary * responseDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            // 将json数组转换为模型数组
            weakSelf.nearAppArray = [NSArray yy_modelArrayWithClass:[NearAppModel class] json:responseDict[@"applications"]];
            // 回到主队列刷新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf loadNearApp];
            });
        }
    }];
    // 启动任务
    [dataTask resume];
}
#pragma mark - CLLocationManagerDelegate
// 授权状态改变回调
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    if (status == kCLAuthorizationStatusDenied) {
        NSLog(@"不允许");
    }
    else {
        // 启动定位服务
        [manager startUpdatingLocation];
    }
}

// 定位改变时回调
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray*)locations
{
    if (locations.count > 0) {
        // 停止定位服务
        [manager stopUpdatingLocation];
        // 从数组中取出任意一个位置信息
        CLLocation * location = locations.firstObject;
        // 根据经纬度请求数据
        [self requestNearApp:location.coordinate];
    }
}

BigPictureViewController

// 用可视化创建视图,在视图控制器创建[[ alloc] init]时,这些视图不会被创建,只有等到视图将要显示时也就是回调loadView方法时,才会被加载。
// 用AutoLayout布局时,只有在ViewDidAppear时才能获取到视图的正确的frame值
// push/pop、present/dismiss、addObsever/removeObsever、启动timer/关闭timer(视图控制器)
// 代码控制状态栏的显示/隐藏需要设置info.plist,添加【View controller-based status bar appearance】
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.picturesScrollView.delegate = self;
    // 添加手势识别器
    UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onScrollViewTap:)];
    [self.picturesScrollView addGestureRecognizer:tapGesture];
    
    // 通过KVO方式观察self.selectedIndex值的变化
    [self addObserver:self forKeyPath:@"selectedIndex" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)onScrollViewTap:(UITapGestureRecognizer *) sender
{
    // 判断当前视图是否被隐藏
    if ([self.picNumLabel isHidden]) {
        for (UIView * view in self.view.subviews) {
            if (![view isKindOfClass:[UIScrollView class]]) {
                // 除了UIScrollView都显示
                view.hidden = NO;
                [UIView animateWithDuration:0.3 animations:^{
                    view.alpha = 1.0;
                } completion:^(BOOL finished) {
                    
                }];
            }
        }
        // 显示状态栏
        [UIApplication sharedApplication].statusBarHidden = NO;
    }
    else {
        // 隐藏视图
        for (UIView * view in self.view.subviews) {
            if (![view isKindOfClass:[UIScrollView class]]) {
                // 除了UIScrollView都显示
                [UIView animateWithDuration:0.3 animations:^{
                    view.alpha = 0.0;
                } completion:^(BOOL finished) {
                    view.hidden = YES;
                }];
            }
        }
        // 隐藏状态栏
        [UIApplication sharedApplication].statusBarHidden = YES;
    }
    
}

- (void)dealloc
{
    // 移除观察者
    [self removeObserver:self forKeyPath:@"selectedIndex"];
}
- (IBAction)onSaveAction:(UIButton *)sender {
    
    __weak typeof(self) weakSelf = self;
    // 保存图片
    // 参数1:要保存的图片
    // target-action
    // 上下文信息,nil
    
    // 获取当前切换的图片地址
    PhotosModel * photosModel = self.photosArray[self.selectedIndex];
    // 通过GCD方式创建线程请求数据
    // 全局队列:并行队列
    // 主队列(UI队列):串行队列
    [KVNProgress showWithStatus:@"正在保存图片到相册"];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 请求数据
        // 同步请求数据
        NSData * imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photosModel.originalUrl]];
        // 回到主队列保存数据
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage * image = [UIImage imageWithData:imageData];
            UIImageWriteToSavedPhotosAlbum(image, weakSelf, @selector(image:didFinishSavingWithError:contextInfo:), nil);
        });
    });
}

// 保存图片到相册完成时回调方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    if (error) {
        [KVNProgress showErrorWithStatus:@"保存图片失败,请再次保存"];
    }
    else {
        [KVNProgress showSuccessWithStatus:@"噢耶,成功喽"];
    }
}
// 填充UIScrollView数据
- (void)loadPhotos
{
    // 图片尺寸
    CGFloat imageWidth = CGRectGetWidth(self.picturesScrollView.frame);
    CGFloat imageHeight = imageWidth / 1.78;
    CGFloat imageY = (CGRectGetHeight(self.picturesScrollView.frame) - imageHeight) / 2;
    
    for (int index = 0; index < self.photosArray.count; index++) {
        // 取出数据
        PhotosModel * model = self.photosArray[index];
        // 创建图片视图
        UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(index*imageWidth, imageY, imageWidth, imageHeight)];
        [self.picturesScrollView addSubview:imageView];
        [imageView sd_setImageWithURL:[NSURL URLWithString:model.originalUrl] placeholderImage:[UIImage imageNamed:@"egopv_photo_placeholder"]];
    }
    // 设置内容尺寸
    self.picturesScrollView.contentSize = CGSizeMake(imageWidth * self.photosArray.count, CGRectGetHeight(self.picturesScrollView.frame));
    // 按页显示
    self.picturesScrollView.pagingEnabled = YES;
    // 关闭弹簧效果
    self.picturesScrollView.bounces = NO;
    // 设置偏移量
    self.picturesScrollView.contentOffset = CGPointMake(self.selectedIndex * imageWidth, 0);
    // 修改当前图片位置
    self.picNumLabel.text = [NSString stringWithFormat:@"%ld Of %ld", self.selectedIndex+1, self.photosArray.count];
}

#pragma mark - UIScrollViewDelegate
// 滚动视图结束减速
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    // 获取当前页数
    self.selectedIndex = scrollView.contentOffset.x / scrollView.frame.size.width;
}

#pragma mark - KVO observer方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"keypath = %@, object = %@, change=%@", keyPath, object, change);
    // 修改标题的文字
    NSInteger currentIndex = [change[@"new"] integerValue] + 1;
    self.picNumLabel.text = [NSString stringWithFormat:@"%ld Of %ld", currentIndex, self.photosArray.count];
}```
##CategoryViewController

  • (void)setObjClass:(Class)objClass
    {
    _objClass = objClass;

    NSArray * objClasses = @[[LimitViewController class], [ReduceViewController class], [FreeViewController class], [HotViewController class]];
    NSArray * cateNames = @[@"限免", @"降价", @"免费", @"热榜"];
    NSArray * categories = @[@"limited", @"down", @"free", @"up"];

    // 找到对应位置
    NSInteger index = [objClasses indexOfObject:_objClass];
    self.categoryName = cateNames[index];
    self.category = categories[index];

    // 定制
    [self customNaviItem];
    }

  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    CategoryModel * model = self.categoryArray[indexPath.row];
    // 传值
    if (self.block) {
    self.block(model.categoryId);
    }
    [self.navigationController popViewControllerAnimated:YES];
    }```

LimitViewController

- (instancetype)init
{
    if (self = [super init]) {
        self.requestURL = kLimitUrl;
    }
    return self;
}```
##SubjectAppView

import "SubjectAppView.h"

import "StarView.h"

@implementation SubjectAppView
{
UIImageView * _appImageView;
UILabel * _appNameLabel;
UILabel * _commentLabel;
UILabel * _downloadLabel;
StarView * _starView;
}

// 重写初始化方法

  • (instancetype)init
    {
    if (self = [super init]) {
    [self createViews];
    }
    return self;
    }

  • (instancetype)initWithFrame:(CGRect)frame
    {
    if (self = [super initWithFrame:frame]) {
    [self createViews];
    }
    return self;
    }

  • (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
    if (self = [super initWithCoder:aDecoder]) {
    [self createViews];
    }
    return self;
    }

// 创建视图
// 1. 使用代码实现AutoLayout,需要将视图作为某个视图的子视图
// 2. 将translatesAutoresizingMaskIntoConstraints设为NO

  • (void)createViews
    {
    _appImageView = [[UIImageView alloc] init];
    _appImageView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:_appImageView];

    _appNameLabel = [[UILabel alloc] init];
    [self addSubview:_appNameLabel];

    _commentLabel = [[UILabel alloc] init];
    [self addSubview:_commentLabel];

    _downloadLabel = [[UILabel alloc] init];
    [self addSubview:_downloadLabel];

    _starView = [[StarView alloc] init];
    [self addSubview:_starView];

    // 将所有视图的关闭停靠模式
    for (UIView * view in self.subviews) {
    view.translatesAutoresizingMaskIntoConstraints = NO;
    }

    // 建立约束,通过mas_makeConstraints建立约束
    // 重新建立约束
    // _appImageView mas_remakeConstraints:<#^(MASConstraintMaker *make)block#>
    [_appImageView mas_makeConstraints:^(MASConstraintMaker *make) {
    // make 表示当前视图建立约束
    // 左、上、底部和父视图间距为3
    make.left.top.bottom.equalTo(self).offset(3);
    // 宽度和高度相等
    make.width.equalTo(_appImageView.mas_height);
    }];

    [_appNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    // 顶部对齐
    make.top.equalTo(_appImageView.mas_top);
    // 水平间距为5
    make.left.equalTo(_appImageView.mas_right).offset(5);
    }];

    [_commentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    // 左对齐
    make.left.equalTo(_appNameLabel.mas_left);
    // 中心点Y相等
    make.centerY.equalTo(_appImageView.mas_centerY);
    }];

    [_downloadLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    // 同一水平线
    make.centerY.equalTo(_appImageView.mas_centerY);
    make.right.equalTo(self.mas_right).offset(-10);
    }];

    [_starView mas_makeConstraints:^(MASConstraintMaker *make) {
    // 左对齐
    make.left.equalTo(_appNameLabel.mas_left);
    // 底部对齐
    make.bottom.equalTo(_appImageView.mas_bottom);
    // 设置确定的高度和宽度
    make.width.equalTo(@65);
    make.height.equalTo(@23);
    }];
    }

// 重写setter方法

  • (void)setAppModel:(Applications *)appModel
    {
    _appModel = appModel;
    [_appImageView sd_setImageWithURL:[NSURL URLWithString:_appModel.iconUrl] placeholderImage:[UIImage imageNamed:@"appproduct_appdefault"]];
    _appNameLabel.text = _appModel.name;
    // 图文混排
    // 创建评论的图文
    NSAttributedString * commentAttr = [self createAttrStringWithText:_appModel.ratingOverall image:[UIImage imageNamed:@"topic_Comment"]];

    _commentLabel.attributedText = commentAttr;

    // 创建下载的图文
    NSAttributedString * downloadAttr = [self createAttrStringWithText:_appModel.downloads image:[UIImage imageNamed:@"topic_Download"]];
    _downloadLabel.attributedText = downloadAttr;

    // 设置星标数值
    _starView.value = [_appModel.starOverall floatValue];
    }

// 创建图文混排的富文本
// CoreText.framework 图文混排框架

  • (NSAttributedString *)createAttrStringWithText:(NSString *)text image:(UIImage *) image
    {
    // NSTextAttachment 可以将图片转换为富文本内容
    NSTextAttachment * attachment = [[NSTextAttachment alloc] init];
    attachment.image = image;
    // 创建通过NSTextAttachment的富文本
    // 图片的富文本
    NSAttributedString * imageAttr = [NSAttributedString attributedStringWithAttachment:attachment];
    // 文字的富文本
    NSAttributedString * textAttr = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]}];

    NSMutableAttributedString * mulableAttr = [[NSMutableAttributedString alloc] init];
    [mulableAttr appendAttributedString:imageAttr];
    [mulableAttr appendAttributedString:textAttr];
    return [mulableAttr copy];
    }

@end

##SubjectCell

// 重写setter方法

  • (void)setModel:(SubjectModel *)model
    {
    _model = model;
    // 装载数据
    self.subjectTitleLabel.text = _model.title;
    [self.subjectImageView sd_setImageWithURL:[NSURL URLWithString:_model.img] placeholderImage:[UIImage imageNamed:@"topic_TopicImage_Default"]];
    [self.descImageView sd_setImageWithURL:[NSURL URLWithString:_model.desc_img] placeholderImage:[UIImage imageNamed:@"topic_Header"]];
    self.descTextView.text = _model.desc;

    // 先移除appsView的所有子视图,循环遍历数组中的对象执行SEL方法
    [self.appsView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    // 等价于
    // for (UIView * view in self.appsView.subviews) {
    // [view removeFromSuperview];
    // }

    // 填充App数据
    // 遍历数据
    // 记录前一个参照视图
    UIView * preView = nil;
    for (int index = 0; index < _model.applications.count; index++) {
    // 取出数据
    Applications * app = _model.applications[index];
    // 创建视图
    SubjectAppView * appView = [[SubjectAppView alloc] init];
    [self.appsView addSubview:appView];
    appView.appModel = app;

      // 建立约束
      [appView mas_makeConstraints:^(MASConstraintMaker *make) {
          // 左对齐
          make.left.equalTo(self.appsView.mas_left);
          // 右对齐
          make.right.equalTo(self.appsView.mas_right);
          // 高度
          make.height.equalTo(self.appsView.mas_height).multipliedBy(1/4.f);
          if (preView == nil) {
              make.top.equalTo(self.appsView.mas_top);
          }
          else {
              make.top.equalTo(preView.mas_bottom);
          }
      }];
      
      // 赋值
      preView = appView;
    

    }
    }



  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
    if (indexPath.section == 0 && indexPath.row == 2) {
    // 清除图片缓存
    [[SDImageCache sharedImageCache] clearDisk];
    [[SDImageCache sharedImageCache] clearMemory];
    // 清除网络数据
    }
    }```
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    // 设置友盟分享的Appkey
    [UMSocialData setAppKey:@"561e6bd3e0f55aa6e5003f5a"];
    
    return YES;
}

#ifdef DEBUG
#define NSLog(...) NSLog(__VA_ARGS__)
#else
#define NSLog(...)
#endif
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>查找附近的人正在使用的APP</string>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容