iOS 推送手机消息背后的技术

作者:allenzzhao,腾讯  IEG运营开发工程师

消息推送我们几乎每天都会用到,但你知道iOS中的消息推送是如何实现的吗?本文将从推送权限申请,到本地和远程消息推送,再到App对推送消息的处理等多个步骤,详细介绍iOS中消息推送的工作流程。

1、概述

消息推送是一种App向用户传递信息的重要方式,无论App是否正在运行,只要用户打开了通知权限就能够收到推送消息。开发者通过调用iOS系统方法就可以发起本地消息推送,例如我们最常见的闹钟应用,App能够根据本地存储的闹钟信息直接发起本地通知,因此即使没有网络也能收到闹钟提醒。

远程消息推送则是由业务方服务器将消息内容按照固定格式发送到Apple Push Notitfication service(简称APNs),然后再经由苹果的APNs服务器推送到用户设备上,例如腾讯新闻可以向用户推送时事热点新闻,QQ邮箱可以为用户推送收到新邮件的提醒,游戏App可以通过这种方式通知玩家有新的游戏福利,既能够及时地通知用户重要信息,也能够促使用户通过推送消息打开或唤醒App,提高App的使用率。除了标题、内容、提示音和角标数字等固定推送参数以外,开发者还可以在推送消息中增加自定义参数,让用户在点击推送消息时能够直达相关新闻、邮件或福利页面,提供更好的用户体验和页面的曝光率。


2、XCode配置

在使用消息推送相关功能之前,我们首先需要准备支持推送功能的证书,个人开发者可以参考腾讯云的TPNS文档,在苹果开发者中心中配置和导出推送证书。

此外,还需要在XCode的工程配置Signing & Capabilities配置中增加消息推送权限,在操作完成后Xcode会自动生成或更新工程的entitlements文件,增加如图所示的APS Environment字段。

3、申请消息推送权限

无论是本地推送还是远程推送,在推送前都必须要先向用户申请推送权限,只有用户授权后才能够收到推送消息。

苹果在iOS10中引入了UserNotifications框架,将推送相关功能进行了封装和升级,除了以前UIApplication可以做到的一些基本的本地和远程消息推送功能外,还增加了撤回或修改推送消息、自定义通知UI、推送消息前台显示等功能。在iOS10及以上的版本中,苹果推荐开发者使用requestAuthorizationWithOptions:completionHandler:方法向用户申请消息推送权限,该方法需要指定一个用于描述推送权限的UNAuthorizationOptions类型参数,包括alert(消息的标题、文字等内容)、sound(消息提示音)、badge(App右上角显示的角标);还可以在该方法的completionHandler回调方法中通过granted参数来判断用户是否允许了授权。相关代码如下:

#import <UserNotifications/UserNotifications.h>
……
[[UNUserNotificationCenter currentNotificationCenter]
requestAuthorizationWithOptions:UNAuthorizationOptionSound|UNAuthorizationOptionAlert|UNAuthorizationOptionBadge
completionHandler:^(BOOL granted, NSError * _Nullable error) {if(granted){//用户允许了推送权限申请}else{//用户拒绝了推送权限申请}
}];

在iOS9中,直接使用UIApplication的registerUserNotificationSettings方法即可,该方法同样需要通过配置sound、alert、badge等参数,但是没有提供用于判断用户点击了授权还是拒绝的回调方法。相关代码如下:

[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)categories:nil]];

要注意无论是UserNotifications还是UIApplication的申请推送权限的方法,上文中的申请用户授权的系统弹窗都只会显示一次,iOS会记录用户对于该App的授权状态,不会向用户重复申请授权。消息推送是App的一项重要功能,同时也是很好的运营手段,因此很多App在启动后会检查消息推送的授权状态,如果用户拒绝了消息推送权限,仍然会以一定的频率弹窗提醒用户,在iOS的设置中心中再去打开App的推送权限。相关代码如下:

