lczalh


  • 首页

  • 归档

iOS开发 SkeletonView

发表于 2019-06-17

SkeletonView一种优雅的方式,向用户显示正在发生的事情,并为他们正在等待的内容做好准备

1.安装

pod "SkeletonView"

2.如何使用

2.1在适当的位置导入SkeletonView

import SkeletonView

2.2设置视图skeletonables

avatarImageView.isSkeletonable = true

2.3显示骨架

1
2
3
4
view.showSkeleton()                 // Solid
view.showGradientSkeleton() // Gradient
view.showAnimatedSkeleton() // Solid animated
view.showAnimatedGradientSkeleton() // Gradient animated

2.4更新骨架(如颜色,动画等)

1
2
3
4
view.updateSkeleton()                 // Solid
view.updateGradientSkeleton() // Gradient
view.updateAnimatedSkeleton() // Solid animated
view.updateAnimatedGradientSkeleton() // Gradient animated

2.5隐藏骨架

1
view.hideSkeleton()

3.UITableView

markdown

3.1遵守SkeletonTableViewDataSource协议,并实现协议方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension SearchMovieViewController: SkeletonTableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.models.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = self.models[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchMovieCell", for: indexPath) as! SearchMovieCell
if model.vod_pic?.isEmpty == false {
cell.movieImageView.kf.indicatorType = .activity
cell.movieImageView.kf.setImage(with: ImageResource(downloadURL: URL(string: model.vod_pic!)!), placeholder: UIImage(named: "zanwutupian"))
}
cell.titleLabel.text = model.vod_name
cell.detailsLabel.text = model.vod_blurb
return cell
}

func collectionSkeletonView(_ skeletonView: UITableView, cellIdentifierForRowAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "SearchMovieCell"
}
}

3.2 设置视图skeletonables

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
class SearchMovieCell: DiaryBaseTableViewCell {

/// 图片
var movieImageView: UIImageView!

/// 标题
var titleLabel: UILabel!

/// 详情
var detailsLabel: LCZAlignTopLabel!


override func config() {
self.isSkeletonable = true
movieImageView = UIImageView()
self.contentView.addSubview(movieImageView)
movieImageView.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(15)
make.centerY.equalToSuperview()
make.height.equalTo(100)
make.width.equalTo(80)
}
movieImageView.layer.cornerRadius = 5
movieImageView.clipsToBounds = true
movieImageView.contentMode = .scaleAspectFill
movieImageView.setContentHuggingPriority(.required, for: .horizontal)
movieImageView.setContentCompressionResistancePriority(.required, for: .horizontal)
movieImageView.isSkeletonable = true

titleLabel = UILabel()
self.contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { (make) in
make.left.equalTo(movieImageView.snp.right).offset(5)
make.top.equalTo(movieImageView.snp.top)
make.right.equalToSuperview().offset(-15)
}
titleLabel.font = LCZBoldFontSize(size: 16)
titleLabel.setContentHuggingPriority(.required, for: .vertical)
titleLabel.setContentCompressionResistancePriority(.required, for: .vertical)
titleLabel.text = "xxxxxxxxx"
titleLabel.isSkeletonable = true
titleLabel.textAlignment = .left

detailsLabel = LCZAlignTopLabel()
self.contentView.addSubview(detailsLabel)
detailsLabel.snp.makeConstraints { (make) in
make.left.equalTo(movieImageView.snp.right).offset(5)
make.top.equalTo(titleLabel.snp.bottom).offset(5)
make.right.equalToSuperview().offset(-15)
make.bottom.equalTo(movieImageView.snp.bottom)
}
detailsLabel.font = LCZFontSize(size: 14)
detailsLabel.numberOfLines = 0;
detailsLabel.textColor = LCZRgbColor(160, 160, 160, 1)
detailsLabel.isSkeletonable = true
detailsLabel.textAlignment = .left
}

}

3.3 显示骨架

1
self.searchMovieView.tableView.visibleCells.forEach { $0.showAnimatedGradientSkeleton(usingGradient: SkeletonGradient(baseColor: UIColor.clouds),animation: GradientDirection.topLeftBottomRight.slidingAnimation()) }

4.UICollectionView

markdown

