Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

技术笔记(一) #55

Open
ShannonChenCHN opened this issue Apr 30, 2017 · 52 comments
Open

技术笔记(一) #55

ShannonChenCHN opened this issue Apr 30, 2017 · 52 comments

Comments

@ShannonChenCHN
Copy link
Owner

ShannonChenCHN commented Apr 30, 2017

前言

记录平时遇到的问题和收获

技术笔记系列

@ShannonChenCHN ShannonChenCHN changed the title 【】官方文档阅读 【Task】官方文档阅读 Apr 30, 2017
@ShannonChenCHN ShannonChenCHN changed the title 【Task】官方文档阅读 【任务】官方文档阅读 Apr 30, 2017
@ShannonChenCHN ShannonChenCHN changed the title 【任务】官方文档阅读 【记录】笔记 May 10, 2017
@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 10, 2017

05/09/2017 周二

笔记:

  1. 使用 WKUserScript 时 Xcode 报错:ndefined symbols for architecture arm64:"_OBJC_CLASS_$_WKUserScript"
    解决办法:导入 WebKit.framework,要注意的是,在 YHMainApp 中导入才有效,在 YHOUSE 中导入不起作用。
    参考:http://stackoverflow.com/a/28763238

  2. h5 中使用 window.location = "yhouse://login"; 的方式来调起 openURL:的方式只对 UIWebView 起作用,在 WKWebView 中不起作用。
    根据腾讯工程师的调研: “WKWebView 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 WKWebView 上直接使用 NSURLProtocol 无法拦截请求。”
    初步推断 WKWebView 不能调起 openURL: ,也是因此导致的。

    其他参考:

h5 中的 JS 代码:

if ($rootScope.inAPP) {
                if (typeof $rootScope.currentUser === 'undefined') {
                    btnStatus = "buy"
                    window.location = "yhouse://login";
                } else {
                    window.location = 'yhouse://vip-payment?redirect=my-member';
                }


} else {
  $location.url('/vip-payment');
}
  1. 如何提高程序的健壮性?
  • TDD
  • 边界条件
  • 出错时的处理
  • 保护措施(数组越界、插入空值等)
  • 日志上报
  1. 担心出 bug,怎么破?
  • 如何减低错误率/bug
  • 如何减低崩溃率
  • 出现问题时怎么查找、追踪
  • 补救措施

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 10, 2017

05/10/2017 周三

1. UINavigationBar 与自定义 navigation bar

参考:

2. webView 预加载

方式一: 参考 PreLoad UIWebView on a not yet displayed UIViewController 和 在第一个 controller 中初始化第二个 controller 后,调用 [secondViewController.view setNeedsLayout] 来触发第二个 controller 的 -viewDidLoad 方法,以达到提前加载 UIWebView 的效果,但是实际效果不太理想,而且此时因为 controller 的生命周期是经过“人为干扰”的,所以还有可能导致一些难以预料的 bug。

携程移动端性能优化 这篇文章中,也提到了 “通过 setNeedsLayout 机制提前初始化下一个 UIWebView,将 HTML 文件初始化,然后捕获点击事件去动态刷新数据 ” 类似的方式。

方式二: 参考 美团大众点评 Hybrid 化建设 文中的思路,我采用了最简单的方式——借助 iOS 系统自身的 webView 缓存机制,提前创建一个只有 1 像素的 webView 进行预加载要请求的 URL。

方式三:
上面提到的两种预加载方式,其实都是基于 webView 自身的缓存,区别仅仅在于怎么去让 webView 把要请求的页面提前加载起来。 在 携程的优化方案 中和美团的优化方案中都提到了一种更靠近 web 方向的预加载方式——静态资源预加载。除了携程和美团之外,豆瓣Rexxar 框架也采用了类似的预加载方案和缓存机制。

携程: 虽然携程用的也是通过提前初始化下一个 UIWebView 来实现预加载,但是文章中所说的预加载不是直接加载这个页面的 URL,而是将 JS、CSS、字体、图片等资源提前加载出来。

影响页面加载速度的因素非常多,我们在对 WebView 加载一个网页的过程进行调试发现,每次加载的过程中都会有较多的网络请求。除了 Web 页面自身的 URL 请求,还会有 Web 页面外部引用的 JS、CSS、字体、图片等都是个独立的 HTTP 请求。这些请求都是串行的,加上浏览器的解析、渲染时间就会导致 WebView 整体加载时间变长,消耗的流量也对应地增多。所以为了加快 WebView 的整体加载时间,我们使用了预加载策略,将 JS、CSS、字体、图片等资源提前加载出来。
iOS 通过 setNeedsLayout 机制提前初始化下一个 UIwebView,将 HTML 文件初始化,然后捕获点击事件去动态刷新数据。

