[iOS UI进阶 - 2.3] 彩票Demo v1.3

A.需求
  1. 真机调试
  2. "关于”模块
  3. 存储开关状态
  4. 打电话、发短信
  5. 应用评分
  6. 打开其他应用
  7. cell 在iOS6 和 iOS7的适配
  8. block的循环引用
  9. 屏幕适配
code source:  code source: https://github.com/hellovoidworld/HelloLottery
 
B.iOS真机测试小功能
(1)打电话
a.方法1
最简单最直接的方式:直接跳到拨号界面
1 NSURL *url = [NSURL URLWithString:@"tel://10086"];
2 [[UIApplication sharedApplication] openURL:url];
缺点
电话打完后,不会自动回到原应用,直接停留在通话记录界面
 
b.方法2
拨号之前会弹框询问用户是否拨号,拨完后能自动回到原应用
1 NSURL *url = [NSURL URLWithString:@"telprompt://10086"];
2 [[UIApplication sharedApplication] openURL:url];
缺点
因为是私有API,所以可能不会被审核通过
 
c.方法3
创建一个UIWebView来加载URL,拨完后能自动回到原应用
1 if (_webView == nil) {
2     _webView = [[UIWebView alloc] initWithFrame:CGRectZero];
3 }
4 [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"tel://10010"]]];
需要注意的是:这个webView千万不要添加到界面上来,不然会挡住其他界面
 
(2)发短信
a.方法1
直接跳到发短信界面,但是不能指定短信内容,而且不能自动回到原应用
1 NSURL *url = [NSURL URLWithString:@"sms://10086"];
2 [[UIApplication sharedApplication] openURL:url];
 
b.方法2
#mark:
注意要写对代理名称messageComposeDelegate和mailComposeDelegate,不是delegate
 
如果想指定短信内容,那就得使用MessageUI框架
包含主头文件
#import <MessageUI/MessageUI.h>

显示发短信的控制器
 1 MFMessageComposeViewController *vc = [[MFMessageComposeViewController alloc] init];
 2 // 设置短信内容
 3 vc.body = @"吃饭了没?";
 4 // 设置收件人列表
 5 vc.recipients = @[@"10086", @"13800138000"];
 6 // 设置代理(这里使用block封装,由于强指针持有self,会有内存泄露)
 7 vc.messageComposeDelegate = self;
 8 
 9 // 显示控制器
10 [self presentViewController:vc animated:YES completion:nil];
 
代理方法,当短信界面关闭的时候调用,发完后会自动回到原应用
 1 - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
 2 {
 3     // 关闭短信界面
 4     [controller dismissViewControllerAnimated:YES completion:nil];
 5    
 6     if (result == MessageComposeResultCancelled) {
 7         NSLog(@"取消发送");
 8     } else if (result == MessageComposeResultSent) {
 9         NSLog(@"已经发出");
10     } else {
11         NSLog(@"发送失败");
12     }
13 }
 
(3)发邮件
a.方法1
用自带的邮件客户端,发完邮件后不会自动回到原应用
1 NSURL *url = [NSURL URLWithString:@"mailto://[email protected]"];
2 [[UIApplication sharedApplication] openURL:url];
 
b.方法2
跟发短信的第2种方法差不多,只不过控制器类名叫做:MFMailComposeViewControlle
 1       // 方法2:使用控制器
 2         MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
 3        
 4         // 发送主题
 5         [mailController setSubject:@"重要作战会议"];
 6        
 7         // 邮件内容
 8         [mailController setMessageBody:@"就是那个...该吃饭了吧" isHTML:NO];
 9        
10         // 收件人
11         [mailController setToRecipients:@[@"[email protected]"]];
12        
13         // 抄送人
14         [mailController setCcRecipients:@[@"[email protected]"]];
15         
16         // 密送人
17          [mailController setBccRecipients:@[@"[email protected]"]];
18         
19         // 附件
20         UIImage *image = [UIImage imageNamed:@"LoginScreen"];
21         NSData *data = UIImagePNGRepresentation(image);
22         [mailController addAttachmentData:data mimeType:@"image/png" fileName:@"attach.png"];
23        
24         // 代理
25         mailController.mailComposeDelegate = shareController;
26        
27         // 弹出mail控制器
28         [shareController presentViewController:mailController animated:YES completion:nil];
 
邮件发送后的代理方法回调,发完后会自动回到原应用
 1 - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
 2 {
 3     // 关闭邮件界面
 4     [controller dismissViewControllerAnimated:YES completion:nil];
 5    
 6     if (result == MFMailComposeResultCancelled) {
 7         NSLog(@"取消发送");
 8     } else if (result == MFMailComposeResultSent) {
 9         NSLog(@"已经发出");
10     } else {
11         NSLog(@"发送失败");
12     }
13 }
 