4.1 遵守SkeletonCollectionViewDataSource协议,并实现协议方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func collectionSkeletonView(_ skeletonView: UICollectionView, cellIdentifierForItemAt indexPath: IndexPath) -> ReusableCellIdentifier {
return "MovieHomeCell"
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MovieHomeCell", for: indexPath) as! MovieHomeCell
let model = self.models[indexPath.row]
if model.vod_pic?.isEmpty == false {
cell.imageView.kf.indicatorType = .activity
cell.imageView.kf.setImage(with: ImageResource(downloadURL: URL(string: model.vod_pic!)!), placeholder: UIImage(named:"zanwutupian"))
}
cell.titleLabel.text = model.vod_name
return cell
}
// 显示骨架cell的个数
func collectionSkeletonView(_ skeletonView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}

4.2 设置视图skeletonables

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
class MovieHomeCell: DiaryBaseCollectionViewCell {

/// 图片
var imageView = UIImageView()

/// 标题
var titleLabel = UILabel()

override func config() {
self.isSkeletonable = true
self.contentView.layer.cornerRadius = 10
self.layer.cornerRadius = 10
self.contentView.clipsToBounds = true

self.contentView.addSubview(self.titleLabel)
self.titleLabel.font = LCZBoldFontSize(size: 16)
self.titleLabel.snp.makeConstraints({ (make) in
make.bottom.equalToSuperview().offset(-10)
make.left.equalToSuperview().offset(10)
make.right.equalToSuperview().offset(-10)
})
self.titleLabel.textAlignment = .center
self.titleLabel.font = LCZFontSize(size: 14)
self.titleLabel.textColor = LCZHexadecimalColor(hexadecimal: "#FECE1D")
self.titleLabel.isSkeletonable = true
self.titleLabel.text = "乐淘视界"

self.contentView.addSubview(self.imageView)
self.imageView.contentMode = .scaleAspectFill
self.imageView.clipsToBounds = true
self.imageView.snp.makeConstraints { (make) in
make.left.top.right.equalToSuperview()
make.bottom.equalTo(self.titleLabel.snp.top).offset(-10)
}
self.imageView.isSkeletonable = true
}

}

3.3 显示骨架

1
2
3
4
5
view.isSkeletonable = true
self.moreMoviesView.collectionView.prepareSkeleton(completion: { done in
self.view.showAnimatedGradientSkeleton(usingGradient: SkeletonGradient(baseColor: UIColor.clouds),
animation: GradientDirection.topLeftBottomRight.slidingAnimation())
})

iOS开发 UILabel

发表于 2019-06-13

1. 超出宽度的文本省略号的问题

1
logisticsDetailsLabel.lineBreakMode = .byCharWrapping

iOS开发 UITabBarController

发表于 2019-06-13

1. 隐藏UITabBarController上的按钮

1
2
3
[viewController willMoveToParentViewController:nil];
[viewController.view removeFromSuperview];
[viewController removeFromParentViewController];

iOS开发 UITableView

发表于 2019-06-13

1. UITableView自适应高度

1
2
self.homeTableView.estimatedRowHeight = 44
self.homeTableView.rowHeight = UITableViewAutomaticDimension;

iOS开发 SnapKit、Masonry

发表于 2019-06-13

1. SnapKit

优先完全显示内容、抵抗压缩

1
2
self.title.setContentHuggingPriority(.required, for: .horizontal)
self.title.setContentCompressionResistancePriority(.required, for: .horizontal)

2. Masonry

优先完全显示内容、抵抗压缩

1
2
[self.titleLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.titleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];

3. 获取frame

1
2
self.titleLabel.layoutIfNeeded()
print(self.titleLabel.frame)

iOS开发 子视图中获取父控制器、父视图

发表于 2019-06-13

Objective-C

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
/**
获取父控制器

@param view 当前视图
@param className 需要获取的控制器名称
@return 返回控制器对象
*/
- (UIViewController *)LCZGetSuperViewController:(UIView *)currentview superViewControllerClassName:(NSString *)className
{
for (UIView* next = [current superview]; next; next = next.superview) {
UIResponder *nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[NSClassFromString(className) class]]) {
return (UIViewController *)nextResponder;
}
}
return nil;
}

/**
获取当前视图中的目标父视图

@param currentView 当前视图
@param superView 父视图
@return 父视图对象
*/
- (UIView *)LCZGetSuperView:(UIView *)currentView superView:(UIView *)superView {
UIView *view = currentView.superview;
while (![view isKindOfClass:[superView class]]) {
if (view.superview == nil) {
return nil;
} else {
view = [view superview];
}
}
return view;
}