美团: 美团的做法跟携程类似,也就是不依赖于系统,而是自己构建和管理缓存,但是美团是预加载本地的静态资源包(实际上携程也有采用离线组件包的优化措施,不知道预加载时有没有用上),而且这个资源包是可以增量更新的。除了方案实现,文章中还提到了预加载静态资源的优势和效果。

每当这个 WebView 发起资源请求的时候,我们会拦截到这些资源的请求,去本地检查一下我们的这些静态资源本地离线包有没有。针对本地的缓存文件我们有些策略能够及时的去更新它。为了安全考虑的话我们也做了一些预下载和安全包的一些加密的工作。

方案三中的预加载具体怎么实现呢?
注册自定义 NSURLProtocol,拦截 webView 中的请求,将 html 请求的 css 和 js 的 URL 替换成本地的。
具体可以参考:

webView 的加载优化,除了 native 端的优化,前端也有些工作要做的,淘宝前端团队在这篇文章 中从页面可见时间与异步加载两个方面进行了探讨。其中还谈到了 didFinishLoad 到底什么时候触发 这个问题,这个问题一直是个谜团,不妨参考一下他们的验证。

那它到底是什么时候触发呢? iOS 官方文档 是 Sent after a web view finishes loading a frame。 结合收集的用户请求和测试,didFinishLoad 是在连续发起的请求结束之后触发,监听一段时间内无请求则触发。

3. API 数据结构的定义

1fa4d4f5-c44f-41db-baa1-df2cf01ef035

对于如上图所示的图文排版样式,我们讨论了两种数据结构:
方式一:

[
   {
    "content" : "http://image01.png",
     "type" : 1
    },
   {
    "content" : "这家店不错!",
     "type" : 0
    },
   {
    "content" : "http://image02.png",
     "type" : 1
    },
   {
    "content" : "不妨试试看?",
     "type" : 0
    }
]

方式二:

[
   {
    "image" : "http://image01.png",
     "text" : ""
    },
   {
    "image" : "",
     "text" : "这家店不错!"
    },
  {
    "image" : "http://image02.png",
     "text" : ""
    },
   {
    "image" : "",
     "text" : "不妨试试看?"
    }
]

3.1 关于字段的定义

方式一的优点在于:

  • 可扩展性好,通用性强,每个元素只包含一个 type 和一个通用字段

缺点在于:

  • content 字段属于泛型,同时表示多种“类型”数据,语义模糊,不方便取值

方式二将一图一文打包成一个对象,其优点在于:

  • 数组中每个元素(对象)内的字段名含义明了,不管是图、还是文,都是一个包含 text 和 image 两个字段的对象,如果是图,text 为空,image 不为空,如果是文,同理。这样的话,在客户端使用取值时,更方便对号入座。

缺点在于:

  • 不具备通用性,有冗余信息

综合来看,我们的业务场景基本上只存在图片和文字两种类型,数据结构相对简单,所以 type 的作用并不大,所以我推荐采用第二种方式。

3.2 共同讨论,信息同步
如果有问题,与服务端或者 h5 的同学讨论时,最好让安卓同学一起参与,减少沟通成本,提高沟通效率,保证信息及时同步。要不然你看了一下是这样,然后商量这样改了,但是后来安卓同学又觉得改成那样比较好,这样就会造成反反复复,效率低。

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 10, 2017

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 11, 2017

05/12/2017 周五

1. WKWebView 的使用和坑点

参考:

2. Cocoapods

问题:运行 KINWebBrowserExample 时出现 build 错误: ibrary not found for -lPods
解决:参考了@noelhunter 提供的这个 Stackoverflow 上的问题 library not found for -lPods,多次尝试后,依然无果。后来干脆重新创建一个新的 project,把原来的一些文件复制过去,然后再安装 pods,然后就 OK 了。
问题虽然解决了,但是原因依然没找到。

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 15, 2017

05/15/2017 周一

1.NSError

  • 如何通过 NSError 获取错误信息
  • 如何创建 NSError 对象
  • 如何正确使用 NSError 追踪错误(通过 Domain,code 构建自己的 error 系统)
  • NSError 与内存管理

延伸阅读:

2. canOpenURL:

        if ([[UIApplication sharedApplication] canOpenURL:request.URL]) {
            [[UIApplication sharedApplication] openURL:request.URL];
        }

