我的GitHub
0%

其实集成个推的途中一直很坎坷,不知道到底他们的推送是个什么东西,透传,普通推送,什么情况才是正常的。这里写一下,没有代码,只是阐述正确情况下推送通道应该产生的效果。

ios的消息是分两部分的 一部分是走apn的通知栏消息 另一部分是走个推通道的透传消息 这两部分是服务端推送代码里面集成个推的后端同事会设定好的 分别是两个不同的方法 如果消息下发的时候 你客户端是在后台的(也就是客户端是离线)那么会收到apn的通知 透传消息就进离线了 只有你下次在线的时候 (也就是下次应用到前台的时候)才会下发下来 如果下发的时候应用是在前台的 那这样的话客户端就直接收到透传消息了

应用退到后台,或者杀进程,cid离线才会推送苹果apn,客户端才会收到apn通知栏提醒的,cid在线直接走个推通道,客户端收到个推透传消息,如果需要展示通知栏需要自己客户端定义处理解析个推透传消息

阅读全文 »

背景:公司的老项目,使用的是uiwebview,我也没用过,第一次做混合开发,所以想直接使用wkwebview去替换当前的uiwebview,毕竟性能上提升了4倍,还有很多乱七八糟的优化等,废话不多说。

###1.首先wkwebview有三个代理方法WKUIDelegate,WKScriptMessageHandler,WKNavigationDelegate

###2.wkwebview创建的时候可以写配置
有一个属性的集合,叫WKWebViewConfiguration
其中还有一些首选项的配置WKPreferences
例如我是这么创建的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//    wkwebview属性的集合
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
// webview一些首选项的配置
WKPreferences *preferences = [[WKPreferences alloc]init];
// 在没有用户交互的情况下,是否允许js打开窗口,macOS默认是yes,iOS默认是no
preferences.javaScriptCanOpenWindowsAutomatically = YES;
// webview的最小字体大小
// preferences.minimumFontSize = 40.0;

configuration.preferences = preferences;

self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];

###3.加载网页,本地加载和加载服务器网页
######1.加载本地网页
将html与css和js拖进项目,使用蓝色物理文件夹放入。
加载本地网页,使用

1
[self.webView loadFileURL:htmlPathUrl allowingReadAccessToURL:folderPathUrl];

其中htmlPathUrl代表html的路径
其中folderPathUrl代表存放js与css的路径
只有这样才能加载完整的网页,否则你可能加载不出css样式和js方法。
例如:

1
2
3
4
5
6
7
8
9
10
#pragma mark 加载url展示页面
//加载本地网页
-(void)loadUrl{
NSString *folderPath = [[NSBundle mainBundle] pathForResource:@"sdk" ofType:@""];
NSURL *folderPathUrl = [NSURL fileURLWithPath:folderPath];

NSString *htmlPath = [folderPath stringByAppendingString:@"/h5/index.html"];
NSURL *htmlPathUrl = [NSURL fileURLWithPath:htmlPath];
[self.webView loadFileURL:htmlPathUrl allowingReadAccessToURL:folderPathUrl];
}

当然,如果网页有ajax请求,注意跨域问题。试试jsonp。这里就提一下,略过。

######2.加载服务器网页
这就没什么好说的了。。直接加载就行。
例如:

阅读全文 »

其实就是用workspace把两个项目连起来
凉白开记录了,懒得写了。。
地址:http://www.jianshu.com/p/6c9b380cfe5c?utm_source=desktop&utm_medium=timeline
如果修改了文件位置或者啥的,报头文件找不到,去示例代码工程把

49F27CAE-95C4-4F32-80EC-543ADFD05B49.png
把sdk的link删掉再加上,重新运行就好了

//—————以下是凉白开总结的——————//

