Light's Blog

The best or nothing.

iOS知识小集-201902

| Comments

2019.02.15

Dart 语法基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
//  Impport library: import 'dart:xxx' / 'package:xxx'
//   Specifying a library prefix: import 'package:lib2/lib2.dart' as lib2;
//   Importing only part of a library: import 'package:lib1/lib1.dart' show/hide foo;
//   Lazily loading a library: import 'package:greetings/hello.dart' deferred as hello;

import 'dart:math';

void main() {
  testVariables();
  testBuiltInTypes();
  testFunctions();
  testOperators();
  testControlFlow();
  testExceptions();
  testClasses();
  testGenerics();
  testAsynchrony();
  test();
}

//   Variables
void testVariables() {
  print("Test Variables\n");

  // Constants
  final name = "Light";
  const age = 26;
  print("name: $name  age: $age");

  // Variables
  var weather = "clear";
  weather = "cloudy";
  print("weather: $weather");

  print("\n");
}

//   Built-in types
void testBuiltInTypes() {
  print("Test Built-in types\n");

  // Numbers
  var integer = 2;
  var pi = 3.1415926;
  var one = int.parse('1');
  assert(one == 1);
  var onePointOne = double.parse('1.1');
  assert(onePointOne == 1.1);
  var twoAsString = integer.toString();
  assert(twoAsString == '2');
  var piAsString = pi.toStringAsFixed(2);
  assert(piAsString == '3.14');
  assert((3 << 1) == 6);
  assert((3 >> 1) == 1);
  assert((3 | 4) == 7);

  // Strings
  var s1 = 'hello dart !';
  var s2 = "how are you";
  print('say hello: $s1');
  print('say grate: ${s2.toUpperCase() + ' TOM ?'}');
  var s3 = '''

  multy line string
  multy line string
  ''';
  print(s3);
  var s4 = 'In a raw string, not even \n gets special treatment';
  print('not raw string: $s4');
  var s5 = r'In a raw string, not even \n gets special treatment';
  print('raw string: $s5');

  // Booleans
  var fullName = '';
  assert(fullName.isEmpty);
  var hitPoints = 0;
  assert(hitPoints <=0);
  var unicorn;
  assert(unicorn == null);
  var iMeantToDoThis = 0 / 0;
  assert(iMeantToDoThis.isNaN);

  // Lists
  var list = [1, 2, 3];
  print('list: $list');
  print('list length: ${list.length}');

  // Maps
  var personInfo = {
    'name': 'Light',
    'age': '26',
    'sex': 'male'
  };
  print('map: $personInfo');
  personInfo['birthday'] = '1992.12.09';
  print('map: $personInfo');
  print('map length: ${personInfo.length}');

  // Runes
  var clapping = '\u{1f44f}';
  print('clapping: $clapping');
  print('clapping code units: ${clapping.codeUnits}');
  print('clapping runes: ${clapping.runes.toList()}');
  Runes input = new Runes('hello \u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print('runes: $input');
  print('string from runes: ${new String.fromCharCodes(input)}');

  // Symbols
  print(#radix);
  print(#bar);

  print("\n");
}

//   Functions
void testFunctions() {
  print("Test Functions\n");

  // Optional parameters
      // Optional named parameters {}
      // Optional positional parameters []
      // Default parameter values = compile time const

  // The main() function
      // a top-level function, which serves as the entrypoint to the app

  // Function as first-class objects
      // pass a function as a parameter to another function
      // assign a function to a variable

  // Anonymous functions
      // ([Type] param1[, ...]) { codeBlock}

  // Lexical scope
      // "follow the curly braces outwards" to see if a variable is in scope

  // Lexical closures
      // A closure is a function object that has access to variables in its lexical scope

  // Return values
      // All function return a value. default return value is null

  print("\n");
}

//   Operators
void testOperators() {
  print("Test Operators\n");

  print('5 / 2 = ${5 / 2}');
  print('5 ~/ 2 = ${5 ~/ 2}');
  print('5 % 2 = ${5 % 2}');
  // as is is!
  // ??=
  // ^ ~expr
  // expr1 ?? expr2
  // . ?. ..

  print("\n");
}

//   Control flow statements
void testControlFlow() {
  print("Test Control flow\n");

  // for loops
  var message = StringBuffer('Dart is fun');
  for (var i = 0; i < 5; i++) {
    message.write('!');
  }
  print(message);
  // for in
  for (var x in [1, 2, 3]) {
    print(x);
  }
  // forEach
  ['a', 'b', 'c'].forEach((char) => print(char));

  // switch
  var command = 'CLOSED';
  switch (command) {
    case 'CLOSED':
      print('Closed');
      continue open;
    open:
    case 'OPEN':
      print('Open');
      break;
    default:
      print('Default');
  }

  print("\n");
}

//   Exceptions
void testExceptions() {
  print("Test Exceptions\n");

  // throw
  // rethrow
  // try on catch finally

  print("\n");
}


class Point {
  // instantce variables
  num x, y;

  // static variables
  static var pointColor = Color.red;

  // convenient constructors
  Point(this.x, this.y);

  // named constructors with initializer list
  Point.origin()
    : x = 0,
          y = 0 {
    print('create point (0, 0)');
  }

  // redirecting constructors
  Point.alongXAxis(num x) : this(x, 0);

  // instance method
  distanceTo(Point p) => sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y));

  // getter
  get distanceToOrigin => distanceTo(Point.origin());

  // static method
  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final \_name;

  // Not in the interface, since this is a constructor.
  Person(this.\_name);

  // In the interface.
  String greet(String who) => 'Hello, $who. I am $\_name.';
}

// An implementation of the Person interface.
class Impostor implements Person {
  get \_name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

enum Color { red, green, blue }

//   Classes
void testClasses() {
  print("Test Classes\n");

  // Using constructors
  var p = Point.origin();

  // Using class members
  // Instance variables
  print('point: $p');
  print('distance between ${p.toString()} and (3, 4): ${p.distanceTo(Point(3, 4))}');

  // Getting an objects's type
  print('The type of 1 is ${1.runtimeType}');

  // Constructors
      // Constructors aren’t inherited
      // Invoking a non-default superclass constructor
      // initializer list
      // superclass's no-arg constructor
      // main class's no-arg constructor
      // Specify the superclass constructor after a colon (:), just before the constructor body.
      // Initializer list
      // Redirecting constructors
      // Factory constructors, have no access to this

  // Methods
  print('Point (5, 6) distance to origin: ${Point(5, 6).distanceToOrigin}');

  // Abstract classes
      // Abstract methods can only exist in abstract classes.
      // Abstract classes are useful for defining interfaces, often with some implementation.

  // Implicit interfaces
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));

  // Extends a class
      // extends super methods
      // override super methods
      // override operators
      // noSuchMethod()

  // Enumerated types
  print(Color.values);
  print(Color.red);
  print(Color.red.index);

  // Adding features to a class: mixins
      // Mixins are a way of reusing a class’s code in multiple class hierarchies

  // Class variables and methods
  print('Point color: ${Point.pointColor}');
  print('distance between (2, 6) and (3, 4): ${Point.distanceBetween(Point(2, 6), Point(3, 4))}');

  print("\n");
}