(4)打开其他常见文件
如果想打开一些常见文件,比如html、txt、PDF、PPT等,都可以使用UIWebView打开
只需要告诉UIWebView文件的URL即可
至于打开一个远程的共享资源,比如http协议的,也可以调用系统自带的Safari浏览器:
1 NSURL *url = [NSURL URLWithString:@”http://www.baidu.com"];
2 [[UIApplication sharedApplication] openURL:url];
 
(5)应用跳转
有时候,需要在本应用中打开其他应用,比如从A应用中跳转到B应用
首先,应用app得有自己的URL地址(在Info.plist中配置)
例如:app应用的URL地址就是:hvw://com.ios.app
 
接着在某个应用中使用UIApplication完成跳转
1 NSURL *url = [NSURL URLWithString:@"hvw://com.ios.app"];
2 [[UIApplication sharedApplication] openURL:url];
 
(6)应用评分
为了提高应用的用户体验,经常需要邀请用户对应用进行评分
应用评分无非就是跳转到AppStore展示自己的应用,然后由用户自己撰写评论

如何跳转到AppStore,并且展示自己的应用
方法1
1 NSString *appid = @"444934666";
2 NSString *str = [NSString stringWithFormat:
3                  @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@", appid];
4 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];

方法2
1 NSString *str = [NSString stringWithFormat:
2                  @"itms-apps://itunes.apple.com/cn/app/id%@?mt=8", appid];
3 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
 
(7)真机调试步骤
真机调试的主要步骤
登录开发者主页
生成cer证书:cer是一个跟电脑相关联的证书文件,让电脑具备真机调试的功能
添加App ID:调试哪些app?
注册真机设备:哪台设备需要做真机调试?
生成MobileProvision文件:结合2、3、4生成一个手机规定文件
导入cer、MobileProvision文件

最终会得到2个文件
Cer文件:让电脑具备真机调试的功能
MobileProvision文件:哪台设备、哪些app、哪台电脑需要做真机调试?
 
 
C.实现
1.真机调试 - 分享功能
  • 新浪微博分享
  • 短信分享
  • 邮件分享
必须要打开系统自带的软件来进行
使用block代码来调用系统软件
 
技术分享
 
  1 //
  2 //  HVWShareViewController.m
  3 //  HelloLottery
  4 //
  5 //  Created by hellovoidworld on 15/1/9.
  6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
  7 //
  8 
  9 #import "HVWShareViewController.h"
 10 #import "HVWArrowSettingItem.h"
 11 #import "HVWSettingGroup.h"
 12 #import <MessageUI/MessageUI.h>
 13 
 14 @interface HVWShareViewController () <MFMessageComposeViewControllerDelegate, MFMailComposeViewControllerDelegate>
 15 
 16 @end
 17 
 18 @implementation HVWShareViewController
 19 
 20 - (void)viewDidLoad {
 21     [super viewDidLoad];
 22     // Do any additional setup after loading the view.
 23    
 24     // 配置数据
 25     HVWSettingItem *weiboShare = [HVWArrowSettingItem itemWithIcon:@"WeiboSina" title:@"新浪微博分享"];
 26     HVWSettingItem *smsShare = [HVWArrowSettingItem itemWithIcon:@"SmsShare" title:@"短信分享"];
 27   
 28     // 为了避免block内持有本控制器,导致内存泄露,先声明一个弱指针
 29     __weak HVWShareViewController *shareController = self;
 30    
 31     smsShare.runningBlock = ^ {
 32         // 方法1:只能打开短信窗口,不能指定内容
 33         //        NSURL *smsURL = [NSURL URLWithString:@"sms://10086"];
 34         //        [[UIApplication sharedApplication] openURL:smsURL];
 35 
 36         // 方法2:
 37         //获取短信发送控制器
 38         if (![MFMessageComposeViewController canSendText]) return;
 39        
 40         MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
 41        
 42         // 短信内容
 43         messageController.body = @"吃饭了没有?";
 44        
 45         // 收件人
 46         messageController.recipients = @[@"10086"];
 47        
 48         // 设置代理,特别注意代理是messageComposeDelegate,不是delegate
 49         messageController.messageComposeDelegate = shareController;
 50        
 51         // 显示控制器
 52         [shareController presentViewController:messageController animated:YES completion:nil];
 53     };
 54    
 55     HVWSettingItem *mailShare = [HVWArrowSettingItem itemWithIcon:@"MailShare" title:@"邮件分享"];
 56     mailShare.runningBlock = ^ {
 57       // 方法1:直接调用
 58 //        NSURL *mailURL = [NSURL URLWithString:@"mailto://[email protected]"];
 59 //        [[UIApplication sharedApplication] openURL:mailURL];
 60 
 61       // 方法2:使用控制器
 62         MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
 63        
 64         // 发送主题
 65         [mailController setSubject:@"重要作战会议"];
 66        
 67         // 邮件内容
 68         [mailController setMessageBody:@"就是那个...该吃饭了吧" isHTML:NO];
 69        
 70         // 收件人
 71         [mailController setToRecipients:@[@"[email protected]"]];
 72        
 73         // 抄送人
 74         [mailController setCcRecipients:@[@"[email protected]"]];
 75         
 76         // 密送人
 77          [mailController setBccRecipients:@[@"[email protected]"]];
 78         
 79         // 附件
 80         UIImage *image = [UIImage imageNamed:@"LoginScreen"];
 81         NSData *data = UIImagePNGRepresentation(image);
 82         [mailController addAttachmentData:data mimeType:@"image/png" fileName:@"attach.png"];
 83        
 84         // 代理
 85         mailController.mailComposeDelegate = shareController;
 86        
 87         // 弹出mail控制器
 88         [shareController presentViewController:mailController animated:YES completion:nil];
 89        
 90     };
 91    
 92     HVWSettingGroup *group = [[HVWSettingGroup alloc] init];
 93     group.items = @[weiboShare, smsShare, mailShare];
 94     [self.data addObject:group];
 95 }
 96 
 97 - (void)didReceiveMemoryWarning {
 98     [super didReceiveMemoryWarning];
 99     // Dispose of any resources that can be recreated.
100 }
101 
102 #pragma mark - MFMessageComposeViewControllerDelegate 代理方法
103 /** 关闭信息后 */
104 - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
105     // 关闭信息窗口
106     [controller dismissViewControllerAnimated:YES completion:nil];
107    
108     // 检测发送情况
109     if (MessageComposeResultSent == result) {
110         NSLog(@"成功发送!");
111     } else if (MessageComposeResultFailed == result) {
112         NSLog(@"发送失败!");
113     } else if (MessageComposeResultCancelled == result) {
114         NSLog(@"取消发送!");
115     } else {
116         NSLog(@"发生错误!");
117     }
118 }
119 
120 /** 关闭邮件后 */
121 - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
122     [controller dismissViewControllerAnimated:YES completion:nil];
123 }
124 
125 @end
 
