iOS11 剛發(fā)布, 現(xiàn)在正忙著適配, 由于導(dǎo)航欄的層級發(fā)生了改變, 所以之前使用UIBarButtonSystemItemFixedSpace的方式設(shè)置導(dǎo)航欄左/右按鈕間距為0的方法在iOS11失效了, 小編在網(wǎng)上找了兩天, 嘗試了各種方法, 最后總結(jié)出適配的方法, 如有bug歡迎評論...
方案一
通過更改系統(tǒng)約束的方法來取消_UIButtonBarStackView的12和_UITAMICAdaptorView的8的間距
這是小編最初使用的方法
缺點(diǎn): 改變系統(tǒng)私有API, 個(gè)人感覺不太穩(wěn)妥
- 自定義UINavigationController, 實(shí)現(xiàn)UINavigationControllerDelegate方法
#pragma mark - UINavigationControllerDelegate
// 每次viewController將要顯示時(shí)都會(huì)調(diào)用此方法
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if (self.viewControllers.count > 1) {
/// 需要在0.01秒后調(diào)用, 否則可能獲取不到UIStackView
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self resetNavigationBarItemSpaces];
});
}
}
- (void)resetNavigationBarItemSpaces {
if ([[UIDevice currentDevice].systemVersion floatValue] < 11.0) { return; }
for (UIView *barContentView in self.navigationBar.subviews) {
if ([NSStringFromClass([barContentView class]) isEqualToString:@"_UINavigationBarContentView"]) {
for (NSLayoutConstraint *constraint in barContentView.constraints) {
if ([NSStringFromClass([constraint.secondItem class]) isEqualToString:@"_UINavigationBarContentView"] && constraint.firstAttribute == NSLayoutAttributeTrailing) {
// 移除UIStackView左/右邊12的間距
if ( constraint.secondAttribute == NSLayoutAttributeTrailing) {
[barContentView removeConstraint:constraint];
} else if (constraint.secondAttribute == NSLayoutAttributeLeading) {
[barContentView removeConstraint:constraint];
}
}
}
for (UIView *stackView in barContentView.subviews) {
if ([stackView isKindOfClass:[UIStackView class]]) {
if (CGRectGetMinX(stackView.frame) < kScreen_Width * 0.5) {
[stackView.superview addConstraint:[NSLayoutConstraint constraintWithItem:stackView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:stackView.superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
} else {
[stackView.superview addConstraint:[NSLayoutConstraint constraintWithItem:stackView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:stackView.superview attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
}
// 移除UITAMICAdaptorView左/右邊8的間距(bug:當(dāng)同一邊有多個(gè)item時(shí),其之間也沒有間距)
for (NSLayoutConstraint *constraint in stackView.constraints) {
if ([constraint.firstItem isKindOfClass:[UILayoutGuide class]]) {
if (constraint.firstAttribute == NSLayoutAttributeWidth && !constraint.secondItem) {
[stackView removeConstraint:constraint];
break;
}
}
}
}
}
break;
}
}
}
方案二
改變button的內(nèi)容偏移和響應(yīng)區(qū)域, 此方案同樣適用iOS11之前
1. 設(shè)置button的內(nèi)容偏移
- 自定義UINavigationController, 實(shí)現(xiàn)UINavigationControllerDelegate方法;
- 在- (void)navigationController: willShowViewController: animated:方法中設(shè)置button的內(nèi)容偏移; 你也可以在button創(chuàng)建的時(shí)候設(shè)置, 但需要在每個(gè)自定義UIBarButtonItem的控制器中都寫一遍偏移的代碼;
- 優(yōu)點(diǎn): 可以改變所有控制器的navigationItem的間距, 便于維護(hù)
- 缺點(diǎn): 每次控制器將要顯示時(shí)都要設(shè)置button的內(nèi)容偏移; 但也只是調(diào)用一次, 對性能影響不大, 所以本人選擇在此設(shè)置button的內(nèi)容偏移
#pragma mark - UINavigationControllerDelegate
// 每次viewController將要顯示時(shí)都會(huì)調(diào)用此方法
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[self resetBarItemSpacesWithController:viewController];
}
- (void)resetBarItemSpacesWithController:(UIViewController *)viewController {
CGFloat space = kScreen_Width > 375 ? 20 : 16;
for (UIBarButtonItem *buttonItem in viewController.navigationItem.leftBarButtonItems) {
if (buttonItem.customView == nil) { continue; }
/// 根據(jù)實(shí)際情況(自己項(xiàng)目UIBarButtonItem的層級)獲取button
UIButton *itemBtn = nil;
if ([buttonItem.customView isKindOfClass:[UIButton class]]) {
itemBtn = (UIButton *)buttonItem.customView;
} else if ([buttonItem.customView isKindOfClass:[UIView class]]) {
itemBtn = (UIButton *)buttonItem.customView.subviews.firstObject;
}
/// 設(shè)置button圖片/文字偏移
itemBtn.contentEdgeInsets = UIEdgeInsetsMake(0, -space,0, 0);
itemBtn.imageEdgeInsets = UIEdgeInsetsMake(0, -space,0, 0);
itemBtn.titleEdgeInsets = UIEdgeInsetsMake(0, -space,0, 0);
/// 改變button事件響應(yīng)區(qū)域
itemBtn.hitEdgeInsets = UIEdgeInsetsMake(0, -space, 0, space);
}
for (UIBarButtonItem *buttonItem in viewController.navigationItem.rightBarButtonItems) {
if (buttonItem.customView == nil) { continue; }
UIButton *itemBtn = nil;
if ([buttonItem.customView isKindOfClass:[UIButton class]]) {
itemBtn = (UIButton *)buttonItem.customView;
} else if ([buttonItem.customView isKindOfClass:[UIView class]]) {
itemBtn = (UIButton *)buttonItem.customView.subviews.firstObject;
}
itemBtn.contentEdgeInsets = UIEdgeInsetsMake(0, 0,0, -space);
itemBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0,0, -space);
itemBtn.titleEdgeInsets = UIEdgeInsetsMake(0, 0,0, -space);
itemBtn.hitEdgeInsets = UIEdgeInsetsMake(0, space, 0, -space);
}
}
2. 改變button的響應(yīng)區(qū)域
此處借鑒UIButton 擴(kuò)大按鈕的響應(yīng)區(qū)域但小編做了些許修改
- 創(chuàng)建UINavigationBar, 重寫- (UIView *)hitTest: withEvent:方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if (point.x<= kScreen_Width * 0.5) {
for (UIBarButtonItem *buttonItem in self.topItem.leftBarButtonItems) {
if (buttonItem.customView == nil) { continue; }
/// 根據(jù)實(shí)際情況(自己項(xiàng)目UIBarButtonItem的層級)獲取button
UIButton *itemBtn = nil;
if ([buttonItem.customView isKindOfClass:[UIButton class]]) {
itemBtn = (UIButton *)buttonItem.customView;
} else if ([buttonItem.customView isKindOfClass:[UIView class]]) {
itemBtn = (UIButton *)buttonItem.customView.subviews.firstObject;
}
/// 將button的坐標(biāo)系從父視圖轉(zhuǎn)化到UINavigationBar上
CGRect newRect = [itemBtn convertRect:[itemBtn hitFrame] toView:self];
/// 如果觸摸點(diǎn)在button的響應(yīng)范圍內(nèi),讓button響應(yīng)此次觸摸事件
if (CGRectContainsPoint(newRect, point)) {
view = itemBtn;
break;
}
}
} else {
for (UIBarButtonItem *buttonItem in self.topItem.rightBarButtonItems) {
if (buttonItem.customView == nil) { continue; }
UIButton *itemBtn = nil;
if ([buttonItem.customView isKindOfClass:[UIButton class]]) {
itemBtn = (UIButton *)buttonItem.customView;
} else if ([buttonItem.customView isKindOfClass:[UIView class]]) {
itemBtn = (UIButton *)buttonItem.customView.subviews.firstObject;
}
CGRect newRect = [itemBtn convertRect:[itemBtn hitFrame] toView:self];
if (CGRectContainsPoint(newRect, point)) {
view = itemBtn;
break;
}
}
}
return view;
}