//   Generics
void testGenerics() {
  print("Test Generics\n");

  // Using collection literals
  var names = <String>['Seth', 'Kathy', 'Lars'];
  var pages = <String, String>{
    'index.html': 'Homepage',
    'robots.txt': 'Hints for web robots',
    'humans.txt': 'We are people, not machines'
  };
  print(names);
  print(pages);
  print(names.runtimeType);

  // Restricting the parameterized type
  // Using generic methods

  print("\n");
}

//   Asynchrony support
void testAsynchrony() {
  print("Test Asynchrony\n");

  // Handling Futures
  // To use await, code must be in an async function—a function marked as async

  // Declaring async functions
  // An async function is a function whose body is marked with the async modifier

  // Handling Streams
  //

  print("\n");
}



//
void test() {
  print("Test \n");

  print("\n");
}

iOS知识小集-201901

| Comments

2019.01.02

Swift 进阶

Swift 语言特性
- Swift 既是高级语言,又是低级语言。既可以使用 mapreduce 这样的方法,编写 高阶函数,又可以直接编译出原生的二进制可执行文件,具有和 C语言 媲美的性能。 (如何理解第二点?)
- Swift 是多范式语言。既可以 面向对象编程 ,又可以 函数式编程
- Swift 鼓励自下而上编程。可以自己构建组件。
- Swift 代码紧凑、精确,同时保持清晰。
- Swift 在实践中是相对安全的。(如何检查变量在被使用前是赋值的?)
- Swift 在不断的发展中。