if(@available(iOS 10.0,*)){[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {if (UNAuthorizationStatusDenied == settings.authorizationStatus) {//用户拒绝消息推送,弹窗提示引导用户去系统设置中进行授权UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"未打开推送功能" message:@"请在设备的\"设置-App-通知\"选项中,允许通知" preferredStyle:UIAlertControllerStyleAlert];UIAlertAction* cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action){[alert dismissViewControllerAnimated: YES completion: nil];}];UIAlertAction* ok = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){[alert dismissViewControllerAnimated: YES completion: nil];NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];if([[UIApplication sharedApplication] canOpenURL:url]){NSURL*url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];[[UIApplication sharedApplication] openURL:url];}}];[alert addAction: cancel];[alert addAction: ok];[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated: YES completion: nil];}}];
}else{UIUserNotificationSettings *setting = [[UIApplication sharedApplication] currentUserNotificationSettings];if (UIUserNotificationTypeNone == setting.types) {//用户拒绝消息推送,处理方式同上}
}

4、本地推送


在iOS10中,UserNotifications框架为我们提供了UNMutableNotificationContent对象描述消息推送的标题、内容、提示音、角表等内容,UNNotificationTrigger对象描述消息推送的推送时间策略,UNNotificationRequest对象整合推送内容和时间。每个Request对象都需要配置一个id来标识该条推送内容,UNUserNotificationCenter通过该id来管理(包括增加、删除、查询和修改)所有的Request。UNNotificationTrigger有四个子类,分别是UNTimeIntervalNotificationTrigger用于通过时间间隔控制消息推送,UNCalendarNotificationTrigger通过日期控制消息推送,UNLocationNotificationTrigger通过地理位置控制消息推送,UNPushNotificationTrigger远程消息推送对象。相关代码如下:

//推送内容
UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title = @"推送标题";
content.body = @"推送内容";
content.sound = [UNNotificationSound defaultSound];//默认提示音
//日期推送,今日15:53:00推送本地消息
NSDateComponents* date = [[NSDateComponents alloc] init];
date.hour = 15;
date.minute = 53;
UNCalendarNotificationTrigger* calendarTrigger = [UNCalendarNotificationTriggertriggerWithDateMatchingComponents:date repeats:NO];
//倒计时推送,2s后推送本地消息
UNTimeIntervalNotificationTrigger *intervalTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:2 repeats:NO];
UNNotificationRequest* request = [UNNotificationRequestrequestWithIdentifier:@"testId" content:content trigger:calendarTrigger];
//将推送请求添加到管理中心才会生效
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {if (error != nil) {NSLog(@"%@", error.localizedDescription);}
}];

在iOS9中,UIApplication提供了presentLocalNotificationNow和scheduleLocalNotification两个本地消息推送的方法,分别表示立即推送和按照固定日期推送,UILocalNotification同时描述了消息内容和推送的时机。示例代码是一个2s后推送的本地消息,soundName属性用于描述消息的提示音,用户可以自定义提示音(需要将音频文件打包到安装包中)或者使用默认提示音乐,repeatInterval和repeatCalendar属性分别用于根据时间差和日期进行重复提示的操作。相关代码如下:

UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:2];
notification.alertTitle = @"推送标题";
notification.alertBody = @"推送内容";
//notification.soundName = UILocalNotificationDefaultSoundName;
notification.soundName = @"mysound.wav";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];

5、远程推送

不同于本地消息推送不依赖网络请求,可以直接调用iOS系统方法,远程消息推送的实现涉及到用户设备、我们自己的业务方服务器和苹果的APNs服务的交互。不同于Android系统中远程消息推送的实现,需要App自身通过后台服务与业务服务器维持长链接通信,iOS中的消息推送是操作系统与苹果的APNs服务器直接交互实现的,App自身并不需要维持与服务器的连接。只要用户开启了推送权限,我们的业务服务器就可以随时通过调用APNs服务向用户推送通知,这样既能够为开发者和用户提供安全稳定的推送服务,也够节省系统资源消耗,提高系统流畅度和电池续航能力。

