Xcode Tips - Spelling and Grammarly

勾选 Check Spelling while typing

如果代码中的英文拼写出现错误,将会出现只能提示,在拼写错误的地方下面加入红色波浪线。

快捷方式 command + ;
将会一条条的小时语法出现错误的地方。

另外一个快捷方式 command + shift + ;,可以帮你推荐正确的拼写。

2020/4/11 posted in  iOS

App Icon Design

Design

我们对符号的应用可以追溯到很久很久之前,总所周知我们现在使用的文字就是从象形文字发展过来的。

  • 象征化

    找到一个简单易懂的象征,物体、图形标注或者符号。

  • 简单性

    设计要简单,不要杂乱无章,太过复杂。如果设计的好,人们一眼就能看懂。

  • 关联性

    创建与用户之间的感情连接,跟UI一样,图标是应用的脸面,一定要有趣并且引人注目

  • 传统性

    对图标的修改要有意义而且谨慎,不建议经常更改应用图标尤其是每次升级的时候,要与时俱进。

比如最初的keynote的图标就是一张讲台,以后的每次迭代升级都是围绕着讲台进行的改变,是与时俱进的,风格不断变化。而且如果你注意到的话讲台上都有一个话筒,这个也是一直有延续的。

屏幕快照 2018-12-11 下午2.16.00

iOS平台与macOS设计的不同又相同。

屏幕快照 2018-12-11 下午2.19.41

配色,元素都是类似的,而且iOS的iwork图标更是保持了一致性,一致的地平面,一致的白色,没有多余复杂的元素。

现在来看GarageBand和Music Memos的关系,有颜色和图标来看,明显有关系。圆圈代表了木吉他上的音孔,弦的振动很清晰很有趣,让人感到动感和创造力

屏幕快照 2018-12-11 下午2.24.00

Q:怎么不显示名字,也能让人理解。

A:高对比度,颜色清晰可见,设计简单,构图独特。

下面是News的图标的例子。

屏幕快照 2018-12-11 下午2.32.55

进行了三次改变设计,才做到简单醒目,即使在文件夹的小图标也会一目了然,这点对于App Icon Design 十分重要。

屏幕快照 2018-12-11 下午2.34.30

Process

设计是一个交互过程,设计时要去考虑这些环节。

  • 独特性

    要做到有区分性,特别是对于一些类似功能的APP

    屏幕快照 2018-12-11 下午2.41.11

  • 实践

    纸笔有时候比鼠标有用的多,绘画是激发灵感的好方法。

    屏幕快照 2018-12-11 下午2.40.50

  • 测试

    要在主屏,在设置,在文件夹中测试不同情形的展示效果,要用图标展现界面的话要特别注意,Clear是一个很完美的典范,简单,多彩,高对比度。

    屏幕快照 2018-12-11 下午2.41.54

  • 耐心

    答案不会立马就来,慢慢来,检查选项,保存好作品,有时候最简单的设计或者你放弃的设计师最好的选择。

2020/4/11 posted in  iOS

APP 后台任务以及捕捉bugly无法捕捉的异常

后台任务

iOS 后台保活的 5 种方式:Background Mode、Background Fetch、Silent Push、PushKit、Background Task。

  • 使用 Background Mode 方式的话,App Store 在审核时会提高对 App 的要求。通常情况下,只有那些地图、音乐播放、VoIP 类的 App 才能通过审核。
  • Background Fetch 方式的唤醒时间不稳定,而且用户可以在系统里设置关闭这种方式,导致它的使用场景很少。
  • Silent Push 是推送的一种,会在后台唤起 App 30 秒。它的优先级很低,会调用 application:didReceiveRemoteNotifiacation:fetchCompletionHandler: 这个 delegate,和普通的 remote push notification 推送调用的 delegate 是一样的。
  • PushKit 后台唤醒 App 后能够保活 30 秒。它主要用于提升 VoIP 应用的体验。
  • Background Task 方式,是使用最多的。App 退后台后,默认都会使用这种方式。

接下来,我们就看一下,Background Task 方式为什么是使用最多的,它可以解决哪些问题?

在你的程序退到后台以后,只有几秒钟的时间可以执行代码,接下来就会被系统挂起。进程挂起后所有线程都会暂停,不管这个线程是文件读写还是内存读写都会被暂停。但是,数据读写过程无法暂停只能被中断,中断时数据读写异常而且容易损坏文件,所以系统会选择主动杀掉 App 进程。

而 Background Task 这种方式,就是系统提供了 beginBackgroundTaskWithExpirationHandler 方法来延长后台执行时间,可以解决你退后台后还需要一些时间去处理一些任务的诉求。

Background Task 方式的使用方法,如下面这段代码所示:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^( void) {
        [self yourTask];
    }];
}

在这段代码中,yourTask 任务最多执行 3 分钟,3 分钟内 yourTask 运行完成,你的 App 就会挂起。 如果 yourTask 在 3 分钟之内没有执行完的话,系统会强制杀掉进程,从而造成崩溃,这就是为什么 App 退后台容易出现崩溃的原因。