专业术语
- 值语义:
- 值类型:具有值语义的类型为值类型,structenum 都是值类型。
- 引用类型:
- 浅拷贝:
- 深拷贝:
- 闭包:持有外部变量的函数成为 闭包(closure)
- 高阶函数:
- 静态派发:
- 动态派发:

Atom 快捷键

打开快捷键列表:cmd + shift + P;
模糊搜索标题:cmd + P;

adb 取 Android 系统日志

  1. brew cask install android-platform-tools
  2. adb bugreport

字节跳动组织核心

技术、用户增长、商业化。
留存、拉新、变现。

2019.01.03

Swift 进阶

使用函数将行为参数化,复用模板代码,调用者提供变换函数。
将重复的代码抽象为函数,将函数行为抽象为模板,作为扩展重复使用。
表示声明 T 为某种类型的占位符。

内建集合类型
- 标准库中的集合类型都具有值语义,且都是用了“写时复制”技术;

数组变形
- 尽量避免直接使用索引值访问数组元素;
- sort:
- map:对数组中每个元素进行变换,返回新数组;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Array {
  func map<T>(_ transform: (Element) -> T) -> [T] {
    var result: [T] = []
    result.reserveCapacity(count)
    for x in self {
      result.append(transform(x))
    }
    return result
  }
}

[1, 2, 3].map{ x in
  return x + 1
}

[1, 2, 3].map{$0 + 1}
  • filter:筛选出符合条件的元素,返回新数组;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension Array {
  func filter(_ predicate:(Element) -> Bool) -> [Element] {
    var result: [Element] = []
    for x in self where predicate(x) {
      result.append(x)
    }
    return result
  }
}

[1, 2, 3].filter{ x in
  return x > 0
}

[1, 2, 3].filter{$0 > 1}
  • reduce:合并数组元素,返回合并值;
1
2
3
4
5
6
7
8
9
10
11
12
extension Array {
  func reduce<T>(_ initialValue: T, _ transform: (T, Element)-> T) - > T {
    var value = initialValue
    for x in Self {
      value = transform(value, x)
    }
    return value
  }
}

[1, 2, 3].reduce(0){value, x in value + x}
[1, 2, 3].reduce(0, +)
  • acummulate:迭代操作元素,返回新数组;
1
2
3
4
5
6
7
8
9
10
11
extension Array {
  func accumulate<Result>(_ initialResult: Result, _ nextParitialResult:(Result, Element) -> Result) -> [Result]{
    var running = initialResult
    return map{ next in
      running = nextParitialResult(running, next)
      return running
    }
  }
}

[1, 2, 3].accumulate(0, +)
  • all:判断是否所有元素都符合条件;
1
2
3
4
5
6
7
extension Array {
  func all(_ predicate: (Element) -> Bool) -> Bool {
    return !contains{!predicate($0)}
  }
}

[1, 2, 3].all{$0 > 0}
  • map2:用reduce实现map;
1
2
3
4
5
6
7
extension Array {
  func map2<T>(_ transform:(Element) -> T) -> [T] {
    return reduce([]){
      $0 + [transform($1)]
    }
  }
}
  • flatMap:先map再joined,当变换函数返回值为数组,且需要合并时使用;
1
2
3
4
5
6
7
8
9
extension Array {
  func flatMap<T>(_ transform:(Element) -> [T]) -> [T] {
    var result: [T] = []
    for x in self {
      result.append(contentsOf: transform(x))
    }
    return result
  }
}
  • forEach:无返回值迭代数组元素;

2019.01.03

Swift 进阶