iOS客户端远程消息推送的实现可以分为以下几个流程:

  1. 用户的iphone通过iOS的系统方法调用与苹果的APNs服务器通信,获取设备的deviceToken,它是由APNs服务分配的用于唯一标识不同设备上的不同App,可以认为是由deviceID、bundleId和安装时的相关信息生成的,App的升级操作deviceToken不变,卸载重装App、恢复和重装操作系统后的deviceToken会发生变化。

  2. 苹果的APNs服务是基于deviceToken实现的,因此需要将设备的deviceToken发送到我们的业务服务器中,用于后续的消息推送。一个设备可能登录过多个用户,一个用户也可能在多个设备中登录过,当我们需要给不同用户推送不同的消息时,除了deviceToken之外,我们还需要保存用户的openid与deviceToken的映射关系。我们可以在用户登录成功后的时机更新openid和deviceToken的映射关系,用户退出后取消映射关系,只保存用户最后登录设备的deviceToken,避免一个设备收到多个重复通知和一个用户在不同设备收到多个通知等情况。

  3. 在新闻类App出现事实热点新闻时,后台服务就可以携带消息内容和deviceToken等内容,向苹果的APNs服务发起消息推送请求,推送消息的实现是异步的,只要请求格式和deviceToken检查通过APNs服务就不会报错,但是用户还是可能因为网络异常或者关闭了推送权限等原因收不到推送消息。

  4. APNs服务向用户设备推送消息这一步也是异步的,在用户关机或网络异常收不到推送的情况下,APNs会为每个deviceToken保留最后一条推送消息,待网络恢复后再次推送。

5.1、获取设备deviceToken

在App启动时,我们可以通过UIApplication的registerForRemoteNotifications方法向苹果的APNS服务器请求deviceToken,如果请求成功则didRegisterForRemoteNotificationsWithDeviceToken回调方法会被执行,为了便于业务服务器的调用,我们一般会将二进制的deviceToken转换为16进制的字符串后再进行存储;如果请求失败则didFailToRegisterForRemoteNotificationsWithError方法也会被调用,并附带具体的错误信息。相关代码如下:

//调用系统方法请求deviceToken
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[[UIApplication sharedApplication] registerForRemoteNotifications];
}
//deviceToken获取成功的回调
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{NSString *deviceTokenStr;NSUInteger length = deviceToken.length;if (![deviceToken isKindOfClass:[NSData class]] || length == 0) {return;}const unsigned char *bytes = (const unsigned char *)deviceToken.bytes;NSMutableString *hex = [NSMutableString new];for (NSInteger i = 0; i < deviceToken.length; i++) {[hex appendFormat:@"%02x", bytes[i]];}deviceTokenStr = [hex copy];NSLog(@"%@", deviceTokenStr);
}
//deviceToken获取失败的回调
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{NSLog(@"error,%@",error);
}

5.2、后台调用APNs推送

业务方服务器调用APNs服务时首先要建立安全连接,进行开发者身份的认证,分为基于证书(Certificate-Based)和基于Token(Token-Based)的认证两种方式,比较常用的是基于证书的认证方式。推送证书分为开发环境和生产环境的证书,分别对应不同的APNs推送接口,我们从苹果开发者平台或者第三方平台导出的推送证书一般有p12和pem两种格式的文件,为了便于接口调用我们可以通过以下命令将p12格式的文件转换为pem证书。

openssl pkcs12 -in push_dev.p12 -out push_dev.pem -nodes

基于证书建立TLS连接的流程如下图所示:

  1. 业务方服务器(Provider)向APNs服务器发起建立TLS连接的请求。

  2. APNs服务器返回的它的证书,供业务方服务器校验。

  3. 业务方服务器提供自己的推送证书,供APNs服务器校验。

  4. APNs服务器验证业务方服务器提供的推送证书无误后,TLS连接就已经建立完成,之后业务方服务器就可以直接向APNs发送消息推送请求了。

业务方与APNs建立请求的简易实现的PHP代码实现如下:

