0%

iOS13 自定义返回键位置问题

iOS13 beta7已经出来了,为了提前适配应用,测试机升级看看应用是否有问题,幸好坑不算多,主要问题就是用系统导航栏的位置返回按钮向右偏移过多。主要与原有项目中的一些自定义导航返回键不统一。


之前项目已经对iOS11后导航栏的变化做过处理
  1. iOS11以下通过UIBarButtonSystemItemFixedSpace进行处理,可以参考论坛的一篇文章,但是iOS13系统处理不了。
  2. 所以寻找新方法解决这个问题,让我们先删除原来项目代码,大刀阔斧的干,要有豁出去的精神。
因为是使用系统导航栏,况且项目很大,不容易去修改整个结构,遂就对系统导航进行修改

网上参考,在寻找一些解决方案。觉得这篇最合适 iOS11 导航栏按钮位置问题的解决——新,简洁流畅无冗余,结合自己项目做一些修改。最主要同时解决iOS13导航栏返回键偏移的问题。
使用layoutMargins这个属性

遍历图层大致可以看到时这样的结构

1
<_UINavigationBarContentView: 0x7fc141607250; frame = (0 0; 414 44); layer = <CALayer: 0x608000038cc0>>

这个UINavigationBarContentView平铺在导航栏中作为iOS11的各个按钮的父视图,该视图的所有的子视图都会有一个layoutMargins被占用,也就是系统调整的占位,我们只要把这个置空就行了.那样的话该视图下的所有的子视图的空间就会变成我们想要的那样,当然为了保险起见,该视图的父视图也就是bar的layoutMargins也置空,这样 整个bar就会跟一个普通视图一样了 左右的占位约束就不存在了

可以过代码处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@implementation UINavigationBar (FixSpace)
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self swizzleInstanceMethodWithOriginSel:@selector(layoutSubviews)
swizzledSel:@selector(vst_layoutSubviews)];
});
}

-(void)vst_layoutSubviews{
[self vst_layoutSubviews];

if (deviceVersion >= 11) {
self.layoutMargins = UIEdgeInsetsZero;
for (UIView *subview in self.subviews) {
if ([NSStringFromClass(subview.class) containsString:@"ContentView"]) {
subview.layoutMargins = UIEdgeInsetsZero;//可修正iOS11之后的偏移
}
}
}
}
@end
最终代码
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
#import <UIKit/UIKit.h>

@interface UINavigationConfig : NSObject

@property (nonatomic, assign) CGFloat vst_defaultFixSpace; // item距离两端的间距,默认为0
@property (nonatomic, assign) BOOL vst_disableFixSpace; // 是否禁止使用修正,默认为NO

+ (instancetype)shared;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

- (CGFloat)vst_systemSpace;

@end

@interface UINavigationItem (VSTFixSpace)

@end

@interface NSObject (VSTFixSpace)

@end

#import "UINavigationVSTFixSpace.h"
#import <objc/runtime.h>

void vst_swizzle(Class oldClass, NSString *oldSelector, Class newClass) {
NSString *newSelector = [NSString stringWithFormat:@"vst_%@", oldSelector];
Method old = class_getInstanceMethod(oldClass, NSSelectorFromString(oldSelector));
Method new = class_getInstanceMethod(newClass, NSSelectorFromString(newSelector));
method_exchangeImplementations(old, new);
}

@implementation UINavigationConfig

+ (instancetype)shared {
static UINavigationConfig *config;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
config = [[self alloc] init];
});
return config;
}

-(instancetype)init {
if (self = [super init]) {
self.vst_defaultFixSpace = 0;
self.vst_disableFixSpace = NO;
}
return self;
}

- (CGFloat)vst_systemSpace {
return MIN([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) > 375 ? 20 : 16;
}

@end

@implementation UINavigationItem (VSTFixSpace)

+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(iOS 11.0, *)) {} else {
NSArray <NSString *>*oriSels = @[@"setLeftBarButtonItem:",
@"setLeftBarButtonItem:animated:",
@"setLeftBarButtonItems:",
@"setLeftBarButtonItems:animated:",
@"setRightBarButtonItem:",
@"setRightBarButtonItem:animated:",
@"setRightBarButtonItems:",
@"setRightBarButtonItems:animated:"];

[oriSels enumerateObjectsUsingBlock:^(NSString * _Nonnull oriSel, NSUInteger idx, BOOL * _Nonnull stop) {
vst_swizzle(self, oriSel, self);
}];
}
});
}

-(void)vst_setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem {
[self setLeftBarButtonItem:leftBarButtonItem animated:NO];
}

-(void)vst_setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem animated:(BOOL)animated {
if (!UINavigationConfig.shared.vst_disableFixSpace && leftBarButtonItem) {// 存在按钮且需要调节
[self setLeftBarButtonItems:@[leftBarButtonItem] animated:animated];
} else {// 不存在按钮,或者不需要调节
[self vst_setLeftBarButtonItem:leftBarButtonItem animated:animated];
}
}


-(void)vst_setLeftBarButtonItems:(NSArray<UIBarButtonItem *> *)leftBarButtonItems {
[self setLeftBarButtonItems:leftBarButtonItems animated:NO];
}