Swift

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
/// 通过当前视图获取父视图的控制器
///
/// - Parameters:
/// - currentView: 当前视图
/// - viewController: 需要获取到的控制器
/// - Returns: 返回控制器
func LCZGetSuperViewController<T: UIViewController>(currentView:UIView, viewController: T.Type) -> T? {
var view: UIView = currentView.superview!
while view.next!.isKind(of: T.self) != true {
guard view.superview != nil else {
return nil
}
view = view.superview!
}
return view.next as? T
}

/// 获取当前视图中的目标父视图
///
/// - Parameters:
/// - currentView: 当前视图
/// - superView: 目标父视图
/// - Returns: 返回父视图
public func LCZGetSuperView<T: UIView>(currentView: UIView, superView: T.Type) -> T? {
var view: UIView = currentView.superview!
while view.isKind(of: T.self) != true {
guard view.superview != nil else {
return nil
}
view = view.superview!
}
return view as? T
}

iOS开发 GCD

发表于 2019-06-13

1. GCD信号量

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
//创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// 等待信号量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 发起网络请求
[BadgeCountModel HomeBadgeCountBlock:^(NSDictionary *data) {
NSNumber *num = data[@"num"];
if (num.integerValue > 0 ) {
weakSelf.rightBt.isShowBadge = YES;
} else {
weakSelf.rightBt.isShowBadge = NO;
}
// 发送信号量
dispatch_semaphore_signal(semaphore);
} andError:^(NSError *error) {
dispatch_semaphore_signal(semaphore);
}];
});

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 发起网络请求
[BadgeCountModel HomeDetailBadgeCountBlock:^(NSArray *data) {
if (!weakSelf.badgeDict) {
weakSelf.badgeDict = [NSMutableDictionary dictionary];
}
[weakSelf.badgeDict removeAllObjects];
NSMutableArray *badgeArray = [NSMutableArray array];
for (NSDictionary *dict in data) {
BadgeCountModel *model = [[BadgeCountModel alloc] init];
model.code = [NSString stringWithFormat:@"%@",dict[@"key"]];
model.value = [NSString stringWithFormat:@"%@",dict[@"value"]];
[badgeArray addObject:model];
NSIndexPath *path = [self getIndexPathWithCode:model.code];
if (!path) {
continue;
}
[weakSelf.badgeDict setObject:model.value forKey:path];
}
[weakSelf.collectionView reloadData];
[[GLMenuManager shareInstance] setRedPointArray:badgeArray];
dispatch_semaphore_signal(semaphore);
} andError:^(NSError *error) {
dispatch_semaphore_signal(semaphore);
}];
});

2. GCD单例

1
2
3
4
5
6
7
8
+ (instancetype)shared {
static Share *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[Share alloc]init];
});
return shared;
}

java开发 MyBatis逆向工程配置

发表于 2019-06-04

mybatis-generator.xml

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

<context id="cosmetic" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressDate" value="true"/>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>

<!--数据库链接地址账号密码-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/diary?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true&amp;useSSL=false" userId="root" password="666666">
<!-- 防止生成字段不全 -->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:orcl"
userId="scott"
password="0000">
</jdbcConnection> -->
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!--生成pojo类存放位置-->
<javaModelGenerator targetPackage="com.diary.generator.pojo" targetProject="src/main/java">
<!-- 是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!--生成映射文件mapper接口存放位置-->
<sqlMapGenerator targetPackage="com.diary.generator.mapper" targetProject="src/main/java">
<!-- 是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!--生成mapper接口、mapper.xml类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.diary.generator.mapper" targetProject="src/main/java">
<!-- 是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!--生成对应表及类名,需要记住的一点是逆向工程无法生成关联关系,只能生成单表操作-->
<table tableName="user" domainObjectName="User" />
<!-- <table tableName="permission" domainObjectName="Permission" />
<table tableName="role" domainObjectName="Role" />
<table tableName="role_permission" domainObjectName="RolePermission" />
<table tableName="user_role" domainObjectName="UserRole" /> -->
</context>
</generatorConfiguration>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指向逆向工程配置文件
File configFile = new File("src/main/resources/mybatis-generator.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);

Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator =
new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}

iOS开发 RunTime

发表于 2019-05-22

1.方法交换

我们可以将Method Swizzling功能封装为类方法,作为NSObject的类别,这样我们后续调用也会方便些。

NSObject+Swizzling.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Swizzling)