$deviceToken= '22124c450762170ca2ddb32a50381dd2c3026dbdb020f6dddcabefdca724fdd6';
//dev params
$devUrl = 'ssl://gateway.sandbox.push.apple.com:2195';
$devCertificate = 'push_dev.pem';
//product params
$proUrl = 'ssl://gateway.push.apple.com:2195';
$proCertificate = 'push_pro.pem';
// Change 2 : If any
$title = '标题';//消息标题
$content = '消息内容';//内容
$ctx = stream_context_create();
// Change 3 : APNS Cert File name and location.
stream_context_set_option($ctx, 'ssl', 'local_cert', $devCertificate);
// Open a connection to the APNS server
$fp = stream_socket_client($devUrl, $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array('alert' =>array('title'=>$title,'body'=>$content),'sound' => 'default');
//自定义内容
$body['userInfo'] = array('url' => 'https://www.qq.com',
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
//发送多个就调用多次fwrite
//$result = fwrite($fp, $msg, strlen($msg));
echo $msg;
if (!$result)echo 'Message not delivered' . PHP_EOL;
elseecho 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);

业务方服务器通过证书与APNs建立安全连接后可以进行连续多次的消息推送操作,每次消息推送都要指定deviceToken和Payload参数。Payload是一个json对象,用于配置iOS在收到远程消息推送时的展现形式,aps参数包含了苹果预设的alert、sound、badge等参数,其中alert参数可以是字符串,或者包含title、body等参数的字典类型;badge参数使用整形设置App图标右上角显示的数字,badge设置为0时角标不会显示;sound参数用于设置推送的声音,不传该参数或者传递空字符串则推送不会发出提示音,设置为default时使用系统默认提示音,也可以设置为具体的音频文件名,需要提前音频文件放到项目的bundle目录,且时长不能超过30s。

除了预设参数以外,我们还可以在aps的同级自定义一些参数,这些参数也可以是字典类型,再嵌套其他参数,例如示例代码中我们自定义的userInfo对象,但是一般推送消息的payload不宜过大,应控制在4K以内,建议只透传一些id和url等关键参数,具体的内容由客户端在收到推送时再去通过网络请求获取。

{"aps" : {"alert" : {"title" : "Game Request","subtitle" : "Five Card Draw","body" : "Bob wants to play poker",},"badge" : 9,"sound" : "gameMusic.wav",},"gameID" : "12345678"
}

上述payload包含了常见的推送消息的标题、副标题、内容、消息提示音、App的角标数字等预设参数,以及一个开发者自定义的gameID参数。用户点击推送消息后会自动启动或从后台唤醒App,我们可以在系统的回调方法中获取到自定义参数,并根据gameID自动为用户打开该游戏页面。

5.3、消息推送调试工具

在进行APNs接口调试时,我们可以利用一些优秀的推送调试工具帮助我们验证payload或证书等内容的合法性。本文介绍两款比较流行的开源软件,分别是国外的Knuff和国内开发者维护的smartPush。

  • Knuff:https://github.com/KnuffApp/Knuff

  • SmartPush:https://github.com/shaojiankui/SmartPush

6、App推送消息的处理

在iOS10中,UserNotifications框架为开发者提供了UNUserNotificationCenterDelegate协议,开发者可以通过实现协议中的方法,在App接收到推送消息和用户点击推送消息时进行一些业务逻辑的处理。无论是本地推送还是远程推送的消息,App的运行状态都可能处于以下三种状态:

  1. App正在前台运行,此时用户正在使用App,收到推送消息时默认不会弹出消息提示框,willPresentNotification回调方法会被调用,开发者可以从UNNotification对象中获取该推送消息的payload内容,进而获取自定义参数,然后显示一个自定义弹窗提示用户收到了新的消息;也可以在willPresentNotification方法中通过completionHandler函数的调用让推送消息直接在前台显示,用户点击前台显示的推送消息时,didReceiveNotificationResponse回调方法也会被执行。

  2. App在后台运行,此时用户点击推送消息会将App从后台唤醒,didReceiveNotificationResponse回调方法会被执行,开发者可以在该方法中获得payload,解析自定义参数并自动打开对应的页面。

  3. App尚未启动,此时用户点击推送消息会打开App,开发者可以从launchOptions中获取本地或远程推送消息中的自定义参数,待页面初始化完成后进行相关页面的跳转。

#import <UserNotifications/UserNotifications.h>
@interface AppDelegate ()<UNUserNotificationCenterDelegate>
@end@implementation AppDelegate
//在App启动后就将AppDelegate对象配置为NotificationCenter的delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[UNUserNotificationCenter currentNotificationCenter].delegate = self;// NSDictionary *localNotification = [launchOptions valueForKey:UIApplicationLaunchOptionsLocalNotificationKey];NSDictionary *remoteNotification = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];if(remoteNotification){//app已退出,点击拉起了appNSDictionary *params = userInfo[@"userInfo"];//此时NavigationController还未初始化,可以先暂存参数,稍后跳转[PageSwitch handlePushSwitch:params];}
}
//用户点击推送消息的回调
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){UNNotification *noti = ((UNNotificationResponse *)response).notification;NSDictionary *userInfo = noti.request.content.userInfo;NSDictionary *params = userInfo[@"userInfo"];//根据消息推送中的参数,在用户点击推送时自动进行跳转[PageSwitch handlePushSwitch:params];
}
//App在前台运行时收到推送消息的回调
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){//可以让App在前台运行时也能收到推送消息completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert);
}