数组类型
- 切片:切片只是数组的另一种表示方法,使用ptr、startIndex、endIndex表示,并不会创建新数组; (如果改变切片中元素,原数组中元素会改变吗?)
- 桥接:Swift数组可以桥接到OC中,编译器会自动把不兼容的值用一个不透明的box包起来;

Swfit 基础

  • Type Parameters
  • Type Parameters Constraints
  • Generic Type
  • Generic Founction
  • Associated Type
  • Associated Type Constraints
  • Generic where Clause

2019.01.07

Swift 进阶

字典 - removeValue(forKey:)
- updateValue(_:forKey:)
- mapValue
- frequency
- merge(_:uniqueKeysWith:)

集合 - IndexSet:整数集合,支持范围操作;
- CharacterSet:Unicode字符集合;

2019.01.08

Swift 进阶

范围 - Range
- CloseRange
- Comparable 协议;
- Strideable 协议;

2019.01.09

Swift 进阶

  • Sequence 协议是集合类型结构的基础,一个序列代表一系列具有相同类型的值,你可以对这些值进行迭代;
1
2
3
4
protocol Sequence {
  associated Iterator : IteratorProtocol
  func makeIterator() -> Iterator
}
  • IteratorProtocol 协议是对迭代器的描述,通过next()方法返回序列中下一个值;
1
2
3
4
protocol InteratorProtocol {
  associated Element
  mutating func next() -> Element?
}

2019.01.10

APP发布

  • 封板之后不要再往发版分支合代码;
  • 修改一定要验证,尤其是跨分支 cherry-pick 代码;
  • APP发布之前一定要看一下 TestFlight 版本是否正常;

Principles

  • What do you want ?
  • What is true?
  • What are you going to do about it ?

微信公开课

  • 我们更多倡导的是利用微信做出好产品分享用户。
  • 因为遵循原则,很多东西我们又必须坚持去改变。
  • 第一我们没有批量导入某一批好友,而是通过用户手动一个一个挑选。第二,在一个产品还没有被验证只能够产生自然增长的时候,我们没有去推广它。
  • 因为好的产品需要一定的独裁,否则它将包含很多不同意见以至于产品性格走向四分五裂。
  • 第一,坚持做一个好的,与时俱进的工具。
  • 微信是一个生活方式。
  • 第二个原动力是,“让创造者体现价值”。
  • 一个好的产品是有自己的使命的。
  • 最主要的是,技术的使命应该是帮助人类提高效率。
  • 一切盈利都是做好产品做好服务后的自然而来的副产品。
  • 小游戏的原动力是,它应该是一个关于创意的平台。并且让产生创意的人体现价值。

