0%

Mac 微信备份的聊天记录存储路径

每次备份聊天记录都是几个G,把聊天记录存到了网盘里面,将来有必要的时候可以从网盘上恢复

备份

查看备份文件

参考路径:

/Users/jft0m/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/Backup

这个备份是区分用户的,不同用户的备份是不能共享的

恢复方式

将来把备份的文件放回前面提到的目录

![image-20190618111708058](/Users/jft0m/Library/Application Support/typora-user-images/image-20190618111708058.png)

如果能看到最后一张图证明数据恢复成功,点击恢复聊天记录至手机

当设置了 scrollEnabled 为 NO 以后,当输入的文字超出可显示区域以后,第一行的左上角会出现一个很小的闪烁的光标,虽然是系统的默认实现,但是看上去就像是个 bug

1
textView.scrollEnabled = NO; 

(此处省略截图)

解决思路

监听文字输入是否超出,超出以后隐藏光标,焦点在可视范围内的时候显示光标。

光标隐藏

光标创建逻辑被封装在 UITextView 内部,无法直接访问,不过我们可以通过修改 tintColor 来实现

UITextView+CaretUtil

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
@interface UITextView (CaretUtil)
@property (nonatomic, strong) UIColor *originTextViewTintColor;
- (void)updateCaretColor;
@end

#import "UITextView+CaretUtil.h"
#import <objc/runtime.h>

static void *kOriginTextViewTintColorKey = "OriginTextViewTintColor";

@implementation UITextView (CaretUtil)

- (void)updateCaretColor {
if (self.selectedTextRange) {
NSLayoutManager *layoutManager = self.layoutManager;
NSTextContainer *textContainer = self.textContainer;
UITextPosition *beginning = self.beginningOfDocument;

UITextRange *selectedRange = self.selectedTextRange;
UITextPosition *selectionStart = selectedRange.start;
UITextPosition *selectionEnd = selectedRange.end;

const NSInteger location = [self offsetFromPosition:beginning toPosition:selectionStart];
const NSInteger length = [self offsetFromPosition:selectionStart toPosition:selectionEnd];

NSRange textRange = NSMakeRange(location, length);
NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:textRange actualCharacterRange:nil];
CGRect caretRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];
if (caretRect.size.height == 0) {
self.tintColor = [UIColor clearColor];
} else {
self.tintColor = self.originTextViewTintColor;
}
}
}

- (void)setOriginTextViewTintColor:(UIColor *)originTextViewTintColor {
objc_setAssociatedObject(self, kOriginTextViewTintColorKey, originTextViewTintColor, OBJC_ASSOCIATION_RETAIN);
}

- (UIColor *)originTextViewTintColor {
return objc_getAssociatedObject(self, kOriginTextViewTintColorKey);
}

@end

选中的时候更新光标颜色

这里直接采取继承,覆写 setSelectedTextRange: 方法,希望代码侵入性低一点的可以选择 AOP

1
2
3
4
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange {
[super setSelectedTextRange:selectedTextRange];
[self updateCaretColor];
}

###TextView Delegate 中更新光标颜色

1
2
3
4
5
6
7
- (void)textViewDidChange:(UITextView *)textView {
[textView updateCaretColor];
}

- (void)textViewDidBeginEditing:(UITextView *)textView {
[textView updateCaretColor];
}

PHImageManager 在初始化的时候添加了 DISPATCH_SOURCE_TYPE_MEMORYPRESSURE dispatch source event,当内存不够的时候会尝试移除缓存。并且 PHImageManager 是一个懒加载的对象

这就意味着当回调发生的时候,你如果曾经调用过 -[PHImageManager defaultManager] 隐式初始化,而且没获得相册权限,就会导致 crash。

阅读全文 »

判读一个图片/视频是否在 iCloud 比较准确的代码

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
- (PHImageRequestID)checkIsCloud:(PHAsset *)asset cachingImageManager:(PHCachingImageManager *)cachingImageManager {
if (asset.mediaType == PHAssetMediaTypeVideo) {
PHVideoRequestOptions *options = [PHVideoRequestOptions new];
options.deliveryMode = PHVideoRequestOptionsDeliveryModeMediumQualityFormat;
return [cachingImageManager requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable avAsset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
if (asset != self.asset) return;
dispatch_async(dispatch_get_main_queue(), ^{
if (info[@"PHImageFileSandboxExtensionTokenKey"]) {
self.iCloudStatus = KICloudStatusNone;
} else if ([info[PHImageResultIsInCloudKey] boolValue]) {
self.iCloudStatus = KICloudStatusNormal;
} else {
self.iCloudStatus = KICloudStatusNone;
}
});
}];
} else {
return [cachingImageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
if (asset != self.asset) return;
dispatch_async(dispatch_get_main_queue(), ^{
if (info[@"PHImageFileSandboxExtensionTokenKey"]) {
self.iCloudStatus = KICloudStatusNone;
} else if ([info[PHImageResultIsInCloudKey] boolValue]) {
self.iCloudStatus = KICloudStatusNormal;
} else {
self.iCloudStatus = KICloudStatusNone;
}
});
}];
}
}

系统在调用 endInteractiveMovement 之后会做一个动画(把 cell 放到它应该在的位置)然后调用一次 cellForItem 确保显示正常。

阅读全文 »