捕捉被系统杀死的异常

系统杀死的app是无法捕捉到崩溃信号的,所以上面的方法也就无效了。

因为app在后台执行任务只会保活三分钟,如果我们在[self yourTask];中的任务执行超过三分钟,那么我们就无法捕获这种崩溃。

但是我们可以在应用被杀死之前先把堆栈信息保存到内存当中,这样当app下次启动联网的时候就可以上传这些堆栈信息到我们自身的服务器当中,分析崩溃日志以及找出异常原因。

打印调用堆栈信息

NSLog(@"%@",[NSThread callStackSymbols]);

应用崩溃有哪些

6

2020/4/10 posted in  iOS

Swift & Objective-C & C++之间的混编

①Objective-C 调用 C++

OC本身可以直接调用C的代码 比如直接可以调用printf方法。
但是OC中的.m直接编译C++的代码就会出现编译错误
需要把OC.m改成OC.mm之后(objective-c变成了objective-c++)引入iostream(#include<iostream>)就可以直接编译了。
这可以在.mm文件当中直接使用c++的代码。
也可以另外新建一个c++的文件,并且创建.h文件。

********CPPHello.hpp声明文件********
#ifndef CPPHello_hpp
#define CPPHello_hpp

#include <stdio.h>
class CPPHello{
    
public:
    static void sayHello();
};

#endif /* CPPHello_hpp */
********CPPHello.cpp实现文件********
#include "CPPHello.hpp"

void CPPHello::sayHello(){
    
    printf("hello world");
    
}

注意:oc调用c++必须要把.m改成.mm,要不然会报错。

②C++ 调用 Objective-C

新建cocoaTouch中的类,语言选择Objective-C,比如objcHello.hobjcHello.m然后在刚才的c++文件(CPPHello.cpp)中import objcHello.h就会出现编译错误。
所以c++语言不能直接import OC的头文件要对头文件进行修改,把头文件所有内容删除替换成c++能够识别的代码比如c或者c++的代码。

//objcHello.h
void objcSayHello();

另外objcHello.m文件要修改成objcHello.mm文件,然后#import <Foundation/Foundation.h>之后就可以在objcHello.mm文件调用oc的代码了。
并且可以在objcHello.mm文件中写oc的类了,比如如下代码。

#import <Foundation/Foundation.h>
#import "ObjcHello.h"

@interface ObjcHello:NSObject

-(void)sayHello;

@end

@implementation ObjcHello

-(void)sayHello{
    
    NSLog(@"hello objc");
}
@end


void objcSayHello(){
    
//  NSLog(@"hello Objc");
    ObjcHello * h = [ObjcHello new];
    [h sayHello];
}

然后修改完之后引入比如#include "ObjcHello.h"就可以在里面对.h文件的方法进行调用了。

#include "CPPHello.hpp"
#include "ObjcHello.h"
void CPPHello::sayHello(){
    
//   printf("hello world");
    objcSayHello();
    
}

③Swift调用C

新建CHello.h文件,然后创建头文件,然后选择创建桥接文件。
在这个桥接文件中#import其他公开头文件用来暴露给swift文件。

1.在.h文件中声明一个方法

CHello.h
#ifndef CHello_h
#define CHello_h

#include <stdio.h>
void sayHello();
#endif /* CHello_h */

2.在.c文件中实现一个方法

Chello.c 
#include "CHello.h"

void sayHello(){
    
    printf("hello c");
    
}

3.在桥接文件中引入CHello.h

//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "CHello.h"

4.在swift当中直接调用sayHello()方法

④Swift & Objective-C & C++混编

新建OC的类比如OCHello.h和OCHello.m

#import <Foundation/Foundation.h>

@interface OCHello : NSObject
-(void)hello;
@end
#import "OCHello.h"

@implementation OCHello
-(void)hello{
    
    NSLog(@"oc sayHello");
}
@end

然后在桥接文件中引入OCHello.h
在swift当中就可以直接调用,代码如下所示

import UIKit
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        var h = OCHello();
        h.hello()

    }
}

如果想要swift调用c++的话可以把OCHello.m改成OCHello.mm
在.mm文件中写c++的代码如下所示

#include <iostream>
#import "OCHello.h"

@implementation OCHello
-(void)hello{
    
//    NSLog(@"oc sayHello");
    std::cout<<"hello cpp\n";
}
@end

swift当中的代码不做任何变化,这样就做到了三种语言的混编。

提示

.mm文件当中可以写oc,c,c++的代码
oc可以完全兼容c语言的代码
swift不可以直接执行c语言的代码,要做一些声明比如上面的调用实现。
swift不可以执行c++的代码,要用oc做中间层,一般要避免。

2020/2/11 posted in  iOS

视图的绘制周期

当view第一次在屏幕上显示的时候,系统会绘制它的内容,然后系统会截取内容的快照,并且将快照作为视图的可见外观,如果你永远不改变视图的内容,那么视图的绘制代码永远不会改变。如果更改了视图的内容,不用直接重新绘制,而是使用setNeedsDisplay或者setNeedsDisplayInRect方法是视图无效。这些方法会告知视图内容以及改变并且需要在下一次进行重绘。如果需要立马重绘,那么需要使用layoutIfNeeded方法。