自从上次写完SDK并接入别人的项目中之后就一直没怎么管过这东西了,昨天一人突然问我调试SDK怎么弄,我说不能。(结果今天啪啪啪自己打脸了) 恩他解决了一个SDK不能调试的一个超级大问题,征得同意后我来记录一下,自己记录一下其次文后并附上他的简书地址。

1.新建workspace。

img

新建workspace

*2.新建文件夹,将*workspace,demo以及SDK**放入文件夹中。****

img

新建文件夹及放入

阅读全文 »

前人挖坑后人填。。。。
阐述一下背景:
 App更新迭代,需要跟后台做一个版本控制更新的开关。正常来说应该是将xcode的version与App Store的version对应,并通过该值判断开关是否打开。
 但是问题来了。。上一位iOS工程师一直是使用的build进行版本控制,本地xocde的version比App Store的version高出不少。
 先说一说Version。分为xcode的version和App Store的version。理论上应该两者一致。方便维护。

###xcode的version
可以通过

1
2
//  获得version号
NSString *versionCode =[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

获取,获取的是info.plist文件中的
335E8913-16AA-4B95-B4CB-63B50AEF28F6.png

###App Store的version
则是通过调取接口:
https://itunes.apple.com/lookup?id=你的app的id号;
调用方式:post
返回结果:

1
2
3
NSArray *array = responseObject[@"results"];
NSDictionary *dict = [array lastObject];
NSLog(@"当前版本为:%@", dict[@"version"]);

代码如下:

1
2
3
4
5
6
7
8
9
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager POST:@"https://itunes.apple.com/lookup?id=414478124" parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
NSArray *array = responseObject[@"results"];
NSDictionary *dict = [array lastObject];
NSLog(@"当前版本为:%@", dict[@"version"]);
} failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
NSLog @"请求失败";
}];
}

正常来说都是对外暴露的版本,xcode与App Store的版本理应一致。方便维护。

###build:
开发内部测试版本号,写多少都行,内部开发测试用的,写多少都行,最好是不要对外暴露。因为每次打包都需要往上+,所以用于版本控制效果不好。

阅读全文 »

#1.第三方App跳转
这是很常见的问题,第三方app跳转到本app去做一些业务操作。
但是这时候会发现,你跳进来,是黑色的,没有界面,没想到吧23333
其实这在前面说过,RN的想法很独特,是一个ctrl上放了无数个view,不停的更改view来渲染,而rn的首页view,则是在appdelegate的初始化方法里去添加的。跳转进来的处理方法里如果你不写,就不会有view添加进来。
所以这时候有两种方法来完成这个view的添加操作:
####A.在第三方跳转进来的方法里做视图加载操作,但是这会遇到一个问题。假使你使用了RN的启动页,你会发现每次跳转进来都会开启启动页。这对用户的体验明显是很不好的。
####B.自己创建一个UIViewCtrl,并作为根视图放进window中,在这个UIViewCtrl中做视图加载操作。这样跳转进来加载根视图的时候就会把这个RN的View给加载上去。而且根据生命周期场景复现的原则,不会再有加载第二次第三次的情况。

如图

Appdelegate.m

RootViewCtrl.m

另外,如果要跳到某一页面做某一操作,又不想重复跳,可以采用以下方式移除

第三方跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma mark --第三方跳转进入这里(新)
-(BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
// NSLog(@"url ====%@",url);
thirdViewController *vc = [thirdViewController sharedthirdViewController];
vc.url = url;

UIViewController *topViewCtrl = self.nav.topViewController;
if ([topViewCtrl isKindOfClass:[thirdViewController class]]) { //判断是否在最顶层
[vc thirdSign];
}else{
[self.nav pushViewController:vc animated:YES];
[vc thirdSign];
}
return YES;
}

#2.应用升级苹果商店跳转
这个就很简单了啊,直接link:app在苹果商店的地址就行,至于怎么找app在苹果商店的地址,谷歌一下或百度一下都有。
#3.启动屏设置
采用了RN的启动页,你需要把原生的启动页稍作更改。把这个勾去掉