使用上面的代码打开 URL yhouse://login 时报错 :

 -canOpenURL: failed for URL: "yhouse://login" - error: "This app is not allowed to query for scheme yhouse"

3.WKWebView

  • 基本使用
  • cookie 同步
  • 缓存
  • UIWebView 与 WKWebView 的封装

4.WKWebView 的一些问题:

  • customUserAgent 属性
  • title 属性
  • 改变 WKWebView 的 scrollViewcontentSize 属性后,系统会在下一次帧率刷新的时候,再给你改回原来的值
  • HTTPS 请求
  • 加载POST请求的时候,会丢失HTTPBody
  • WKWebView 中原生和 JavaScript 之间的交互
    • WKUserContentControllerWKScriptMessageHandler 协议
    • -evaluateJavaScript:completionHandler:
  • NSURLProtocol 与 缓存
  • 暂时不能在 xib 和 storyboard 中直接添加 WKWebView

延伸阅读:

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 16, 2017

05/16/2017 周二

1.获取 iOS 系统版本号

2.读取真机上 APP 中的沙盒数据

打开 Xcode -> 菜单 -> Window -> Devices -> 从左侧 DEVICES 列表选择要查看的真机 -> 找到 Installed Apps -> 选中要读取的 APP -> 点击下方的⚙按钮 -> 选择 Download Container -> 下载数据到桌面 -> 右击 -> 显示包内容

参考:

3.文件操作与 NSFileManager

4.基于 UIWebView 和 WKWebView 的封装

5.webView 调试工具

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 17, 2017

05/17/2017 周三

一、笔记

1.使用 WKWebView 时遇到的两个问题

  • 退出登录后,但是常驻的 h5 页面显示的仍然是登录状态的页面(TODO: 原因?)
    • 直接 reload 不管用
    • 调用 loadRequest: 重新加载原来的 URL,正常情况下看似可以,但是如果上次登陆过,杀掉 APP 进程,重新打开,再退出登录,这个时候也不管用
    • 干掉原来的 webView,再重新新建一个 webView,问题得以解决
  • 登陆成功后,不能成功调用 JS 更新页面内容(TODO)

2.在 iOS 应用的 webView 中、Safari 中调试 JavaScript

3.崩溃问题的预防和补救

4.自定义 Xcode 文件模板

延伸阅读:

二、bug 和其他问题

1.生成商户快照,出现图片为空的情况

  • 原因:服务端/h5 未提供亮点详情的图片数据
  • 回顾:开发过程中未及时与 PM 等进行沟通

2.创建商户快照的 view 时,出现崩溃现象

  • 原因:会员权益详情图片为空时, image 为 nil,所以获取到的 image.width 为 0,除以一个值 0 的数之后得到的结果是 NaN(Not a number),这个 NaN 值是不能被当做数值使用的,否则会崩溃。
  • 回顾:
    • 自测
    • 边界条件不全面(图片为空)
    • 崩溃保护(除以 0)
    • 再 Code Review 一遍
  • 延伸阅读:

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 18, 2017

05/18/2017 周四

1.测试(TDD、单元测试)#24

  • 如何检测程序能在各种条件下正常运行(test case)
  • 重构后如何确保改动不会造成新的 bug

2.自定义 tabBar、tabBarController

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 19, 2017

05/19/2017 周五

1.为什么拼两个相同的参数会加载失败

https://m.yhouse.com/share-tab-app?id=1689 能打开
https://m.yhouse.com/share-tab-app?id=1689&id=1689 不能打开

2.URL 与 Restful API

见笔记 NSURL and Restul API

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 22, 2017

05/22/2017 周一

1.KVO

2.Universal Links and Custom URI Scheme

3. Cordova

4. WKWebView 相关问题

  • 什么是 WKNavigation?一个 WKNavigation 对象代表什么?
  • WKNavigationAction 是用来干什么的?
  • WKNavigationResponse 是用来干什么的?
  • authentication challenge 是什么个流程?
  • 什么时候才会出现 “web view’s web content process is terminated”?
  • 一个网页的加载的完整过程?

5. JavaScript 中的 Navigator 对象是什么?

  • Navigator 对象包含有关浏览器的信息:userAgent、appVersion、appName 等等。
  • Navigator 对象包含的属性描述了正在使用的浏览器,我们可以使用这些属性进行平台专用的配置,比如,UserAgent
  • 虽然这个对象的名称显而易见的是 Netscape 的 Navigator 浏览器,但其他实现了 JavaScript 的浏览器也支持这个对象
  • Navigator 对象的实例是唯一的,可以用 Window 对象的 navigator 属性来引用它