在iOS9中,UIApplication提供了下面三个消息推送的处理方法,分别是远程消息推送、远程静默推送和本地消息推送的回调处理方法。前两个回调方法都能够用于App远程消息推送的处理,同时使用时只有远程静默推送方法会被调用,当payload包含参数content-available=1时,该推送就是静默推送,静默推送不会显示任何推送消息,当App在后台挂起时,静默推送的回调方法会被执行,开发者有30s的时间内在该回调方法中处理一些业务逻辑,并在处理完成后调用fetchCompletionHandler。

//远程消息推送回调方法,ios(3.0, 10.0)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;
//远程静默推送回调方法,ios(7.0, *)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler API_AVAILABLE(ios(7.0));
//本地消息推送回调方法,ios(4.0, 10.0)
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

UIApplication中的这三个方法在:①App在前台运行时收到通知,②App在后台运行时用户点击推送消息拉起App,这两种场景下都会被调用,区别是前两种方法对应远程消息推送的接收和点击触发响应,didReceiveLocalNotification用于本地消息推送。我们可以通过UIApplication的applicationState属性来判断App是否在前台运行,然后分别实现:①用户点击消息唤起后台App并打开对应页面,②用户前台使用App时显示自定义弹窗。

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{if([UIApplication sharedApplication].applicationState == UIApplicationStateActive){NSLog(@"在前台,%@",userInfo);}else{NSLog(@"从后台进入前台,%@",userInfo);NSDictionary *params = userInfo[@"userInfo"];if([Tools isValidString:params[@"url"]]){NSString *routeUrl = params[@"url"];[PageSwitch handlePushSwitch:params];}}
}

7、总结

本文首先介绍了消息推送相关的工程配置和推送权限的申请,然后分别介绍了本地和远程消息推送的不同使用场景和实现方法,最后介绍了App在收到推送消息后的相关回调方法和处理逻辑。在实际的项目开发中,我们往往会选择腾讯云推送或极光推送等更加成熟的第三方消息推送平台,这些平台都提供了相对完善的推送和数据统计服务,通过接口和SDK屏蔽了底层逻辑的实现,通过对iOS消息推送的实现过程的了解也能够帮助我们更好的使用这些平台。

由于时间的关系,自己的研究并不深入,如有疏漏和错误,欢迎留言指正交流~

8、扩展阅读

  1. 苹果官方技术文档,https://developer.apple.com/documentation/usernotifications

  2. 史上最全iOS Push技术详解,https://cloud.tencent.com/developer/article/1198303

  3. iOS远程推送-APNs详解,https://juejin.im/post/6844903893592178696

  4. iOS静默推送进阶知识,https://www.jianshu.com/p/c211bd295d58

  5. iOS10自定义通知UI,https://www.jianshu.com/p/85ac47bdf387

  6. 信鸽文档-推送服务介绍,https://xg.qq.com/docs/ios_access/ios_push_introduction.html

  7. 浅谈iOS和Android后台实时消息推送的原理和区别,https://cloud.tencent.com/developer/article/1150967

  8. 浅谈基于HTTP2推送消息到APNs,http://www.linkedkeeper.com/167.html

  9. PHP基于socket的ios 推送的实现,https://www.fzb.me/2015-9-7-sockect-implement-for-apns.html

  10. 如何构建一套高可用的移动消息推送平台?,https://www.infoq.cn/article/HA-mobile-message-push-platform

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/64408.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信里有人每天早上发的早报新闻是从哪里获取的?