iOS-4
iOS-5

2019/10/11 posted in  iOS

启动优化

main 函数执行开始会进入到appDelegatedidFinishLaunchingWithOptions方法执行

很多人在一些第三方 SDK 初始化配置,各种初始化的工作都放在appDelegatedidFinishLaunchingWithOptions 这个阶段执行,其实是不合理的,正确的做法应该是弄清楚哪些是APP 启动时候真正需要的初始化功能,哪些是在其对应功能开始使用时才需要初始化的,梳理完之后把他们都放在对应的阶段进行。

首屏渲染这部分的代码止于didFinishLaunchingWithOptions 方法作用于结束。因为这时候首屏已经完成了渲染,所以可以不着急立马对其优化,但是那些阻碍线程的操作还是要优先处理的。

首屏渲染完成之后的阶段应该是非首屏其他业务模块的初始化,监听的注册,配置文件的读取等等。

时间统计

可以在Edit Scheme中添加Arguments用来统计启动时间

屏幕快照 2019-04-09 上午11.51.38

添加参数DYLD_PRINT_STATISTICS 并且 value 为1

这样启动app的时候就会有时间统计

Total pre-main time: 3.3 seconds (100.0%)
         dylib loading time: 551.91 milliseconds (16.4%)
        rebase/binding time: 1.8 seconds (54.5%)
            ObjC setup time: 754.55 milliseconds (22.5%)
           initializer time: 217.73 milliseconds (6.4%)
           slowest intializers :
             libSystem.B.dylib :  12.87 milliseconds (0.3%)

同时这个顺序也就代表了应用启动时的执行顺序。

2019/10/11 posted in  iOS

Swifter4

  1. Curring(柯里化)

    把接受多个参数的方法变换成接受第一个参数的方法,并且返回接受余下的参数并返回结果的新方法。

    在Swift2中可以将方法进行Curring 但是在Swift4不再支持方法的柯里化

    屏幕快照 2019-02-12 下午3.48.34

  2. @autoclosure

    func logIfTrue(predicate: () -> Bool) {
            if predicate() {
    print("true")
    }
    }
    调用方式1
    logIfTrue(predicate: {2 > 1})
    调用方式2
    logIfTrue{2 > 1}

    原来的闭包的书写方式如上面所写,但是看起来不是很容易理解,这时就出现了自动闭包。

    swift4 中的自动闭包书写和 swift2中不同 区别点在@autoclosure的位置,swift2中是在参数之前,在 swift4中是在参数之后。

    func logIfTrue(predicate:@autoclosure () -> Bool) {
            if predicate() {
    print("true")
    }
    }
    logIfTrue(predicate: 2 > 1)

    另外需要注意@autoclosure 并不支持带有输入参数的写法,只有形如()-> T 的参数才能使用这个特性进行简化。

  3. Designated、Convenience 和Required

    只有在子类重写了父类的 designated 方法的时候才可以直接调用父类的 convenience 方法

    如果添加 required 关键字修饰初始化方法,则在子类中必须对该 required 方法进行实现。这样做的好处就是可以保证依赖于某个 designated 方法的 convenience 一直可以被使用。

  4. Swift 中的Array,Dictionary和Set在集合中只能存储同一个类型的元素,但是NSArray可以存储多种不同类型的数据,如果想要Array 存储不同类型的数据,可以使用Any AnyObject

    let store:[Any] = [1,"1"]

2019/10/10 posted in  iOS

UINavigationItem

纵观Apple的官方应用程式的导航栏你就会发现在iOS11之后导航栏的高度会自动变化,文字的大小以及位置也会随之变化。
这是iOS11之后的新特性,苹果是使用给UINavigationItem添加扩展的方法增加的新功能,这也给了我们新的一种思路,为了不去修改之前代码的原则上添加新的功能的方式。

extension UINavigationItem {
    public enum LargeTitleDisplayMode : Int {
        /// Automatically use the large out-of-line title based on the state of the previous item in the navigation bar. An item with largeTitleDisplayMode=Automatic will show or hide the large title based on the request of the previous navigation item. If the first item pushed is set to Automatic, then it will show the large title if the navigation bar has prefersLargeTitles=YES.
        case automatic

        
        /// Always use a larger title when this item is top most.
        case always

        
        /// Never use a larger title when this item is top most.
        case never
    }
}
  • largeTitie的两种展现形式如下所示

屏幕快照 2018-12-05 下午1.23.25 屏幕快照 2018-12-05 下午1.23.29

设置方式

  • xib设置

    • never 一直都是小标题
    • automatic 自动切换
    • always 一直都是大标题

屏幕快照 2018-12-05 下午1.30.58

  • 代码设置

    因为这是新增加的属性所以要做判断
     if #available(iOS 11.0, *) {
    self.navigationItem.largeTitleDisplayMode = .automatic
    } else {
    // Fallback on earlier versions
    };
2019/9/11 posted in  iOS