/**
方法交换

@param originalSelector 源方法
@param swizzledSelector 新方法
*/
+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector
andSwizzledSelector:(SEL)swizzledSelector;

@end

NS_ASSUME_NONNULL_END

NSObject+Swizzling.m

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
#import "NSObject+Swizzling.h"
#import<objc/runtime.h>

@implementation NSObject (Swizzling)

+ (void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector
andSwizzledSelector:(SEL)swizzledSelector {
Class class = [self class];
//原有方法
Method originalMethod = class_getInstanceMethod(class, originalSelector);
//替换原有方法的新方法
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
//先尝试給源SEL添加IMP,这里是为了避免源SEL没有实现IMP的情况
BOOL didAddMethod = class_addMethod(class,originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {//添加成功:说明源SEL没有实现IMP,将源SEL的IMP替换到交换SEL的IMP
class_replaceMethod(class,swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {//添加失败:说明源SEL已经有IMP,直接将两个SEL的IMP交换即可
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}

@end

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import "ViewController.h"
#import "NSObject+Swizzling.h"

@implementation ViewController (Swizzling)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 交换对象方法
[self methodSwizzlingWithOriginalSelector:@selector(viewWillAppear:) andSwizzledSelector:@selector(lczViewWillAppear:)];

// 交换类方法
[object_getClass([LibDevModel class]) methodSwizzlingWithOriginalSelector:@selector(onScanOver:) andSwizzledSelector:@selector(lczOnScanOver:)];
});
}

2.消息转发

2.1 转发对象方法

LibDevModel+Swizzling.h

1
2
3
4
5
6
7
8
9
#import <DoorMasterSDK/DoorMasterSDK.h>

NS_ASSUME_NONNULL_BEGIN

@interface LibDevModel (Swizzling)
- (void)lczTimerCancel;
@end

NS_ASSUME_NONNULL_END

LibDevModel+Swizzling.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "LibDevModel+Swizzling.h"
#import "GLGuardViewController.h"

@implementation LibDevModel (Swizzling)

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 获取 aSelector 的方法签名
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) { // 如果无法获取
// 判断 ViewController 是否能够响应 aSelector
if ([ViewController instancesRespondToSelector:aSelector]) {
// 获取 ViewController 中 aSelector 的方法签名
signature = [ViewController instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([ViewController instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:[ViewController new]];
}
}

GLGuardViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
#import "GLGuardViewController.h"


@interface GLGuardViewController ()

@end

@implementation GLGuardViewController

- (void)lczTimerCancel {
NSLog(@"调用了");
}

2.2 转发类方法

LibDevModel+Swizzling.h

1
2
3
4
5
6
7
8
9
#import <DoorMasterSDK/DoorMasterSDK.h>

NS_ASSUME_NONNULL_BEGIN

@interface LibDevModel (Swizzling)
+ (void)lczTimerCancel;
@end

NS_ASSUME_NONNULL_END

LibDevModel+Swizzling.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 获取 aSelector 的方法签名
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) { // 如果无法获取
// 判断 ViewController 是否能够响应 aSelector
if ([GLGuardViewController respondsToSelector:aSelector]) {
// 获取 ViewController 中 aSelector 的方法签名
signature = [GLGuardViewController methodSignatureForSelector:aSelector];
}
}
return signature;
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([GLGuardViewController respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:[GLGuardViewController class]];
}
}

GLGuardViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
#import "GLGuardViewController.h"


@interface GLGuardViewController ()

@end

@implementation GLGuardViewController

+ (void)lczTimerCancel {
NSLog(@"调用了");
}

AFNetworking 网络请求重试机制

发表于 2019-04-29

使用递归方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)downloadFileRetryingNumberOfTimes:(NSUInteger)ntimes 
success:(void (^)(id responseObject))success
failure:(void (^)(NSError *error))failure
{
if (ntimes <= 0) {
if (failure) {
NSError *error = ...;
failure(error);
}
} else {
[self getPath:@"/path/to/file" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(...);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[self downloadFileRetryingNumberOfTimes:ntimes - 1 success:success failure:failure];
}];
}
}

摘录于AFNetworking Issuse

12
Liu Chao Zheng

Liu Chao Zheng

少一点抱怨,多一点努力

19 日志
5 标签
RSS
GitHub
© 2019 Liu Chao Zheng
由 Hexo 强力驱动
主题 - NexT.Gemini
总访客 人 总访问量 次