微信里有人每天早上发的早报新闻是从哪里获取的? 是专门有人做这种新闻搜集嘛?还是有什么渠道可以获取这样的早报新闻资讯呢? 其实,他们都是从一些大的新闻网站上挑选每日重要的新闻资讯,一般是12条,整理好,然后第二天早上发到群里或朋友圈中,使人们利用碎片化时间,很…

iOS新闻客户端开发教程7-新闻列表

在上教程中&#xff0c;我们介绍了二级导航栏的开发&#xff0c;今天我们来讲解iOS开发中非常常用和重要的组件&#xff1a;“列表”&#xff0c;即UITableView。本节课程将会介绍横向滚动列表和竖向滚动列表&#xff0c;分别来实现二级栏目滑动切换和新闻内容列表的功能。 UIT…

(Android+IOS)正在做一个新闻App,做的差不多了,听听大家的建议 (图)

(AndroidIOS)正在做一个新闻App,做的差不多了,听听大家的建议! 新闻采集器做好了,前端展示APP界面感觉还不是很好,还需要改进改进,希望发布(Android和IOS版本)前听听大家的建议! 新闻采集器做好了,前端展示APP界面感觉还不是很好,还需要改进改进,希望发布前听听大家的建议!

iOS 摸鱼周报 #83 | ChatGPT 的风又起来了

本期概要 本期话题&#xff1a;各大搜索引擎开始接入类 ChatGPT 功能本周学习&#xff1a;Python 中的匿名函数与闭包内容推荐&#xff1a;iOS 越狱检测、获取虚拟内存状态、使用 KeyChain 进行持久化等内容摸一下鱼&#xff1a;Stable Diffusion 功能尝鲜&#xff1b;关于技术…

编程和数学是什么关系?编程学习为什么会这么火呢?

近两年&#xff0c;编程学习成为了一个热门话题&#xff0c;其热度不亚于之前的奥数&#xff0c;为什么突然会有这么多人想要学编程&#xff0c;其中不限于互联网从业者&#xff0c;而更多是中小学学生&#xff0c;那么&#xff0c;今天悉之君就带大家一探究竟。 什么是编程&a…

计算机编程数学英语不好怎么办,英语和数学不好的人是不是学不会编程?

原标题&#xff1a;英语和数学不好的人是不是学不会编程&#xff1f; 收到很多咨询的留言&#xff0c;学生总是会问&#xff1a; “我成绩不好&#xff0c;能学好编程吗&#xff1f;” “我数学不好是不是代表逻辑思维不行&#xff1f;” “我英语都不及格&#xff0c;那么多单…

用编程学数学:让数学不枯燥,让编程不神秘!

许多人总爱问&#xff1a;编程那么难&#xff0c;能学好吗&#xff0c;或者学编程到底能干啥&#xff1f;等等诸如此类的问题。 但是&#xff0c;其实编程并没有大家想象中的那么难&#xff0c;编程要培养的也只是一项基础的思维逻辑。 编程所需要的很多能力和数学是相通的。…

为什么人人都学Python,讲清楚了,只要初中数学基础你就可以编程

Python越来越热&#xff0c;随着大数据和人工智能的兴起&#xff0c;Python将会继续热。 2022年7月&#xff0c;Python依旧占据Tiobe榜首位置&#xff0c;属实是“霸榜”编程语言。 不仅如此&#xff0c;Python在其他排行榜中&#xff0c;也是常年占据榜首或者前列位置&#…

编程用不到微积分,可我们为什么还要学数学?

数学对于一位程序员到底意味着什么呢&#xff1f; 先跟你分享一个关于 Google 面试题的故事&#xff1a; 2004 年的某天&#xff0c;硅谷的交通动脉 101 公路上突然出现了一块巨大的广告牌&#xff0c;上面是一道数学题&#xff1a;{e 的连续数字中最先出现的 10 位质数}.com。…

数学不好能学好编程吗?你来告诉你

诚然&#xff0c;编程离不开数学&#xff0c;或者可以说数学是任何科学的基础&#xff0c;但这不意味着在开始学习编程之前必须对数学很在行或者数学分数很高&#xff0c;那我们今天就来讨论下数学与编程的关系。 首先&#xff0c;如果想要能够进行基本的编程&#xff0c;哪些数…

分享:作为程序员,为什么你应该学好数学?