-(void)vst_setLeftBarButtonItems:(NSArray<UIBarButtonItem *> *)leftBarButtonItems animated:(BOOL)animated {
if (!UINavigationConfig.shared.vst_disableFixSpace && leftBarButtonItems.count) {// 存在按钮且需要调节
UIBarButtonItem *firstItem = leftBarButtonItems.firstObject;
CGFloat width = UINavigationConfig.shared.vst_defaultFixSpace - UINavigationConfig.shared.vst_systemSpace;
if (firstItem.width == width) {// 已经存在space
[self vst_setLeftBarButtonItems:leftBarButtonItems animated:animated];
} else {
NSMutableArray *items = [NSMutableArray arrayWithArray:leftBarButtonItems];
[items insertObject:[self fixedSpaceWithWidth:width] atIndex:0];
[self vst_setLeftBarButtonItems:items animated:animated];
}
} else {// 不存在按钮,或者不需要调节
[self vst_setLeftBarButtonItems:leftBarButtonItems animated:animated];
}
}

-(void)vst_setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem{
[self setRightBarButtonItem:rightBarButtonItem animated:NO];
}

- (void)vst_setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem animated:(BOOL)animated {
if (![UINavigationConfig shared].vst_disableFixSpace && rightBarButtonItem) {// 存在按钮且需要调节
[self setRightBarButtonItems:@[rightBarButtonItem] animated:animated];
} else {// 不存在按钮,或者不需要调节
[self vst_setRightBarButtonItem:rightBarButtonItem animated:animated];
}
}

- (void)vst_setRightBarButtonItems:(NSArray<UIBarButtonItem *> *)rightBarButtonItems{
[self setRightBarButtonItems:rightBarButtonItems animated:NO];
}

- (void)vst_setRightBarButtonItems:(NSArray<UIBarButtonItem *> *)rightBarButtonItems animated:(BOOL)animated {
if (!UINavigationConfig.shared.vst_disableFixSpace && rightBarButtonItems.count) {// 存在按钮且需要调节
UIBarButtonItem *firstItem = rightBarButtonItems.firstObject;
CGFloat width = UINavigationConfig.shared.vst_defaultFixSpace - UINavigationConfig.shared.vst_systemSpace;
if (firstItem.width == width) {// 已经存在space
[self vst_setRightBarButtonItems:rightBarButtonItems animated:animated];
} else {
NSMutableArray *items = [NSMutableArray arrayWithArray:rightBarButtonItems];
[items insertObject:[self fixedSpaceWithWidth:width] atIndex:0];
[self vst_setRightBarButtonItems:items animated:animated];
}
} else {// 不存在按钮,或者不需要调节
[self vst_setRightBarButtonItems:rightBarButtonItems animated:animated];
}
}

- (UIBarButtonItem *)fixedSpaceWithWidth:(CGFloat)width {
UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
fixedSpace.width = width;
return fixedSpace;
}

@end

@implementation NSObject (VSTFixSpace)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (@available(iOS 13.0, *)) {
NSDictionary <NSString *, NSString *>*oriSels = @{@"_UINavigationBarContentView": @"layoutSubviews",
@"_UINavigationBarContentViewLayout": @"_updateMarginConstraints"};
[oriSels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull cls, NSString * _Nonnull oriSel, BOOL * _Nonnull stop) {
vst_swizzle(NSClassFromString(cls), oriSel, NSObject.class);
}];
}
});
}

- (void)vst_layoutSubviews {
[self vst_layoutSubviews];
if (UINavigationConfig.shared.vst_disableFixSpace) return;
if (![self isMemberOfClass:NSClassFromString(@"_UINavigationBarContentView")]) return;
id layout = [self valueForKey:@"_layout"];
if (!layout) return;
SEL selector = NSSelectorFromString(@"_updateMarginConstraints");
IMP imp = [layout methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(layout, selector);
}

- (void)vst__updateMarginConstraints {
[self vst__updateMarginConstraints];
if (UINavigationConfig.shared.vst_disableFixSpace) return;
if (![self isMemberOfClass:NSClassFromString(@"_UINavigationBarContentViewLayout")]) return;
[self vst_adjustLeadingBarConstraints];
[self vst_adjustTrailingBarConstraints];
}

- (void)vst_adjustLeadingBarConstraints {
if (UINavigationConfig.shared.vst_disableFixSpace) return;
NSArray<NSLayoutConstraint *> *leadingBarConstraints = [self valueForKey:@"_leadingBarConstraints"];
if (!leadingBarConstraints) return;
CGFloat constant = UINavigationConfig.shared.vst_defaultFixSpace - UINavigationConfig.shared.vst_systemSpace;
for (NSLayoutConstraint *constraint in leadingBarConstraints) {
if (constraint.firstAttribute == NSLayoutAttributeLeading &&
constraint.secondAttribute == NSLayoutAttributeLeading) {
constraint.constant = constant;
}
}
}

- (void)vst_adjustTrailingBarConstraints {
if (UINavigationConfig.shared.vst_disableFixSpace) return;
NSArray<NSLayoutConstraint *> *trailingBarConstraints = [self valueForKey:@"_trailingBarConstraints"];
if (!trailingBarConstraints) return;
CGFloat constant = UINavigationConfig.shared.vst_systemSpace - UINavigationConfig.shared.vst_defaultFixSpace;
for (NSLayoutConstraint *constraint in trailingBarConstraints) {
if (constraint.firstAttribute == NSLayoutAttributeTrailing &&
constraint.secondAttribute == NSLayoutAttributeTrailing) {
constraint.constant = constant;
}
}
}

@end

原文demo地址

叶世昌 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!