6. KILabel

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 23, 2017

05/23/2017 周二

1.如何查看 GitHub 项目用了哪些语言

打开要查看的 repository -> command+F -> 输入 % -> 结果如下图
1b7a8ab9-2e20-4ca0-b120-7159447f921e

2.GitHub 使用 Electron 重写桌面客户端

原文

  • Web 技术实现跨平台(Electron和TypeScript)
  • 重新构建 GitHub 桌面客户端的主要原因是为了减少支持多平台开发所耗费的成本
  • Web并非完美的平台,不过原生应用也不是。使用Electron重写原生应用只不过是在不同的权衡点之间进行了交换
  • 类似的 web 技术:用来开发桌面客户端的 pojala/electrino 框架

3.GraphQL VS. REST

4.消除 Xcode 中的警告

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// write your code here
#pragma clang diagnostic pop

警告语句对应的 warning,见 Which Clang Warning Is Generating This Message?

5.Top 10 WWDC 2016 Videos

6.WKWebView 调研笔记

  • API 及具体使用

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 24, 2017

05/24/2017 周三

1.dfmuir/KINWebBrowser 的实现

2. 二维码

3.Bug 反馈的处理

用户反馈了一个诡异的显示 bug,最终发现是因为用户在设置中开启了特殊功能
Setting->General->Accessibility->Button Shapes

d0450653-894d-48d4-ae4d-0e1bbd1e26fc
c1d2ebf7-d622-446b-ab1b-6c7aa1530824

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 27, 2017

05/27/2017 周六

1.IBInspectable / IBDesignable

IBInspectable / IBDesignable - NSHipster

2.边界条件的考虑

会员页在网络离线状态下的一些 bug 没有测到

3.UIDebuggingInformationOverlay

4.WebView 加载问题

-didFailLoadWithError:-webViewDidFinishLoad:方法不被调用的情况:

  • 离线状态下,第一次加载失败后,后面联网后,再 reload
  • 404 的情况下

延伸阅读:

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented May 31, 2017

05/31/2017 周三

1.保存图片到相册前开启权限

问题:

  • 如何获取权限开启状态
  • 如何引导用户去开启

参考:

2.回顾 NSTimer

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 1, 2017

06/01/2017 周四

1.h5 VS. 原生

2.React Native 与 Weex

3.NSSet

4.reload 和 loadRequest 的区别

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 2, 2017

06/02/2017 周五

1.为什么会出现登陆之后刷新不了的问题

  • 拦截请求出了问题
  • 在调用时没有看 函数名是否正确
  • debug web view 时 也没有注意是 cookie 的问题,还是 JavaScript 函数调用失败的问题

2. WKWebView 的 cookie 问题

  • 几种不同的插入 cookie 的方式
  • WKWebView 的 cookie 到底存在哪里
  • h5 是从哪里拿到 cookie 的

3. Web View 的 Error

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 4, 2017

06/04/2017 周日

1. Language tags in HTML and XML

ISO 标准中对语言和地区的定义,示例:

Code Language Subtags
en English language
mas Masai language
fr-CA French as used in Canada language+region
es-419 Spanish as used in Latin America language+region
zh-Hans Chinese written with Simplified script language+script

2.浏览器引擎的原理

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 5, 2017

06/05/2017 周一

  1. UserAgent

  2. URL encode

3.h5 和原生中的网络超时、无网络

  1. 由于在解析 Model 时类型声明不正确,导致 iPhone 5 上发生崩溃

  2. 将 URL 的 Query String 转成 key-value 的形式

@interface  NSString (URL)
@end
@implementation NSString (URL)

- (NSDictionary *)URLQueryKeyValues {
    if (self.length == 0) {
        return @{};
    }
    
    NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init];
    NSArray *urlComponents = [self componentsSeparatedByString:@"&"];
    for (NSString *keyValuePair in urlComponents) {
        NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
        NSString *key = [pairComponents firstObject];
        NSString *value = [pairComponents lastObject];
        
        [queryStringDictionary safeSetObject:[value stringDecode] forKey:key];
    }
    
    return [NSDictionary dictionaryWithDictionary:queryStringDictionary];
}

@end

6.前端开发查询 API 兼容性
caniuse.com

7.h5 (JavaScript)中如何判断当前网络状态?

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 6, 2017

06/06/2017 周二