你好&#xff0c;我是黄申&#xff0c;目前在 LinkedIn 从事数据科学的工作&#xff0c;主要负责全球领英的搜索引擎优化&#xff0c;算法和数据架构的搭建。 2006 年&#xff0c;我博士毕业于上海交通大学计算机科学与工程专业&#xff0c;在接下来十余年时间里&#xff0c;我…

没有数学基础可以学编程吗?

一、为什么学编程 这里我并不是问大家&#xff0c;是因为兴趣啊还是就业学编程。 而是&#xff0c;我想要学Python为了量化交易&#xff0c;或者我要处理表格。我想要学Java我就想自己建站。是否有这种非常明确的目标&#xff0c;有目标才能明确学习路线。 如果在这里&#…

只有1%的人才知道的ChatGPT写作技巧

随意的提示只能产出糟糕的输出&#xff0c;要想让ChatGPT输出高质量内容&#xff0c;需要一些技巧。原文: Stop doing this on ChatGPT and get ahead of the 99% of its users[1] 如果你尝试过用ChatGPT写作&#xff0c;也许会对AI生成的内容感到沮丧&#xff0c;也许认为Chat…

边锋网络入选2019中国互联网企业100强榜单

【TechWeb】8月14日消息&#xff0c;中国互联网协会、工业和信息化部网络安全产业发展中心&#xff08;工业和信息化部信息中心&#xff09;今日发布了2019年中国互联网企业100强榜单&#xff0c;杭州边锋网络技术有限公司&#xff08;下文简称边锋网络&#xff09;入选。 边锋…

【关于2022年卡塔尔世界杯】

2022卡塔尔世界杯最全面的看点和分析,相信一定有你感兴趣的一点,相信一定会有你感兴趣的,推荐点赞收藏~~ 2022年世界杯比以往任何时候都晚,因为卡塔尔太热了…… 然而,四年一度的世界杯终于……来了 今年的世界杯,你最期待什么? 你认为谁会成为今年的冠军? 和小文一…

数据趣事:豪掷2200亿美元举办的世界杯有多精彩!世界杯趣事你知道哪些

2022卡塔尔世界杯正如火如荼的进行着&#xff0c;此次的卡塔尔世界杯也是中东和阿拉伯地区首次举办&#xff0c;为此卡塔尔更是豪掷2200亿美元&#xff0c;远超历届主办国。 本届世界杯共有32支来自不同国家的队伍&#xff0c;他们都有一个共同的奋斗目标——捧起大力神杯&…

中国20强(上市)游戏公司2022年财报分析:营收结构优化,市场竞争进入白热化

易观&#xff1a;受全球经济增速下行的消极影响&#xff0c;2022年国内外游戏市场规模普遍下滑。但中国游戏公司凭借处于全球领先水平的研发、发行和运营的能力与经验&#xff0c;继续加大海外市场布局&#xff0c;推动高质量发展迈上新台阶。 风险提示&#xff1a;本文内容仅代…

盛大边锋总裁许朝军离职创业正组建团队

2月15日消息&#xff0c;盛大边锋总裁许朝军今日向腾讯科技证实自己已离职创业&#xff0c;创业选择的方向是移动互联网&#xff0c;目前正在组建团队开发产品。许朝军还在腾讯微博中感叹&#xff1a;“成功是偶然&#xff0c;失败是必然。但是自己要开始惊险一跳!” 据了解&am…

摊牌了,.NET开发者,准备赋能未来

hi&#xff0c;这里是桑小榆。一名.net开发&#xff0c;从19年毕业至今一直从事相关技术已近4年。 发展至今&#xff0c;很有必要分享分享我的经历以及对于.net开发的看法和见解。 篇幅有些长&#xff0c;无论你是学生&#xff0c;职业人&#xff0c;.NET开发者还是其他语言开发…

边锋游戏:用精细化运营使游戏流失率降低 26% ,只是数据驱动价值的冰山一角...

如今&#xff0c;我国游戏行业市场受限于监管政策&#xff0c;增速放缓。同时&#xff0c;随着市场流量的僵化&#xff0c;同质化严重&#xff0c;竞争激烈程度只增不减&#xff0c;粗放的推广方式也已成历史&#xff0c;数据驱动精细化运营逐渐成为企业焦点。 已知的众多成功案…