一般我们更新APP版本好的时候都会对用户推送一个有好的新特性页面,往往以轮播页面的形式来展示这次的更新或者APP的一些主要特性。
一般思路可以分为以下几个要点:
- 1.在
AppDelegate
获取最新版本号 - 2.用最新版本号与上一次(存在)版本号对比
- 相等 : 进入主界面
- 不等 : 进入新特性页面
- 3.显示完新特性页面后进入主程序页面
- 4.存储APP版本号
1.分析CFBundleShortVersionString
与CFBundleVersion
的不同
在开发中一般我们会把这两个来标识应用版本号的变量定义宏,从宏定义来看一个是
CFBundleVersion
获取当前版本号,CFBundleShortVersionString
是获取当前biuld号。具体不同如下:
//获取当前版本号
#define BUNDLE_VERSION [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]`
//获取当前版本的biuld
#define BIULD_VERSION [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]
Key | Xcode name | Summary |
---|---|---|
CFBundleShortVersionString | Bundle versions string, short | The release-version-number string for the bundle |
CFBundleVersion | Bundle version | The build-version-number string for the bundle |
不同点 :
CFBundleShortVersionString
和表示应用构建迭代(包括发布与未发布)的CFBundleVersion
的值不同,并且这个值可以被包含在InfoPlist.strings
文件中进行本地化。一般Build
供内部使用来确定唯一版本。相同点从命名规则上来看 :两个都是只能包含数字
(0~9)
和.
分割,整数开头0会被忽略。
- 具体可以去苹果官方文档中进行查阅,由于两者还有所差别我建议使用
CFBundleVersion
作为程序的版本号标识。
2.实现新特性页面的判断
1.新建一个工具类 DCAppVersionTool
用来版本号的存取
DCAppVersionTool.h
/**
* 获取之前保存的版本
*
* @return NSString类型的AppVersion
*/
+ (NSString *)dc_GetLastOneAppVersion;
/**
* 保存新版本
*/
+ (void)dc_SaveNewAppVersion:(NSString *)version;
DCAppVersionTool.m
// 获取保存的上一个版本信息
+ (NSString *)dc_GetLastOneAppVersion {
return [[NSUserDefaults standardUserDefaults] stringForKey:@"AppVersion"];
}
// 保存新版本信息(偏好设置)
+ (void)dc_SaveNewAppVersion:(NSString *)version {
[[NSUserDefaults standardUserDefaults] setObject:version forKey:@"AppVersion"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
2.在AppDelegate
的didFinishLaunchingWithOptions
方法中抽取一个方法[self setUpRootVC];
进行如下判断
#pragma mark - 根控制器
- (void)setUpRootVC
{
if ([BUNDLE_VERSION isEqualToString:[DCAppVersionTool dc_GetLastOneAppVersion]]) {//判断是否当前版本号等于上一次储存版本号
// 进入主程序页面
}else{
[DCAppVersionTool dc_SaveNewAppVersion:BUNDLE_VERSION]; //储存当前版本号
// 进入新特性页面
}
3.实现新特性页面的封装
在实现轮播页面上我打算用UICollectionView
来实现,让Cell内部展示素材图片,Cell的尺寸等于屏幕的尺寸。当主要功能实现后我们应该再考虑结合具体素材的情况下是否添加跳过,UIPageControl
和滑动最后一页再往后滑动自动跳转到主页面等功能。其次把以上功能进行一层简易的封装。
封装思路: 首先我在新特性页面
DCNewFeatureViewController.h
文件中外铺一个block
方法
/**
新特性属性设置
*imageArray 图片数组
*showSkip 是否展示跳过
*selColor 选择小圆点的颜色
*showPageCount 是否展示小圆点
@param BaseSettingBlock 基础设置
*/
- (void)setUpFeatureAttribute:(void(^)(NSArray **imageArray,UIColor **selColor,BOOL *showSkip,BOOL *showPageCount))BaseSettingBlock;
封装思路: 从方法中能看出,此方法提供了一个图片数组的接口,两个是否,是否展示小圆点,以及是否展示右上角跳过按钮,和一个小圆点选中自定义颜色的接口,用来供调用选择。
核心代码
- 懒加载初始化
UICollectionView
、跳过按钮和小圆点UIPageControl
- (UICollectionView *)collectionView
{
if (!_collectionView) {
UICollectionViewFlowLayout *dcFlowLayout = [UICollectionViewFlowLayout new];
dcFlowLayout.minimumLineSpacing = dcFlowLayout.minimumInteritemSpacing = 0;
dcFlowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:dcFlowLayout];
_collectionView.delegate = self;
_collectionView.dataSource = self;
_collectionView.frame = [UIScreen mainScreen].bounds;
_collectionView.pagingEnabled = YES;
_collectionView.bounces = NO;
_collectionView.showsHorizontalScrollIndicator = NO;
[self.view insertSubview:_collectionView atIndex:0];
[_collectionView registerClass:[DCNewFeatureCell class] forCellWithReuseIdentifier:DCNewFeatureCellID];
}
return _collectionView;
}
- (UIButton *)skipButton
{
if (!_skipButton) {
_skipButton = [UIButton buttonWithType:UIButtonTypeCustom];
_skipButton.frame = CGRectMake(ScreenW - 85, 30, 65, 30);
[_skipButton addTarget:self action:@selector(skipButtonClick) forControlEvents:UIControlEventTouchUpInside];
_skipButton.hidden = YES;
_skipButton.backgroundColor = [[UIColor lightGrayColor]colorWithAlphaComponent:0.8];
_skipButton.titleLabel.font = [UIFont systemFontOfSize:14];
_skipButton.layer.cornerRadius = 15;
_skipButton.layer.masksToBounds = YES;
[self.view addSubview:_skipButton];
}
return _skipButton;
}
- (UIPageControl *)pageControl
{
if (!_pageControl && _imageArray.count != 0) {
_pageControl = [[UIPageControl alloc] init];
_pageControl.numberOfPages = _imageArray.count;
_pageControl.userInteractionEnabled = false;
[_pageControl setPageIndicatorTintColor:[UIColor lightGrayColor]];
UIColor *currColor = (_selColor == nil) ? [UIColor darkGrayColor] : _selColor;
[self.pageControl setCurrentPageIndicatorTintColor:currColor];
_pageControl.frame = CGRectMake(0, ScreenH * 0.95, ScreenW, 35);
[self.view addSubview:_pageControl];
}
return _pageControl;
}
- 根据
Block
回调中的两个是否在Set方法中进行显示影藏判断
#pragma mark - 是否展示跳过按钮
- (void)setShowSkip:(BOOL)showSkip
{
_showSkip = showSkip;
self.skipButton.hidden = !self.showSkip;
}
#pragma mark - 是否展示page小圆点
- (void)setShowPageCount:(BOOL)showPageCount
{
_showPageCount = showPageCount;
self.pageControl.hidden = !self.showPageCount;
}
- 设置数据源和代理,并且在Cell中进行对是否有跳过素材按钮的判断
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return _imageArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DCNewFeatureCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:DCNewFeatureCellID forIndexPath:indexPath];
cell.nfImageView.image = (DCIsiPhoneX) ? [UIImage imageNamed:[NSString stringWithFormat:@"%@_x",_imageArray[indexPath.row]]] : [UIImage imageNamed:_imageArray[indexPath.row]];
cell.hideBtnImg = @"hidden";
[cell dc_GetCurrentPageIndex:indexPath.row lastPageIndex:_imageArray.count - 1];
WEAKSELF
cell.hideButtonClickBlock = ^{
[weakSelf restoreRootViewController:[[DCTabBarController alloc] init]];
};
return cell;
}
#pragma mark - <UICollectionViewDelegateFlowLayout>
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(ScreenW, ScreenH);
}
- UICollectionViewCell.h
/* 跳过图片素材 */
@property (strong , nonatomic)NSString *hideBtnImg;
/**
用来获取页码
@param currentIndex 当前index
@param lastIndex 最后index
*/
- (void)dc_GetCurrentPageIndex:(NSInteger)currentIndex lastPageIndex:(NSInteger)lastIndex;
- UICollectionViewCell.m
- (void)setHideBtnImg:(NSString *)hideBtnImg
{
_hideBtnImg = hideBtnImg;
if (hideBtnImg.length != 0) {
[self.hideButton sizeToFit]; //自适应
[self.hideButton setImage:[UIImage imageNamed:hideBtnImg] forState:UIControlStateNormal];
self.hideButton.center = CGPointMake(ScreenW * 0.5, ScreenH * 0.9);
}
}
#pragma mark - 获取页码index
- (void)dc_GetCurrentPageIndex:(NSInteger)currentIndex lastPageIndex:(NSInteger)lastIndex
{
_hideButton.hidden = (currentIndex == lastIndex) ? NO : YES; //只有当前index和最后index相等时隐藏按钮才显示
}
最后还有一个当用户滑到最后一张图片的时候,仍然想继续往后滑动来进入主页面,这个时候我们要通过
UIScrollView
的代理方法。这里有一个注意点,由于上方我们初始化UICollectionView
的时候设置了bounces
属性为NO,所以,我们还需在实现跳转之前做一层判断。如下:
#pragma mark - 通过代理来让她滑到最后一页再左滑动就切换控制器
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (_imageArray.count < 2) return; //一张图或者没有直接返回
_collectionView.bounces = (scrollView.contentOffset.x > (_imageArray.count - 2) * ScreenW) ? YES : NO;
if (scrollView.contentOffset.x > (_imageArray.count - 1) * ScreenW) {
[self restoreRootViewController:[[DCTabBarController alloc] init]];
}
}
最后展示下实现效果GIF
DCNewFeatureViewController *dcFVc = [[DCNewFeatureViewController alloc] init];
[dcFVc setUpFeatureAttribute:^(NSArray *__autoreleasing *imageArray, UIColor *__autoreleasing *selColor, BOOL *showSkip, BOOL *showPageCount) {
*imageArray = @[@"guide1",@"guide2",@"guide3",@"guide4"];
*showPageCount = YES;
*showSkip = YES;
}];