Swift 开发

  • 使用 Kingfiser 异步加载网络图片 imageView.kf.setImage(with:URL(string:"http://xxx.png"))
  • 使用 Material 自动布局 view.layout().top().left().width().height()
  • 使用 SnapKit 自动布局 view.snp.makeConstrains{(make) in // layout}
  • 使用 Extension 按功能模块分隔代码,相对清晰;
  • 使用 Moya 封装网络模块请求;
  • 使用 IGListKit 实现CollectionView,TableView;
  • 使用 SwiftyUserDefaults 实现 UserDefault 存取;
  • 使用 Realm 实现 数据库 存取;
  • 使用 Codable 实现 JSON 解析;

2019.01.11

RxSwift

  • Every Observable sequence is just a sequence. The key advantage for an Observable vs Swift’s Sequence is that it can also receive elements asynchronously.
  • Observable(ObservableType) is equivalent to Sequence.
  • ObservableType.subscribe method is equivalent to Sequence.makeIterator method.
  • Observer (callback) needs to be passed to ObservableType.subscribe method to receive sequence elements instead of calling next() on the returned iterator.
  • When an observable is created, it doesn’t perform any work simply because it has been created.
  • However, if you just call a method that returns an Observable, no sequence generation is performed and there are no side effects. Observable just defines how the sequence is generated and what parameters are used for element generation. Sequence generation starts when subscribe method is called.

2019.01.16

RxSwift 使用场景

  • 使用 RxSwift 实现 Target Action
  • 使用 RxSwift 实现 Delegate
  • 使用 RxSwift 实现 闭包回调
  • 使用 RxSwift 实现 Notification
  • 使用 RxSwift 实现 KVO
  • 使用 RxSwift 实现 依赖任务
  • 使用 RxSwift 实现 多异步任务同步

RxSwift 核心概念

  • Obervable:Single,Completable,Maybe,Driver,ControlEvent
  • Observer:AnyObserver,Binder
  • Obervable & Observer:AsyncSubject、PublishSubject、ReplaySubject、BehaviorSubject、Variable、ControlProperty
  • Operator:创建新序列 或 变换、组合原有序列
  • Disposable:DisposeBag、takeUntil
  • Schedulers:subscribeOn、observeOn、MainScheduler、SerialDispatchQueueScheduler、ConcurrentDispatchQueueScheduler、OperationQueueScheduler
  • Error Handling:retry、retryWhen、catchError

2019.01.17

RxSwift 常用操作符

Observable:描述可观察序列值是如何产生的,或者说观察者是如何接收该序列的,当subscribe时,真正触发序列产生。

创建 Observable
- create:通过构建函数,完整创建一个自定义的 Observable。各种操作符都是通过create实现的。
- just:创建只发出一个元素的 Observable。当只产生一个元素时,可以使用just快速创建,比如网络请求结果?
- timer:创建一定延时后,只发出一个元素的 Observable。类似于 dispatch_after ?
- from:将其他类型转换为 Observable。比如数组、可选值等。不必自己实现这个转化过程了。
- repeatElement:创建重复发出某个元素的 Observable。
- deferred:直到订阅发生,才创建 Observable,并且为每位订阅者创建全新的 Observable。订阅者获取独立序列。
- interval:创建每隔一段时间就发出一个索引数的 Observable。相当于一个repeated timer。
- empty:创建只发出一个完成事件的 Observable。什么时候使用呢?
- never:创建一个不会发出任何元素的 Observable。什么时候使用呢?
- startWith:创建一个在头部插入一些元素的 Observable。
- error:创建一个只有 error 的 Observable。

创建 可组合的Observable
- merger:创建一个合并多个 Observable 的 Observable,当某个 Observable 发出元素时,他就发出元素。类似多条生产线合并到一个出口。可交替发出元素。
- concat:创建一个合并多个 Observable 的 Observable,当前一个 Observable 发出完毕时,下一个 Observable 才开始发出元素。按顺序发出元素。
- zip:通过组合函数,创建一个将多个 Observable 的元素组合之后发出的 Observable。严格按照索引数组合发出。
- combineLatest:通过组合函数,创建一个将多个 Observable 的最新元素组合之后发出的 Observable。用最新值替换对应值后组合发出。

转换 Observable 元素
- map:通过转换函数,将 Observable 的每个元素转换后发出。
- scan/accumulator:通过转换函数,将 Observable 的每个元素累积之前的结果转换后发出。
- reduce:通过转换函数,将 Observable 的所有元素累积的结果发出。

转换 Observable 元素 为 Observable
- flatMap:通过转换函数,将 Observable 的每个元素转换成其他的 Observable ,然后将这些 Observables 合并发出;
- flatMapLatest:通过转换函数,将 Observable 的每个元素转换成其他的 Observable,然后取最新的发出;
- concatMap:通过转换函数,将 Observable 的每个元素转换成其他的 Observable 后,按顺序发出;
- publish:将 Observable 转换为可被连接的 Observable。直到 connect 才开始发出元素。
- replay:将 Observable 转换为可被连接的 Observable。直到 connect 时开始发出缓存的最新n个元素。
- refCount:将 Observable 转换为可自动连接和断开的的 Observable。
- connect:通知 ConnectableObservable 可以开始发出元素了。

如何发送 Observable 元素
- delay:将 Observable 的所有元素延迟一段设定好的时间发出;
- materialize:将 Observable 产生的时间全部转换成元素发出;
- dematerialize:将 materialize 转换后的元素还原;
- ignoreElements:将 Observable 产生的 next 事件忽略,只发出 completed 和 error 事件;只关心终止时使用
- buffer:将 Observable 元素周期性的以 元素集合 发出来。
- window:将 元素集合 周期性的以 Observable 形态发出来。
- groupBy:将 Observable 以键值分组为 子Observable 发出来。
- single:将 Observable 限制为只发出一个元素。

  • filter:只发出通过判定的元素。
  • take:只发出头 n 个元素。
  • takeWhile:从头发出通过判定的元素。
  • takeUntil:从头发出直到另一个 Observable 发出元素。
  • takeLast:只发出尾 n 个元素。
  • elementAt:只发出第 n 个元素。
  • skip:跳过头 n 个元素。
  • skipWhile:从头跳过通过判定的元素。
  • skipUntil:从头跳过直到另一个 Observable 发出元素。
  • sample:按第二个 Observable 对 第一个 Observable 采样发出元素。
  • debounce:过滤掉高频产生的元素。
  • distinctUntilChanged:只发出与上个元素不同的元素。
  • delaySubscription:将延迟一段时间后才订阅 Observable。

  • amb:在多个源 Observable 中,取第一个发出元素的 Observable 只发出其元素。

在哪个线程发出和接收元素
- subscribeOn:指定 Observable 在哪个 Scheduler 执行。
- observeOn:指定 Observable 在哪个 Scheduler 发出通知。

当发生某些事件时如何操作
- do:当 Observable 产生某些事件时,执行某个操作。可以来注册一些回调操作,单独回调。
- timeout:如果 Observable 在规定时间内么有产生任何元素,产生一个超时的 error 事件。

如何处理 Observable error 事件
- catchError:拦截一个 error 事件,将它替换成其他 Observable。
- catchErrorJustReturn:拦截一个 error 事件,将它替换成其他元素并结束。
- retry:拦截一个 error 事件,并重新订阅该 Observable。

  • using:创建一个可被清除的资源,它和 Observable 具有相同的生命周期。

RxSwift 常用架构

  • MVVM:View –> Observable –> ViewModel –> Observable –> Controller
  • RxFeedback:State –> Event –> StateChanged –> Event –> State
  • ReactorKit:View –> Action –> Reactor –> State

2019.01.18

IGListKit

  • [ListDiffable] –> ListAdapter –> ListSectionController –> Cell
  • Data: needs conform ListDiffable
  • ListAdapter:init with UICollectionView and ListAdapterUpdater,set datasource delegate,use performUpdates(animated:) to tell data has changed
  • UIViewController:needs conform ListAdapterDataSource,implement objects(for:)listAdapter(_:sectionControllerFor:)emptyView(for:)
  • ListSectionController:needs save data, state, and override numberOfItemscellForItem(at:)sizeForItem(at:)didUpdate(to:)didSelectItem(at:)
  • UICollectionViewCell:needs implement cellSize(for:)
  • 布局的关键在于如何划分Cell,采用分层组合的方式,类似于 ComponentKit ,提高复用性
  • UUID().uuidString 来生成 唯一标识
  • 一种 model 类型 对应一种 sectionController,sectionController 负责如何展示 model,可以用一个cell展示,也可以分多个cell展示
  • 避免数据与UI不一致导致的crash
  • 分离dataSource;

2019.01.24

SwiftyUserDefaults

  • add keys
1
2
3
4
5
extension DefaultsKeys {
  static let firstInFlag = DefaultsKey<Bool>("firstInFlag")
  static let username = DefaultsKey<String?>("username")
  static let password = DefaultsKey<String?>("password")
}
  • use keys
1
2
3
4
5
6
7
8
9
10
11
12
func storeDataWithUserDefault (){
    if !Defaults[.firstInFlag] {
      print("Is first in")
      Defaults[.firstInFlag] = true
      Defaults[.username] = "Light"
      Defaults[.password] = "123456"
    } else {
      print("Not first in")
      print("username \(Defaults[.username] ?? "not set")")
      print("password \(Defaults[.password] ?? "not set")")
    }
  }

RealmSwift

  • Declare Model
1
2
3
4
class Person: Object {
  @objc dynamic var name = ""
  @objc dynamic var age = 0
}
  • Get Realm let realm = try! Realm()
  • Query let studentOver20 = realm.objects(Person.self).filter("age > 20").first
  • Update try! realm.write { realm.add(mySelf) realm.add(other) }

2019.01.25

Swift 5.0 新特性

  • 应用瘦身:Swfit 应用不再包含标准库的动态链接库;
  • Swift 语言
    • @dynamicCallable 简化方法调用,增强与动态语言的协作性;
    • Key paths 现在支持指向自身的 key path \.self
    • enum case 不定参数 需转换为 数组传入;
    • try? 表达式不再返回嵌套的optional;
    • 字面量初始化
    • String 插值更高效,_ExpressibleByStringInterpolation方法需要替换;
  • Swfit 标准库
    • DictionaryLiteral 重命名为 KeyValuePairs
    • Sequence 不再有关联类型 SubSequence
  • Swift Package Manager
  • Swift Compiler

2019.01.28

RxSwift 原理解析

2019.01.30

RTCRoom

  • Login
    • 初始化 RTCRoom
    • 获取 LoginInfo
    • 登录 -login:loginInfo:withCompletion:
  • Get Room List
    • 获取聊天室列表 -getRoomList:cnt:withCompletion:
  • Create Room
    • 创建聊天室 -creatRoom:roomInfo:withCompletion:
  • Enter Room
    • 进入聊天室 -enterRoom:withCompletion:
  • Show Local Video
    • 展示本地视频 -startLocalPreview:
    • 停止本地视频 -stopLocalPreview
    • 设置音频清晰度 -setHDAudio:
    • 设置视频码率 -setBitrateRange:max:
    • 切换前后摄像头 -switchCamera
    • 静音推流 -setMute:
    • 设置美颜效果 -setBeautyStyle:beaultyLevel:whitenessLevel:ruddinessLevel:
    • 进入前台 -switchToForeground
    • 进入后台 -switchToBackground:
  • Show Remote Video
    • 展示远程视频 -addRemoteView:withUserID:playBegin:playError:
  • On Get Pusher List
    • 记录 PusherInfo
    • 记录 playerView
    • 展示远程视频,出错时 -onPusherQuit:
    • 重新布局;
  • On Pusher Join
    • 记录 playerView
    • 记录 PusherInfo
    • 展示远程视频,出错时 -onPusherQuit:
    • 重新布局;
  • On Pusher Quit
    • 删除 playerView
    • 删除 PusherInfo
    • 重新布局;
  • On Room Close

iOS知识小集-180924

| Comments

修改UISearchBar取消按钮颜色

1
2
3
- (void)willPresentSearchController:(UISearchController *)searchController{
  [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitleTextAttributes:@{NSForegroundColorAttributeName:UIColorFromRGBA(0xff4a4a4a)} forState:UIControlStateNormal];
}

解决XCode10 引入非同一目录下头文件没有自动提示的问题

Xcode –> File –> Workspace Settings –> Build System –> Legacy Build System。
需重新编译后生效。

非 Retina 显示器,升级到 Mojave 之后发现文字不清晰问题

如果你在用 “非 Retina” 的显示器,升级到 Mojave 之后发现文字不清晰了,是因为 Mojave 默认关闭了文字的次像素渲染,如果需要可以通过终端里输入这个命令重新打开:
sudo defaults write -g CGFontRenderingFontSmoothingDisabled -bool NO
重新打开对应软件后生效。

iOS知识小集-180917

| Comments

相机拍摄图片方向调整

1
2
3
4
5
6
  if (image.imageOrientation != UIImageOrientationUp) {
      UIGraphicsBeginImageContext(image.size);
      [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
      image = UIGraphicsGetImageFromCurrentImageContext();
      UIGraphicsEndImageContext();
  }

Playground running 卡死

1、修改platform为Mac OS;
2、修改为手动执行;
3、为手动执行增加快捷键;

iOS知识小集-180910

| Comments

Siri Kit

Siri Kit作用

通过语音完成第三方应用功能,偏向于工具型操作。

实现机制

Domain:业务领域;
Intent:领域中的任务或指令;
语音识别 –> Domain / Intent –> 下发到已注册的Extension进行处理。
接近固定形式的表述更容易被识别。

集成

需要注意develop target系统版本问题。