2.“关于”模块
使用self.tableView.headerView自定义组头部
 
技术分享
 
(1)使用xib设计头部
技术分享
 
(2)创建一个类“设置”界面控制器
技术分享
 
应用评分
其实就是打开AppStore中相应的软件
协议头:itms-apps://
 
打电话
使用webView来打开URL
协议头:tel://
 
 1 //
 2 //  HVWAboutViewController.m
 3 //  HelloLottery
 4 //
 5 //  Created by hellovoidworld on 15/1/8.
 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.
 7 //
 8 
 9 #import "HVWAboutViewController.h"
10 #import "HVWArrowSettingItem.h"
11 #import "HVWSettingGroup.h"
12 
13 @interface HVWAboutViewController ()
14 
15 @end
16 
17 @implementation HVWAboutViewController
18 
19 - (void)viewDidLoad {
20     [super viewDidLoad];
21     // Do any additional setup after loading the view.
22    
23     // 准备一个webView
24     UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
25     [self.view addSubview:webView];
26    
27    
28     // 配置item和group
29     HVWSettingItem *gradeSupport = [HVWArrowSettingItem itemWithTitle:@"评分支持"];
30    
31     // 跳转到app store进行应用评分
32     gradeSupport.runningBlock = ^ {
33       // 其实这是网易新闻的app id
34         NSString *appid = @"425349261";
35         NSString *appStoreAddr = [NSString stringWithFormat:@"http://itunes.apple.com/app/id%@?mt=8", appid];
36         NSURL *appStoreURL = [NSURL URLWithString:appStoreAddr];
37         [[UIApplication sharedApplication] openURL:appStoreURL];
38     };
39    
40     HVWSettingItem *servicePhone = [HVWArrowSettingItem itemWithTitle:@"客服电话"];
41     NSString *phoneNumber = @"020-83568090";
42     servicePhone.subTitle = phoneNumber;
43    
44     servicePhone.runningBlock = ^{
45         NSURL *phoneURL = [NSURL URLWithString:[NSString stringWithFormat:@"tel://%@",phoneNumber]];
46         [webView loadRequest:[NSURLRequest requestWithURL:phoneURL]];
47     };
48 
49     HVWSettingGroup *group = [[HVWSettingGroup alloc] init];
50     group.items = @[gradeSupport, servicePhone];
51     [self.data addObject:group];
52    
53     // header
54     // 配置header view
55     UINib *nib = [UINib nibWithNibName:@"HVWAboutHeader" bundle:[NSBundle mainBundle]];
56     UIView *view = [[nib instantiateWithOwner:nil options:nil] lastObject];
57     self.tableView.tableHeaderView  = view;
58     self.tableView.tableHeaderView.frame = view.bounds;
59 }
60 
61 - (void)didReceiveMemoryWarning {
62     [super didReceiveMemoryWarning];
63     // Dispose of any resources that can be recreated.
64 }
65 
66 @end
 