把这里改一下

#3.按钮点击封装防止二次点击
是不是有时候控件响应较慢,点击两次还以为没反应,结果却运行两次操作,感觉比较烦人。
这里贴出一个大佬给的方式,其实是采用定时器操作,只要有点击操作,做出事件处理,并开启定时器,在定时器时间内屏蔽其他的点击操作。

阅读全文 »

git:https://github.com/react-community/react-navigation/issues/1493
来,下猛药:
首先找到文件:src/views/ScenesReducer.js
然后用下面的代码:
  let k = null;
  let v = null;
  staleScenes.forEach(scene => {
  let {key} = scene;
  k = key;
  v = scene;
  });
  newStaleScenes = k && v ? new Map([[k, v]]) : new Map();
  newStaleScenes.forEach(mergeScene);
替换:staleScenes.forEach(mergeScene);
保存编译,就只有返回某页的动画了

转自http://www.cnblogs.com/lc901221/p/7543094.html
我就是那个贴了帖子看不懂答案的傻逼,。。

另外:
transitionConfig : () => ({
transitionSpec: {
duration: 0,
timing: Animated.timing,
easing: Easing.step0,
},
}),
去掉动画效果

2018-2-23 更新
以上解决方案适用于1.0.0beta版
升级到React-navigation 1.1.1版本后可直接通过key去回退指定版本。
至于怎么取key=>可以在跳转时缓存页面对应key值,通过页面名去获取缓存的key值跳转。
注意源码中当页面层级index为0时(首页)不会back。这一块应该是有想法的,但在特定情况下会有bug,因为我们项目就遇到了–但我暂时无法用语言总结出出现的场景。

无星的RN学习之旅(一)-环境安装以及新建项目
无星的RN学习之旅(二)-RN与原生的通信
无星的RN学习之旅(三)-bridge is not set.
无星的RN学习之旅(四)——通信、持久化存储、xcode打包
无星的RN学习之旅(六)-第三方App跳转,苹果商店跳转,loading框

阅读全文 »

废话不多说,直接上问题:
1.如何拼接请求头?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//申明返回的结果是json类型

manager.responseSerializer = [AFJSONResponseSerializer serializer];

//申明请求的数据是json类型

manager.requestSerializer=[AFJSONRequestSerializer serializer];

// /如果报接受类型不一致请替换一致text/html或别的

[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[manager.requestSerializer setValue:value forHTTPHeaderField:key];
[manager.requestSerializer setValue: value forHTTPHeaderField:key];
[manager.requestSerializer setValue: value forHTTPHeaderField:key];


2.如何返回参数在调用的同一方法中?

block,参数带有block去实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__block NSString *longitude; // __block,静态变量
[self.manager POST:URL parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {

NSDictionary *locDictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableLeaves error:nil];
longitude = [locDictionary objectForKey:@"lon"]; // 纠正后经度

if (finishBlock)
{
finishBlock(longitude, nil);
}

} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@", error);
if (finishBlock)
{
finishBlock(nil, error);
}
}];
阅读全文 »

说说最近项目的一些感想吧。

###一、RN的创意
RN其实我觉得是一个很有创意的想法。不知道各位写RN项目的时候,有没有打开Xcode看过app的层级关系,我发现RN的这个想法,真的很有创意。
作为一名原生的开发,一直都是一个控制器上放一个View,然后在这个底层的View上添加UI控件,当需要一个新的视图的时候,创建一个新的视图控制器,再放新的View。
###重点来了!RN并不是这么做的

根视图.png
RN是将App创建的时候生成的根视图控制器,也就是底层的视图控制器,作为根本,然后通过JS文件写的视图,也就是View,不停的增加在这个rootViewCtrl上,进行覆盖替换。
###二、Text的区别
或许有的兄弟还没遇到这个坑,假如使用图片或者其他背景色作为背景,往上添加Text标签的时候,安卓默认为透明背景色,但是苹果默认为灰白色。因此,在写App的时候,需要在Text的样式添加backgroundColor为transparent