1. __kindof 修饰符

2. NS_REQUIRES_SUPER

3. -conformsToProtocol:

A class is said to “conform to” a protocol if it adopts the protocol or inherits from another class that adopts it. Protocols are adopted by listing them within angle brackets after the interface declaration.

This method determines conformance solely on the basis of the formal declarations in header files, as illustrated above. It doesn’t check to see whether the methods declared in the protocol are actually implemented—that’s the programmer’s responsibility.

4. 如何实现一个类中定义的 delegate 方法被调用时,在其子类和其 delegate 对象中都能被通知到?

参考 casa 的 casatwy/RTNetworking

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 7, 2017

06/07/2017 周三

1.如何查看 UIWebView/WKWebView 中的请求

  • 使用 Safari 进行远程调试
  • NSURLProtocol 拦截
  • NSURLcache 拦截
  • 在 JavaScript 中监听 AJAX 请求,然后再通过 JavaScript Handler 与原生进行交互,调起 UIWebView 的 delegate 方法

参考:

2. Debug UIWebView 的几种方式

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 8, 2017

06/08/2017 周四

1.怎样才算是真正的登录成功(也就是可以 post “login success” 的 notification)?

  • 本地用户数据更新
  • 第三方服务登录(IM)
  • webView cookie 更新

2.UIWebView 与 NSURLCache

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 9, 2017

06/09/2017 周五

1. -reload 方法和 -loadRequest: 方法

2.UIApplication

3.搭建本地服务器

3.1 运行 Apache $ sudo apachectl start
3.2 退出 Apache $ sudo apachectl stop
3.3 把工程文件夹放到以下位置中 /Library/WebServer/Documents
3.4 在浏览器中访问:在地址栏中输入地址 http://localhost/工程文件夹名称/,回车。

注意: 不用后一定要记得退出。

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 12, 2017

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 13, 2017

06/11/2017 周二

1.localStorage

2.解决WKWebView加载POST请求无法发送参数问题

3. UIWebView 相关

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 27, 2017

06/27/2017 周二

1.[isMemberOfClassisKindOfClass 的区别](difference between isKindOfClass and isMemberOfClass)

  • isKindOfClass::判断该对象是不是一个指定类 ClassA 的实例或者一个指定类 ClassA 的子类 SubclassOfA 的实例。
  • isMemberOfClass::只用来判断该对象是不是一个指定类 ClassA 的实例,如果要想判断它是不是一个指定类 ClassA 的子类 SubclassOfA 的实例,用上面的方法👆。

2. Cookie 跨域问题

当前页面 URL 为:http://m-test.mycompany.com/user-center
在执行下面的 JavaScript 脚本写入 cookie 后,在通过 document.cookie 来查看 cookie 时,只能看到 USER_ID,而没有 _pk_ses.1.5e9e 的值。

document.cookie = '_pk_ses.1.5e9e=*; domain=m.mycompany.com; path=/'; 
document.cookie = 'USER_ID=6ECB64E0E0; domain=.mycompany.com; path=/';  

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 30, 2017

06/30/2017 周五

1. UIWebView 的 mediaPlaybackRequiresUserAction 属性

作用:是否允许 h5 中的视频自动播放
WKWebView:在 WKWebView 中有对应的 mediaPlaybackRequiresUserAction(iOS 8.0–9.0) 和 mediaTypesRequiringUserActionForPlayback(iOS 10.0+) 属性。
示例:可以尝试在你的 UIWebView 中加载如下的 HTML 代码测试一下效果。

<!DOCTYPE HTML>
<html>
<body>

<video controls="controls" autoplay="autoplay">
  <source src="http://www.w3school.com.cn/i/movie.ogg" type="video/ogg" />
  <source src="http://www.w3school.com.cn/i/movie.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>

</body>
</html>

2. UIWebView 的 allowsInlineMediaPlayback 属性

作用:是否允许 h5 中的视频行内播放,也就是不使用原生的全屏播放。
WKWebView:在 WKWebView 中有对应的 allowsInlineMediaPlayback 属性,文档中说明,在 iOS 10.0 之前的 <video> 标签必须要添加 webkit-playsinline 属性,才可以开启行内播放。
使用方式:如果要想实现行内播放,
① h5 中的 <video> 标签加入属性 webkit-playsinline,示例代码如下:

<!DOCTYPE HTML>
<html>
    <head>
        <title>视频播放测试</title>
        <meta charset="utf-8">
    </head>
    <body>
        <video controls="controls" webkit-playsinline>
            <source src="http://www.w3school.com.cn/i/movie.ogg" type="video/ogg" />
            <source src="http://www.w3school.com.cn/i/movie.mp4" type="video/mp4" />
            Your browser does not support the video tag.
        </video>
        
    </body>
</html>

② 然后设置 UIWebView 的该属性为 YES

 webview.allowsInlineMediaPlayback = YES;

3. UIWebView 的 scalesPageToFit 属性

作用:当网页宽度超出 WebView 容器范围时,通过设置该属性为 YES 让网页按比例进行自适应,同时允许用户通过手势缩放查看网页。
WKWebView:根据 StackOverflow 上的解决方案,我们可以通过调用 JavaScript 来给 head 中添加设置 content 宽度的 meta 标签。
示例代码如下:

  NSString *scalesPageToFitScriptString = @"\
            var meta = document.createElement('meta');\
            meta.setAttribute('name', 'viewport');\
            meta.setAttribute('content', 'width=device-width'); \
            document.getElementsByTagName('head')[0].appendChild(meta);";

  WKUserScript *scalesPageToFitScript = [[WKUserScript alloc] initWithSource:scalesPageToFitScriptString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
   [userContentController addUserScript:scalesPageToFitScript];

   WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
           configuration.userContentController = userContentController;
  _wkWebView = [[WKWebView alloc] initWithFrame:self.bounds configuration:configuration];

延伸阅读:

@ShannonChenCHN ShannonChenCHN changed the title 【记录】技术笔记 技术笔记 Jun 30, 2017
@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 4, 2017

07/04/2017 周二

1.弹窗的问题

2.通过代码从 storyboard 中初始化 View Controller

第一步:在 storyboard 中设置该 controller 的 Storyboard ID
第二步:使用以下方法进行初始化

[[UIStoryboard storyboardWithName:@"YourStoryBoardName" bundle:nil] instantiateViewControllerWithIdentifier:@"StoryboardIDOfYourController"];

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 6, 2017

07/06/2017 周四

1. C语言预处理命令之条件编译

#if
#ifdef
#elif
#endif

2.阴影

  • 阴影的实现
  • 如何实现既有阴影又有圆角?
  • 如何实现只有其中一条边有阴影
  • 没有内容和颜色时阴影是无法出现的

3.接口字段定义的问题

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 11, 2017

07/10/2017 周一

1.如何在 UIScrollView 上响应单指点击事件

2. SVN 工作流

@ShannonChenCHN
Copy link
Owner Author

07/11/2017 周二

1.重复文件导致 CocoaPods 报错

在 SVN 上提交新文件时,由于文件重复导致执行 pod install 时报错:

error....

原因:.xcodeproj 工程文件中只在 a 目录下有一个 xxx.h 和 一个 xxx.m 文件,但是在 finder 实际目录 b 中,也有一个 xxx.h 和 一个 xxx.m 文件,所以在 SVN 提交时,就提交了同样的文件,这样导致在我的本地工程目录中看上去正常,但是在同事那里就不正常,pod install 一执行就报错。

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 12, 2017

07/12/2017 周三

1. URL 中的特殊字符

问题:URL 中出现 “|” 符号导致 Encode 的问题,UIWebView 因此加载失败
解决:将 “|” 替换成 “%07” 就 OK 了

2. h5 页面中的 JS 代码错误所引发的问题

h5 页面中的 JS 代码错误,导致 reload UIWebView 时,加载完成的代理方法走了两遍,第一遍走的是 didFailLoad,第二次走的是 didFinishLoad

3. 如何复制一个 UIView 对象?

根据不同的情况,有两种实现方式:

  • 使用 NSKeyedArchiver 实现深拷贝
  • 截屏成一张图片

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 17, 2017

07/17/2017 周一

1.切换 API 环境的问题

背景:由于之前我们在开发时,切换 API 环境都需要手动修改 URL 地址,所以很不方便,易出错,而且还需要自己去记住。所以我想使用更简单的处理方式来解决——定义几个不同的环境对应的宏,然后进行条件编译。

问题:尽管我们开发团队没有问题,使用起来更方便,但是没想到给测试团队带来了一些问题,他们的持续集成脚本也需要修改,这一点我倒是没考虑到,还好我们的 leader 想起来了。👏

反思:对于这种问题,我以后应该如何避免呢?重新来过一遍还会再犯吗?

  • 一个人的力量是有限的,在团队里一定要发挥协同作用,做好信息同步:改了原有的东西,尤其是一些重要的地方,一定要让大家都知道,“大家”是谁?大家包括直属上级、同组开发人员、以及测试人员
  • 平时,我们需要去了解一下经常协作的部门/团队的工作,比如测试团队是怎样打包的,UED 部门是怎样切图的,这样才能不偏执、不盲目、不狭隘。

收获:

  • 考虑问题要全面
  • iOS 持续集成

2.图文帖九宫格图集高度计算的问题

背景:为了复用原来的代码,但是又不能直接拿来用,需要修改一下,所以我把原有的图文帖图集的代码重构了一下,改完后我大概测了一下,感觉没什么问题,没想到今天还是被发现了一个 bug。

问题:在一个计算高度的方法里面,方法中的参数名和方法里面定义的局部变量名重名了,结果导致最后的结果不对。

反思:

  • 重构不是改了原来的代码就不管了,改的时候要读懂原来的代码,改完后首先要 code review 一下,最后一定要测试之前的逻辑是否能跑通
  • 改过原来的代码后一定要做让其他人知道,要做记录,至少要让测试知道改了什么地方

收获:

  • 如何安全地修改原来的代码、以及做好重构工作?

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 19, 2017

07/18/2017 周二

1.UIView 截图的问题

具体讨论见 iOS 中生成图片的几种方式

采用方式一截取高德地图的 mapView 时,不能得到完整的内容,而采用方式二,可以得到完整的内容。

方式一:

- (UIImage *)sc_snapshotImage {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *snap = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snap;
}

方式二:

- (UIImage *)sc_snapshotImageAfterScreenUpdates:(BOOL)afterUpdates {
    if (![self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
        return [self sc_snapshotImage];
    }
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0);
    [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:afterUpdates];
    UIImage *snap = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snap;
}

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 19, 2017

07/19/2017 周三

1. SVN 版本控制的问题

如果在① 修改文件名,或者 ② 移动文件位置时没有将旧文件的状态提交,可能会导致冗余文件的产生。
也就是说,当你将文件结构由:

- root
  - dir_1
    - file_A
    - file_B
  - dir_2

改为

- root
  - dir_1
  - dir_2
    - file_A
    - file_B

我们需要在 SVN 中,对 dir_2 下的新文件 file_A file_B 进行 add 操作,同时需要对 dir_1 下的原文件 file_A file_B 进行 delete 操作,否则文件目录中会出现两份 file_A file_B 文件。

2.如何设置 UIButton 中的内容往右上方对齐?

[_closeButton setImage:@"close_icon" forState:UIControlStateNormal];
_closeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
_closeButton.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;

具体讨论见 如何调整 UIButton 中的元素(image 和 title)的布局

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 20, 2017

07/20/2017 周四

1.如何将 JavaScript 对象转成 JSON 字符串?

现在的浏览器的 JavaScript 库基本上都自带 JSON 对象,使用 JSON.stringify() 方法可以将一个JavaScript值转换为一个JSON字符串。

var j={"name":"binchen"};
JSON.stringify(j);  // '{"name":"binchen"}'

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 21, 2017

07/20/2017 周五

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 24, 2017

07/24/2017 周一

1.如何在 UINavigationController 中使用自定义 UINavigationBar?

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];