#mark:
1.不要使用autolayout,不然header显示不正确
2.不能使用代理方法 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
发现这样设置的头部是从section==1才开始的
 
 
3.存储开关状态
(1)监听开关
(2)及时存储
(3)初始化读取数据
使用preferences存储,key是cell的title,value就是bool值
监听所有的“开关”类型cell,让“开关”类型的cell在开关改变值之后自动存储键值
技术分享
 
HVWSettingCell:
 1 /** 创建“开关”类型的cell */
 2 - (UISwitch *)switchView {
 3     if (nil == _switchView) {
 4         _switchView = [[UISwitch alloc] init];
 5        
 6         // 监听开关
 7         [_switchView addTarget:self action:@selector(switchChange) forControlEvents:UIControlEventValueChanged];
 8     }
 9     return _switchView;
10 }
11  
12 /** 开关变化事件 
13 * 存储开关状态到preferences
14 */
15 - (void) switchChange {
16     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
17     // 使用cell的title作为key,开关状态作为value
18     [defaults setBool:self.switchView.isOn forKey:self.item.title];
19     // 立即存储
20     [defaults synchronize];
21 }
22 
23 /** 读取开关状态 */
24 - (BOOL) readSwitchStatus {
25     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
26     return [defaults boolForKey:self.item.title];
27 }
 
4.打开其他应用
技术分享
 
一样使用openURL
(1)给要打开的app配置应用URL(可以配置多个)
a.info.plist:添加URL types
b.添加协议头(可以配置多个,也可以不配)
c.添加identifier
技术分享
 
(2)打开应用/打开AppStore下载应用
所以应该配置两个URL:
  • 打开应用URL
  • 下载应用URL
 
a.判断app是否已经安装
UIApplication中的方法: - (BOOL) canOpenURL:(NSURL *) url
 
这里使用素材带的json数据
技术分享
 
 
HVWProductViewController
 1 #pragma mark <UICollectionViewDelegate>
 2 /** 选择事件 */
 3 - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
 4     HVWProduct *product = self.products[indexPath.item];
 5    
 6     NSURL *appUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", product.urlSchema, product.urlId]];
 7     NSURL *appStoreUrl = [NSURL URLWithString:product.appStoreUrl];
 8 
 9     UIApplication *application = [UIApplication sharedApplication];
10     // 先判断是否存在此app
11     if ([application canOpenURL:appUrl]) {
12         // 打开app
13         [application openURL:appUrl];
14     } else {
15         // 跳转到app store下载app
16         [application openURL:appStoreUrl];
17     }
18 }
 
5.cell的 iOS6 & iOS7的适配(这里是针对iOS6适配成iOS7的样式)
由于cell在iOS6和iOS7上cell的显示不一样,所以需要进行统一适配
a.tableView使用统一的自定义背景
使用自带小图进行平铺操作合成背景
<1>清空backgroundView(避免在group样式下屏蔽了backgroundColor)
<2>使用平铺设置backgroundColor
 
b.cell使用统一的背景
c.cell使用统一的选中背景
<1>重写初始化方法initWithStyle
<2>设置选中时背景,选中状态适当调整颜色
 
d.统一cell内子控件样式
<1>清除子控件背景颜色
 
e.调整cell宽度,消除两边空隙
  • 重写cell的setFrame方法
  • 在iOS6及以下系统修改cell的位置尺寸,适配iOS7
 
f.加上cell之间的分隔线
  • iOS6中,cell中右部分的子控件不属于contentView,所以contentView的宽度是不定的
  • 每组的最后一行不需要分隔线
 
#mark:系统升级OSX10.10还有Xcode6之后,苹果似乎已经完全舍弃了iOS6,即使使用Xcode5下载了iOS6模拟器,打开后就是黑屏一片,不能使用。
 
 
6.block内的循环引用导致内存泄露
在block代码内,使用了强指针指向block所在的对象(例如self),导致循环引用,不能释放资源
将block内的对象引用设置为弱指针:使用"__weak"修饰符
注意:也不要使用强指针访问对象的属性
技术分享
 
 
7.屏幕适配
Autoresizing属性:能够设置控件在不同尺寸屏幕下的大小
要先取消勾选Autolayout才能使用Autoresizing属性
技术分享
 
四周的4条指示条:代表和边框的距离固定
中间的2条指示条:代表宽高的自动适配

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。