1
backgroundColor:'transparent',

###三、原生与RN的通信
以前对原生与RN的通信不太了解。现在有了一些想法。
1.callback的通信方式,是会返回一个callback,这个callback是可以保存的,也就是说这个返回结果可以保存再用的。
2.promise,这个就比较有趣了,形象的说,这是个“通道”,RN的方法中,放一个promise的参数,在原生的module中,可以先定义几个promise的回调,在不同的地方用。
举个例子,我在RN中写几个方法。分别是:调用A方法,调用B方法,调用C方法,调用D方法。都是带Promise的。

promise.png
(lz是iOS开发,iOS中可以先声明几个变量)在iOS的module中,可以先声明几个promise的回调。比如先声明

1
2
3
4
5
6
7
8
9
 RCTPromiseResolveBlock resolveA
RCTPromiseResolveBlock resolveB
RCTPromiseResolveBlock resolveC
RCTPromiseResolveBlock resolveD

RCTPromiseRejectBlock rejectA
RCTPromiseRejectBlock rejectB
RCTPromiseRejectBlock rejectC
RCTPromiseRejectBlock rejectD

这四个回调的函数可以先声明,为什么会叫他通道,原因就是它其实可以这么理解,你将本来理解可能混乱的东西专一化,定义一个A方法成功的回调resolveA和一个A方法失败的回调rejectA,这两个回调只用于A方法。这么理解起来是不是清晰多了。因此可以理解为他是一个原生回调给RN的通信通道。
3.原生直接发消息,通过

1
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];

这种方式注意了,请使用单例。

阅读全文 »

报错信息:bridge is not set. This is probably because you’ve “
“explicitly synthesized the bridge in %@, even though it’s inherited “
“from RCTEventEmitter.

踩坑了。。。
我相信肯定有兄弟需要从原生的OC向RN主动发起事件。而且这种场景也很多,比如集成第三方的服务,通过代理回调获取结果发送给RN等等等等。。。
我将我遇到的坑总结一下吧。
先看下官网怎么写的

官网介绍1

官网介绍2.png
1.你是不是像官网一样,先写一个继承自RCTEventEmitter的对象,什么都不写,看看能不能运行?
答案是:并不能运行=。=你必须写实现,也就是以下这个方法

1
2
3
4
5
//.m文件
- (NSArray<NSString *> *)supportedEvents
{
return @[@"EventReminder"];
}

2.你在使用发送文件,也就是

1
[self sendEventWithName:@"消息名" body:参数];

运行这个方法的时候有没有崩溃啊=。=
崩溃的提示是不是

1
2
3
bridge is not set. This is probably because you've "
"explicitly synthesized the bridge in %@, even though it's inherited "
"from RCTEventEmitter.

其实就是bridage为空。
这个时候你查这个报错提示是不是发现有人说在Appdelegate.m里把rootView的bridage赋给这个你创建的对象,然后你试了一下确实发出去了开始哈皮=。=

阅读全文 »

有时候用AFNetworking发送数据的时候会产生这种错误:

1
Request failed: unacceptable content-type: text/html

3259244-7ef2c6bad256fe3a.png.jpeg

这是因为text/html这个方式AF中没有。。这种时候有两种方式解决
1.直接修改AF的文件:
AFURLResponseSerialization.m文件中大约226行处,将

1
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

修改为:

1
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html",nil];

2.jpg
此时便可以正常发送消息了。
不过不是很建议直接修改,比如你要封装sdk总不能直接把别人写的第三方封进来吧,一方面别人集成容易产生冲突,第二你的sdk体积会大很多。这时候可以采用第二种方式
2.在你发送的时候每次都添加

3.jpg

1
2
3
4

manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"text/plain", nil];
阅读全文 »