或者:

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController pushViewController:yourRootViewController animated:NO];

参考资料

2. URL 编解码

  • 编码:URL 中如果有汉字或其他特殊字符的时候,需要转化成UTF-8:
NSString * encodingString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  • 解码:如果获取到的 URL 是含有这样的字符串:%3A%2F%2F,此时需要我们进行UTF-8解码:
NSString *str = [model.album_name stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

3. 响应者链条:如何使得 view 中超出其 bounds 的 subview 能够响应点击事件?

@ShannonChenCHN
Copy link
Owner Author

07/25/2017 周二

1.CIContext bad access crash

https://stackoverflow.com/questions/27632618/cicontext-bad-access-crash

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 26, 2017

07/26/2017 周三

1.使用 drawInRect: 绘制 UIImage 时得到的图片不能像原图那样清晰

解决办法:使用 UIGraphicsBeginImageContextWithOptions 函数绘制图片,而不是UIGraphicsBeginImageContext 函数。

原因:因为 UIGraphicsBeginImageContext 函数绘制图片时是按照 scale factor 为 1 来处理的,而 UIGraphicsBeginImageContextWithOptions 函数是可以指定 scale factor 参数的。比如:

UIGraphicsBeginImageContextWithOptions(originalQRCodeImage.size, NO, [[UIScreen mainScreen] scale]);

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 29, 2017

07/29/2017 周五

1. Universal Links

详细笔记

@ShannonChenCHN
Copy link
Owner Author

07/29/2017 周六

1.如何将 JavaScript 中的 String 转为 Number?

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jul 31, 2017

07/31/2017 周一

1. 调试 WebViewJavaScriptBridge 遇到的问题

原代码:

             var YHApp = {};
             var handlerNames = new Array("share", "requestLocation", "last");
             
             for (var i in handlerNames) {
                 var handlerName = handlerNames[i];
                 
                 YHApp[handlerName] = function(data, callback) {
                        alert(handlerName); // 我希望这里打印的就是对应的 handlerName,但是每次都是 last
                 }
             }
            YHApp.share();

修改后的代码:

             var YHApp = {};
             var handlerNames = new Array("share", "requestLocation", "last");
             
             for (var i in handlerNames) {
                 var handlerName = handlerNames[i];
                 
                 YHApp[handlerName] = function (a) {
                     return function(data, callback) {
                        alert(a); // 这里打印的就是对应的 handlerName
                     }
                 }(handlerName);
             }
             YHApp.share();

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Aug 1, 2017

08/01/2017 周二

1. html网页显示html代码的方法?

1)<textarea>标签

2)<xmp> 标签

2. HTML 中的有序列表如何显示像 1.1、1.2 这样的序号?

<head>
...
<style>
ol { counter-reset: item }
li{ display: block }
li:before { content: counters(item, ".") " "; counter-increment: item }
</style>
...
</head>
<body>
...
<ol>
  <li>li element
    <ol>
      <li>sub li element</li>
      <li>sub li element</li>
      <li>sub li element</li>
    </ol>
  </li>
  <li>li element</li>
  <li>li element
    <ol>
      <li>sub li element</li>
      <li>sub li element</li>
      <li>sub li element</li>
    </ol>
  </li>
</ol>
...
</body>

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Aug 7, 2017

08/07/2017 周一

1. 自定义 collectionViewLayout 时出现警告

自定义 collectionViewLayout 时出现警告:

2017-08-07 19:32:23.134 StickyCollectionView[1290:47457] Logging only once for UICollectionViewFlowLayout cache mismatched frame
2017-08-07 19:32:23.135 StickyCollectionView[1290:47457] UICollectionViewFlowLayout has cached frame mismatch for index path <NSIndexPath: 0xc000000000400016> {length = 2, path = 0 - 2} - cached value: {{0, 689}, {375, 300}}; expected value: {{0, 650}, {375, 300}}
2017-08-07 19:32:23.136 StickyCollectionView[1290:47457] This is likely occurring because the flow layout subclass StickCollectionViewFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them

在 Stack Overflow 上看到这个问题 Warning: UICollectionViewFlowLayout has cached frame mismatch for index path 'abc' 的答案,说是在 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 方法中,通过 [super layoutAttributesForElementsInRect:rect]; 获取返回 attributes 时,要先 copy 一份,才能修改 attributes。

NSArray * original   = [super layoutAttributesForElementsInRect:rect];
NSArray * attributes = [[NSArray alloc] initWithArray:original copyItems:YES];

但实际上,我代码就是这么写的啊,找了半天才发现,原来是在 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect 方法中的另外一个地方,调用了 [self layoutAttributesForItemAtIndexPath:indexPath],然后直接修改了 attributes。这里也是需要 copy 后再修改的。

@ShannonChenCHN
Copy link
Owner Author

08/10/2017 周四

1. 数组越界问题

问题:bugly 后台上看到奔溃日志:

** -[__NSSingleObjectArrayI objectAtIndex:]: index 18446744073709551615 beyond bounds [0 .. 8]

通过分析发现 18446744073709551615 正好是 NSUIntegerMax 的的值,从而定位到这行代码:

UIImageView *imageView = imageViews[imageURLs.count - 1];

原因:
NSArray 的 objectAtIndex: 方法会把 index 参数转成 NSInteger 类型,所以当 imageURLs.count == 0 时,imageURLs.count - 1 的值(-1)也就被转成了 NSUIntegerMax。

参考:

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Aug 13, 2017

08/11/2017 周五

1.JS 闭包

  • 闭包可以理解为函数内部的函数
  • 除了获取全局变量, 闭包还可以获取闭包外面的非全局变量

参考:

2.JS 中 this 的用法

  • this是Javascript语言的一个关键字。
  • 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。
  • this 指的是,调用函数的那个对象。

参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant