cdts/xdts-ios 3/TreeHole/CYHResetCode/CYH/QMUIKit/QMUIComponents/QMUINavigationTitleView.m

839 lines
38 KiB
Mathematica
Raw Normal View History

2023-07-27 09:20:00 +08:00
/**
* Tencent is pleased to support the open source community by making QMUI_iOS available.
* Copyright (C) 2016-2021 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
//
// QMUINavigationTitleView.m
// qmui
//
// Created by QMUI Team on 14-7-2.
//
#import "QMUINavigationTitleView.h"
#import "QMUICore.h"
#import "UIFont+QMUI.h"
#import "UIImage+QMUI.h"
#import "UILabel+QMUI.h"
#import "UIActivityIndicatorView+QMUI.h"
#import "UIViewController+QMUI.h"
#import "UIView+QMUI.h"
#import "UINavigationItem+QMUI.h"
#import "QMUIAppearance.h"
@interface QMUINavigationTitleView ()
@property(nonatomic, strong, readonly) UIView *contentView;
@property(nonatomic, assign) CGSize titleLabelSize;
@property(nonatomic, assign) CGSize subtitleLabelSize;
@property(nonatomic, strong) UIImageView *accessoryTypeView;
@end
@implementation QMUINavigationTitleView
#pragma mark -
- (instancetype)initWithFrame:(CGRect)frame {
return [self initWithStyle:QMUINavigationTitleViewStyleDefault frame:frame];
}
- (instancetype)initWithStyle:(QMUINavigationTitleViewStyle)style {
return [self initWithStyle:style frame:CGRectZero];
}
- (instancetype)initWithStyle:(QMUINavigationTitleViewStyle)style frame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.qmui_useAsNavigationTitleView = YES;
[self addTarget:self action:@selector(handleTouchTitleViewEvent) forControlEvents:UIControlEventTouchUpInside];
_contentView = [[UIView alloc] init];
_contentView.userInteractionEnabled = NO;
[self addSubview:self.contentView];
_titleLabel = [[UILabel alloc] init];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader;
[self.contentView addSubview:self.titleLabel];
_subtitleLabel = [[UILabel alloc] init];
self.subtitleLabel.textAlignment = NSTextAlignmentCenter;
self.subtitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
self.subtitleLabel.accessibilityTraits |= UIAccessibilityTraitHeader;
[self.contentView addSubview:self.subtitleLabel];
self.userInteractionEnabled = NO;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
self.style = style;
self.needsLoadingView = NO;
self.loadingViewHidden = YES;
self.needsAccessoryPlaceholderSpace = NO;
self.needsSubAccessoryPlaceholderSpace = NO;
self.needsLoadingPlaceholderSpace = YES;
self.accessoryType = QMUINavigationTitleViewAccessoryTypeNone;
[self qmui_applyAppearance];
self.horizontalTitleFont = QMUINavigationTitleView.appearance.horizontalTitleFont ?: UINavigationBar.qmui_appearanceConfigured.titleTextAttributes[NSFontAttributeName];
self.horizontalSubtitleFont = QMUINavigationTitleView.appearance.horizontalSubtitleFont ?: self.horizontalTitleFont;
self.adjustsSubviewsTintColorAutomatically = YES;
self.tintColor = QMUICMIActivated ? NavBarTitleColor : UINavigationBar.qmui_appearanceConfigured.titleTextAttributes[NSForegroundColorAttributeName];
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@, title = %@, subtitle = %@", [super description], self.title, self.subtitle];
}
#pragma mark -
- (void)refreshLayout {
UINavigationBar *navigationBar = [self navigationBarSuperviewForSubview:self];
if (navigationBar) {
[navigationBar setNeedsLayout];
}
[self setNeedsLayout];
}
- (void)setNeedsLayout {
[self updateTitleLabelSize];
[self updateSubtitleLabelSize];
[self updateSubAccessoryViewHidden];
[super setNeedsLayout];
}
// titleView navigationBariOS 11 titleView.superview.superview == navigationBariOS 10 titleView.superview == navigationBar
- (UINavigationBar *)navigationBarSuperviewForSubview:(UIView *)subview {
if (!subview.superview) {
return nil;
}
if ([subview.superview isKindOfClass:[UINavigationBar class]]) {
return (UINavigationBar *)subview.superview;
}
return [self navigationBarSuperviewForSubview:subview.superview];
}
- (void)updateTitleLabelSize {
if (self.titleLabel.text.length > 0) {
// CGSizeCeil titleView sizeThatFits pt layoutSubviews px ...
self.titleLabelSize = CGSizeCeil([self.titleLabel sizeThatFits:CGSizeMax]);
} else {
self.titleLabelSize = CGSizeZero;
}
}
- (void)updateSubtitleLabelSize {
if (self.subtitleLabel.text.length > 0) {
// CGSizeCeil titleView sizeThatFits pt layoutSubviews px ...
self.subtitleLabelSize = CGSizeCeil([self.subtitleLabel sizeThatFits:CGSizeMax]);
} else {
self.subtitleLabelSize = CGSizeZero;
}
}
- (CGSize)loadingViewSpacingSize {
if (self.needsLoadingView && (self.needsLoadingPlaceholderSpace || !self.loadingViewHidden)) {
// title loading loading
CGSize size = CGSizeMake(self.loadingViewSize.width + self.loadingViewMarginRight, self.loadingViewSize.height);
return size;
}
return CGSizeZero;
}
- (CGSize)loadingViewSpacingSizeIfNeedsPlaceholder {
CGSize size = CGSizeMake([self loadingViewSpacingSize].width * (self.needsLoadingPlaceholderSpace ? 2 : 1), [self loadingViewSpacingSize].height);
return size;
}
- (CGSize)accessorySpacingSize {
if (self.accessoryView || self.accessoryTypeView) {
UIView *view = self.accessoryView ?: self.accessoryTypeView;
return CGSizeMake(CGRectGetWidth(view.bounds) + self.accessoryViewOffset.x, CGRectGetHeight(view.bounds));
}
return CGSizeZero;
}
- (CGSize)subAccessorySpacingSize {
if (self.subAccessoryView) {
UIView *view = self.subAccessoryView;
return CGSizeMake(CGRectGetWidth(view.frame) + self.subAccessoryViewOffset.x, CGRectGetHeight(view.frame));
}
return CGSizeZero;
}
- (CGSize)accessorySpacingSizeIfNeedesPlaceholder {
return CGSizeMake([self accessorySpacingSize].width * (self.needsAccessoryPlaceholderSpace ? 2 : 1), [self accessorySpacingSize].height);
}
- (CGSize)subAccessorySpacingSizeIfNeedesPlaceholder {
return CGSizeMake([self subAccessorySpacingSize].width * (self.needsSubAccessoryPlaceholderSpace ? 2 : 1), [self subAccessorySpacingSize].height);
}
- (UIEdgeInsets)titleEdgeInsetsIfShowingTitleLabel {
return CGSizeIsEmpty(self.titleLabelSize) ? UIEdgeInsetsZero : self.titleEdgeInsets;
}
- (UIEdgeInsets)subtitleEdgeInsetsIfShowingSubtitleLabel {
return CGSizeIsEmpty(self.subtitleLabelSize) ? UIEdgeInsetsZero : self.subtitleEdgeInsets;
}
- (CGFloat)firstLineWidthInVerticalStyle {
CGFloat firstLineWidth = self.titleLabelSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsetsIfShowingTitleLabel);
firstLineWidth += [self loadingViewSpacingSizeIfNeedsPlaceholder].width;
firstLineWidth += [self accessorySpacingSizeIfNeedesPlaceholder].width;
return firstLineWidth;
}
- (CGFloat)secondLineWidthInVerticalStyle {
CGFloat secondLineWidth = self.subtitleLabelSize.width + UIEdgeInsetsGetHorizontalValue(self.subtitleEdgeInsetsIfShowingSubtitleLabel);
if (self.subtitleLabelSize.width > 0 && self.subAccessoryView && !self.subAccessoryView.hidden) {
secondLineWidth += [self subAccessorySpacingSizeIfNeedesPlaceholder].width;
}
return secondLineWidth;
}
- (CGSize)contentSize {
if (self.style == QMUINavigationTitleViewStyleSubTitleVertical) {
CGSize size = CGSizeZero;
CGFloat firstLineWidth = [self firstLineWidthInVerticalStyle];// loadingaccessorytitleLabel
CGFloat secondLineWidth = [self secondLineWidthInVerticalStyle];
size.width = MAX(firstLineWidth, secondLineWidth);
size.height = self.titleLabelSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsetsIfShowingTitleLabel) + self.subtitleLabelSize.height + UIEdgeInsetsGetVerticalValue(self.subtitleEdgeInsetsIfShowingSubtitleLabel);// accessoryViewloadingView 便 subview titleView
return CGSizeFlatted(size);
} else {
CGSize size = CGSizeZero;
size.width = self.titleLabelSize.width + UIEdgeInsetsGetHorizontalValue(self.titleEdgeInsetsIfShowingTitleLabel) + self.subtitleLabelSize.width + UIEdgeInsetsGetHorizontalValue(self.subtitleEdgeInsetsIfShowingSubtitleLabel);
size.width += [self loadingViewSpacingSizeIfNeedsPlaceholder].width + [self accessorySpacingSizeIfNeedesPlaceholder].width;
size.height = MAX(self.titleLabelSize.height + UIEdgeInsetsGetVerticalValue(self.titleEdgeInsetsIfShowingTitleLabel), self.subtitleLabelSize.height + UIEdgeInsetsGetVerticalValue(self.subtitleEdgeInsetsIfShowingSubtitleLabel));
size.height = MAX(size.height, [self loadingViewSpacingSizeIfNeedsPlaceholder].height);
size.height = MAX(size.height, [self accessorySpacingSizeIfNeedesPlaceholder].height);
return CGSizeFlatted(size);
}
}
- (CGSize)sizeThatFits:(CGSize)size {
CGSize resultSize = [self contentSize];
resultSize.width = MIN(resultSize.width, self.maximumWidth);
return resultSize;
}
- (void)layoutSubviews {
if (CGSizeIsEmpty(self.bounds.size)) {
return;
}
[super layoutSubviews];
self.contentView.frame = self.bounds;
BOOL alignLeft = self.contentHorizontalAlignment == UIControlContentHorizontalAlignmentLeft;
BOOL alignRight = self.contentHorizontalAlignment == UIControlContentHorizontalAlignmentRight;
// sizeThatFitsize使
CGSize maxSize = self.bounds.size;
// sizemaxSize
CGSize contentSize = [self contentSize];
contentSize.width = MIN(maxSize.width, contentSize.width);
contentSize.height = MIN(maxSize.height, contentSize.height);
// titleView subviews contentHorizontalAlignment
CGFloat contentOffsetLeft = floorInPixel((maxSize.width - contentSize.width) / 2.0);
CGFloat contentOffsetRight = contentOffsetLeft;
// loading
CGFloat loadingViewSpace = [self loadingViewSpacingSize].width;
// accessoryView
UIView *accessoryView = self.accessoryView ?: self.accessoryTypeView;
// accessoryView
CGFloat accessoryViewSpace = [self accessorySpacingSize].width;
BOOL isTitleLabelShowing = self.titleLabel.text.length > 0;
BOOL isSubtitleLabelShowing = self.subtitleLabel.text.length > 0;
BOOL isSubAccessoryViewShowing = isSubtitleLabelShowing && self.subAccessoryView && !self.subAccessoryView.hidden;
UIEdgeInsets titleEdgeInsets = self.titleEdgeInsetsIfShowingTitleLabel;
UIEdgeInsets subtitleEdgeInsets = self.subtitleEdgeInsetsIfShowingSubtitleLabel;
if (self.style == QMUINavigationTitleViewStyleSubTitleVertical) {
CGFloat firstLineWidth = [self firstLineWidthInVerticalStyle];// contentSize.width MIN/MAX
CGFloat firstLineMinX = 0;
CGFloat firstLineMaxX = 0;
if (alignLeft) {
firstLineMinX = contentOffsetLeft;
} else if (alignRight) {
firstLineMinX = MAX(contentOffsetLeft, contentOffsetLeft + contentSize.width - firstLineWidth);
} else {
firstLineMinX = contentOffsetLeft + MAX(0, CGFloatGetCenter(contentSize.width, firstLineWidth));
}
firstLineMaxX = firstLineMinX + MIN(firstLineWidth, contentSize.width) - (self.needsLoadingPlaceholderSpace ? [self loadingViewSpacingSize].width : 0);
firstLineMinX += self.needsAccessoryPlaceholderSpace ? accessoryViewSpace : 0;
if (self.loadingView) {
if (self.needsLoadingPlaceholderSpace || !self.loadingView.hidden) {
self.loadingView.frame = CGRectSetXY(self.loadingView.frame, firstLineMinX, CGFloatGetCenter(self.titleLabelSize.height, self.loadingViewSize.height) + titleEdgeInsets.top);
firstLineMinX = CGRectGetMaxX(self.loadingView.frame) + self.loadingViewMarginRight;
}
}
if (accessoryView) {
accessoryView.frame = CGRectSetXY(accessoryView.frame, firstLineMaxX - CGRectGetWidth(accessoryView.frame), CGFloatGetCenter(self.titleLabelSize.height, CGRectGetHeight(accessoryView.frame)) + titleEdgeInsets.top + self.accessoryViewOffset.y);
firstLineMaxX = CGRectGetMinX(accessoryView.frame) - self.accessoryViewOffset.x;
}
if (isTitleLabelShowing) {
firstLineMinX += titleEdgeInsets.left;
firstLineMaxX -= titleEdgeInsets.right;
self.titleLabel.frame = CGRectFlatMake(firstLineMinX, titleEdgeInsets.top, firstLineMaxX - firstLineMinX, self.titleLabelSize.height);
} else {
self.titleLabel.frame = CGRectZero;
}
if (isSubtitleLabelShowing) {
CGFloat secondLineWidth = [self secondLineWidthInVerticalStyle];
CGFloat secondLineMinX = 0;
CGFloat secondLineMaxX = 0;
CGFloat secondLineMinY = subtitleEdgeInsets.top + (isTitleLabelShowing ? CGRectGetMaxY(self.titleLabel.frame) + titleEdgeInsets.bottom : 0);
if (alignLeft) {
secondLineMinX = contentOffsetLeft;
} else if (alignRight) {
secondLineMinX = MAX(contentOffsetLeft, contentOffsetLeft + contentSize.width - secondLineWidth);
} else {
secondLineMinX = contentOffsetLeft + MAX(0, CGFloatGetCenter(contentSize.width, secondLineWidth));
}
secondLineMaxX = secondLineMinX + MIN(secondLineWidth, contentSize.width);
secondLineMinX += self.needsSubAccessoryPlaceholderSpace ? [self subAccessorySpacingSize].width : 0;
if (isSubAccessoryViewShowing) {
self.subAccessoryView.frame = CGRectSetXY(self.subAccessoryView.frame, secondLineMaxX - CGRectGetWidth(self.subAccessoryView.frame), secondLineMinY + CGFloatGetCenter(self.subtitleLabelSize.height, CGRectGetHeight(self.subAccessoryView.frame)) + self.subAccessoryViewOffset.y);
secondLineMaxX = CGRectGetMinX(self.subAccessoryView.frame) - self.subAccessoryViewOffset.x;
}
self.subtitleLabel.frame = CGRectFlatMake(secondLineMinX, secondLineMinY, secondLineMaxX - secondLineMinX, self.subtitleLabelSize.height);
} else {
self.subtitleLabel.frame = CGRectZero;
}
} else {
CGFloat minX = contentOffsetLeft + (self.needsAccessoryPlaceholderSpace ? accessoryViewSpace : 0);
CGFloat maxX = maxSize.width - contentOffsetRight - (self.needsLoadingPlaceholderSpace ? loadingViewSpace : 0);
if (self.loadingView) {
if (self.needsLoadingPlaceholderSpace || !self.loadingView.hidden) {
self.loadingView.frame = CGRectSetXY(self.loadingView.frame, minX, CGFloatGetCenter(maxSize.height, self.loadingViewSize.height));
minX = CGRectGetMaxX(self.loadingView.frame) + self.loadingViewMarginRight;
}
}
if (accessoryView) {
accessoryView.frame = CGRectSetXY(accessoryView.frame, maxX - CGRectGetWidth(accessoryView.bounds), CGFloatGetCenter(maxSize.height, CGRectGetHeight(accessoryView.bounds)) + self.accessoryViewOffset.y);
maxX = CGRectGetMinX(accessoryView.frame) - self.accessoryViewOffset.x;
}
if (isSubtitleLabelShowing) {
maxX -= subtitleEdgeInsets.right;
// contentSize label center
BOOL shouldSubtitleLabelCenterVertically = self.subtitleLabelSize.height + UIEdgeInsetsGetVerticalValue(subtitleEdgeInsets) < contentSize.height;
CGFloat subtitleMinY = shouldSubtitleLabelCenterVertically ? CGFloatGetCenter(maxSize.height, self.subtitleLabelSize.height) + subtitleEdgeInsets.top - subtitleEdgeInsets.bottom : subtitleEdgeInsets.top;
self.subtitleLabel.frame = CGRectFlatMake(MAX(minX + subtitleEdgeInsets.left, maxX - self.subtitleLabelSize.width), subtitleMinY, MIN(self.subtitleLabelSize.width, maxX - minX - subtitleEdgeInsets.left), self.subtitleLabelSize.height);
maxX = CGRectGetMinX(self.subtitleLabel.frame) - subtitleEdgeInsets.left;
} else {
self.subtitleLabel.frame = CGRectZero;
}
if (isTitleLabelShowing) {
minX += titleEdgeInsets.left;
maxX -= titleEdgeInsets.right;
// contentSize label center
BOOL shouldTitleLabelCenterVertically = self.titleLabelSize.height + UIEdgeInsetsGetVerticalValue(titleEdgeInsets) < contentSize.height;
CGFloat titleLabelMinY = shouldTitleLabelCenterVertically ? CGFloatGetCenter(maxSize.height, self.titleLabelSize.height) + titleEdgeInsets.top - titleEdgeInsets.bottom : titleEdgeInsets.top;
self.titleLabel.frame = CGRectFlatMake(minX, titleLabelMinY, maxX - minX, self.titleLabelSize.height);
} else {
self.titleLabel.frame = CGRectZero;
}
}
// UIControlContentVerticalAlignmentTop contentVerticalAlignment
// UIControlContentVerticalAlignmentFill
CGFloat offsetY = CGFloatGetCenter(maxSize.height, contentSize.height);
if (self.contentVerticalAlignment == UIControlContentVerticalAlignmentTop) {
offsetY = 0;
} else if (self.contentVerticalAlignment == UIControlContentVerticalAlignmentBottom) {
offsetY = maxSize.height - contentSize.height;
}
[self.subviews enumerateObjectsUsingBlock:^(UIView *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (!CGRectIsEmpty(obj.frame)) {
obj.frame = CGRectSetY(obj.frame, CGRectGetMinY(obj.frame) + offsetY);
}
}];
}
#pragma mark - setter / getter
- (void)setMaximumWidth:(CGFloat)maximumWidth {
_maximumWidth = maximumWidth;
[self refreshLayout];
}
- (void)setContentHorizontalAlignment:(UIControlContentHorizontalAlignment)contentHorizontalAlignment {
[super setContentHorizontalAlignment:contentHorizontalAlignment];
[self refreshLayout];
}
- (void)setNeedsLoadingPlaceholderSpace:(BOOL)needsLoadingPlaceholderSpace {
_needsLoadingPlaceholderSpace = needsLoadingPlaceholderSpace;
[self refreshLayout];
}
- (void)setNeedsAccessoryPlaceholderSpace:(BOOL)needsAccessoryPlaceholderSpace {
_needsAccessoryPlaceholderSpace = needsAccessoryPlaceholderSpace;
[self refreshLayout];
}
- (void)setAccessoryViewOffset:(CGPoint)accessoryViewOffset {
_accessoryViewOffset = accessoryViewOffset;
[self refreshLayout];
}
- (void)setNeedsSubAccessoryPlaceholderSpace:(BOOL)needsSubAccessoryPlaceholderSpace {
_needsSubAccessoryPlaceholderSpace = needsSubAccessoryPlaceholderSpace;
[self refreshLayout];
}
- (void)setSubAccessoryViewOffset:(CGPoint)subAccessoryViewOffset {
_subAccessoryViewOffset = subAccessoryViewOffset;
[self refreshLayout];
}
- (void)setLoadingViewMarginRight:(CGFloat)loadingViewMarginRight {
_loadingViewMarginRight = loadingViewMarginRight;
[self refreshLayout];
}
- (void)setHorizontalTitleFont:(UIFont *)horizontalTitleFont {
_horizontalTitleFont = horizontalTitleFont;
if (self.style == QMUINavigationTitleViewStyleDefault) {
self.titleLabel.font = horizontalTitleFont;
[self refreshLayout];
}
}
- (void)setHorizontalSubtitleFont:(UIFont *)horizontalSubtitleFont {
_horizontalSubtitleFont = horizontalSubtitleFont;
if (self.style == QMUINavigationTitleViewStyleDefault) {
self.subtitleLabel.font = horizontalSubtitleFont;
[self refreshLayout];
}
}
- (void)setVerticalTitleFont:(UIFont *)verticalTitleFont {
_verticalTitleFont = verticalTitleFont;
if (self.style == QMUINavigationTitleViewStyleSubTitleVertical) {
self.titleLabel.font = verticalTitleFont;
[self refreshLayout];
}
}
- (void)setVerticalSubtitleFont:(UIFont *)verticalSubtitleFont {
_verticalSubtitleFont = verticalSubtitleFont;
if (self.style == QMUINavigationTitleViewStyleSubTitleVertical) {
self.subtitleLabel.font = verticalSubtitleFont;
[self refreshLayout];
}
}
- (void)setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
_titleEdgeInsets = titleEdgeInsets;
[self refreshLayout];
}
- (void)setSubtitleEdgeInsets:(UIEdgeInsets)subtitleEdgeInsets {
_subtitleEdgeInsets = subtitleEdgeInsets;
[self refreshLayout];
}
- (void)setTitle:(NSString *)title {
_title = title;
self.titleLabel.text = title;
[self refreshLayout];
}
- (void)setSubtitle:(NSString *)subtitle {
_subtitle = subtitle;
self.subtitleLabel.text = subtitle;
[self refreshLayout];
}
- (void)setAccessoryType:(QMUINavigationTitleViewAccessoryType)accessoryType {
// accessoryViewaccessoryType
if (self.accessoryView) {
accessoryType = QMUINavigationTitleViewAccessoryTypeNone;
}
_accessoryType = accessoryType;
if (accessoryType == QMUINavigationTitleViewAccessoryTypeNone) {
[self.accessoryTypeView removeFromSuperview];
self.accessoryTypeView = nil;
[self refreshLayout];
return;
}
if (!self.accessoryTypeView) {
self.accessoryTypeView = [[UIImageView alloc] init];
self.accessoryTypeView.contentMode = UIViewContentModeCenter;
}
UIImage *accessoryImage = nil;
if (accessoryType == QMUINavigationTitleViewAccessoryTypeDisclosureIndicator) {
accessoryImage = [NavBarAccessoryViewTypeDisclosureIndicatorImage qmui_imageWithOrientation:UIImageOrientationUp];
}
self.accessoryTypeView.image = accessoryImage;
[self.accessoryTypeView sizeToFit];
// setImage sizeToFit addSubview addSubview sizeThatFits:
if (self.accessoryTypeView.superview != self) {
[self.contentView addSubview:self.accessoryTypeView];
}
[self refreshLayout];
}
- (void)setAccessoryView:(UIView *)accessoryView {
if (_accessoryView != accessoryView) {
[_accessoryView removeFromSuperview];
_accessoryView = nil;
}
if (accessoryView) {
_accessoryView = accessoryView;
self.accessoryType = QMUINavigationTitleViewAccessoryTypeNone;
[self.accessoryView sizeToFit];
[self.contentView addSubview:self.accessoryView];
}
[self refreshLayout];
}
- (void)setSubAccessoryView:(UIView *)subAccessoryView {
if (_subAccessoryView != subAccessoryView) {
[_subAccessoryView removeFromSuperview];
_subAccessoryView = nil;
}
if (subAccessoryView) {
_subAccessoryView = subAccessoryView;
[self.subAccessoryView sizeToFit];
[self.contentView addSubview:self.subAccessoryView];
}
[self refreshLayout];
}
- (void)updateSubAccessoryViewHidden {
if (self.subAccessoryView && self.subtitleLabel.text.length && self.style == QMUINavigationTitleViewStyleSubTitleVertical) {
self.subAccessoryView.hidden = NO;
} else {
self.subAccessoryView.hidden = YES;
}
}
- (void)setNeedsLoadingView:(BOOL)needsLoadingView {
_needsLoadingView = needsLoadingView;
if (needsLoadingView) {
if (!self.loadingView) {
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:NavBarActivityIndicatorViewStyle size:self.loadingViewSize];
self.loadingView.color = self.tintColor;
[self.loadingView stopAnimating];
[self.contentView addSubview:self.loadingView];
}
} else {
if (self.loadingView) {
[self.loadingView stopAnimating];
[self.loadingView removeFromSuperview];
_loadingView = nil;
}
}
[self refreshLayout];
}
- (void)setLoadingViewHidden:(BOOL)loadingViewHidden {
_loadingViewHidden = loadingViewHidden;
if (self.needsLoadingView) {
loadingViewHidden ? [self.loadingView stopAnimating] : [self.loadingView startAnimating];
}
[self refreshLayout];
}
- (void)setLoadingViewSize:(CGSize)loadingViewSize {
_loadingViewSize = loadingViewSize;
if (self.loadingView) {
self.loadingView.qmui_size = loadingViewSize;
[self refreshLayout];
}
}
- (void)setActive:(BOOL)active {
_active = active;
if ([self.delegate respondsToSelector:@selector(didChangedActive:forTitleView:)]) {
[self.delegate didChangedActive:active forTitleView:self];
}
if (self.accessoryType == QMUINavigationTitleViewAccessoryTypeDisclosureIndicator) {
if (active) {
[UIView animateWithDuration:.25f delay:0 options:QMUIViewAnimationOptionsCurveIn animations:^(void){
self.accessoryTypeView.transform = CGAffineTransformMakeRotation(AngleWithDegrees(-180));
} completion:^(BOOL finished) {
}];
} else {
[UIView animateWithDuration:.25f delay:0 options:QMUIViewAnimationOptionsCurveIn animations:^(void){
self.accessoryTypeView.transform = CGAffineTransformMakeRotation(AngleWithDegrees(0.1));
} completion:^(BOOL finished) {
}];
}
}
}
#pragma mark - Style & Type
- (void)setStyle:(QMUINavigationTitleViewStyle)style {
_style = style;
if (style == QMUINavigationTitleViewStyleSubTitleVertical) {
self.titleLabel.font = self.verticalTitleFont;
self.subtitleLabel.font = self.verticalSubtitleFont;
} else {
self.titleLabel.font = self.horizontalTitleFont;
self.subtitleLabel.font = self.horizontalSubtitleFont;
}
[self refreshLayout];
}
- (void)tintColorDidChange {
[super tintColorDidChange];
if (self.adjustsSubviewsTintColorAutomatically) {
UIColor *color = self.tintColor;
self.titleLabel.textColor = color;
self.subtitleLabel.textColor = color;
self.loadingView.color = color;
}
}
#pragma mark - Events
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
self.alpha = highlighted ? UIControlHighlightedAlpha : 1;
}
- (void)handleTouchTitleViewEvent {
BOOL active = !self.active;
if ([self.delegate respondsToSelector:@selector(didTouchTitleView:isActive:)]) {
[self.delegate didTouchTitleView:self isActive:active];
}
self.active = active;
[self refreshLayout];
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// -[UIViewController setTitle:] QMUINavigationTitle
OverrideImplementation([UIViewController class], @selector(setTitle:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIViewController *selfObject, NSString *title) {
// call super
void (*originSelectorIMP)(id, SEL, NSString *);
originSelectorIMP = (void (*)(id, SEL, NSString *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, title);
if ([selfObject.navigationItem.titleView isKindOfClass:QMUINavigationTitleView.class]) {
((QMUINavigationTitleView *)selfObject.navigationItem.titleView).title = title;
}
};
});
// -[UINavigationItem setTitle:] QMUINavigationTitleView
OverrideImplementation([UINavigationItem class], @selector(setTitle:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationItem *selfObject, NSString *title) {
// call super
void (*originSelectorIMP)(id, SEL, NSString *);
originSelectorIMP = (void (*)(id, SEL, NSString *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, title);
if ([selfObject.titleView isKindOfClass:QMUINavigationTitleView.class]) {
((QMUINavigationTitleView *)selfObject.titleView).title = title;
}
};
});
// title titleView titleView title
OverrideImplementation([UINavigationItem class], @selector(setTitleView:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationItem *selfObject, QMUINavigationTitleView *titleView) {
// call super
void (*originSelectorIMP)(id, SEL, UIView *);
originSelectorIMP = (void (*)(id, SEL, UIView *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, titleView);
if ([titleView isKindOfClass:QMUINavigationTitleView.class]) {
if (titleView.title.length <= 0) {
NSString *title = selfObject.qmui_viewController.title ?: selfObject.title;
titleView.title = title;
}
}
};
});
});
}
@end
@interface QMUINavigationTitleView (UIAppearance)
@end
@implementation QMUINavigationTitleView (UIAppearance)
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self setDefaultAppearance];
});
}
+ (void)setDefaultAppearance {
QMUINavigationTitleView *appearance = [QMUINavigationTitleView appearance];
appearance.adjustsSubviewsTintColorAutomatically = YES;
appearance.maximumWidth = CGFLOAT_MAX;
appearance.loadingViewSize = CGSizeMake(18, 18);
appearance.loadingViewMarginRight = 3;
appearance.horizontalTitleFont = NavBarTitleFont;
appearance.horizontalSubtitleFont = NavBarTitleFont;
appearance.verticalTitleFont = UIFontMake(15);
appearance.verticalSubtitleFont = UIFontLightMake(12);
appearance.accessoryViewOffset = CGPointMake(3, 0);
appearance.subAccessoryViewOffset = CGPointMake(3, 0);
appearance.titleEdgeInsets = UIEdgeInsetsZero;
appearance.subtitleEdgeInsets = UIEdgeInsetsZero;
}
@end
#pragma mark - LargeTitle
@implementation QMUINavigationTitleView (LargeTitleCompatibility)
- (void)setAlpha:(BOOL)alpha animated:(BOOL)animated {
// push pop titleView alpha alpha 0 1 contentView alpha QMUINavigationTitleView
[UIView qmui_animateWithAnimated:animated duration:0.25f animations:^{
self.contentView.alpha = alpha;
}];
}
@end
@implementation UINavigationBar (LargeTitleCompatibility)
- (UIView *)qmui_largeTitleView {
for (UIView *subview in self.subviews) {
if ([NSStringFromClass(subview.class) hasSuffix:@"LargeTitleView"]) {
return subview;
}
}
return nil;
}
@end
@implementation UINavigationController(LargeTitleCompatibility)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// -[UINavigationController _updateTopViewFramesToMatchScrollOffsetInViewController:contentScrollView:topLayoutType:]
OverrideImplementation([UINavigationController class], sel_registerName("_updateTopViewFramesToMatchScrollOffsetInViewController:contentScrollView:topLayoutType:"), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationController *selfObject, UIViewController *viewController, UIScrollView *scrollView, NSUInteger topLayoutType) {
// call super
void (*originSelectorIMP)(id, SEL, UIViewController *, UIScrollView *, NSUInteger);
originSelectorIMP = (void (*)(id, SEL, UIViewController *, UIScrollView *, NSUInteger))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, viewController, scrollView, topLayoutType);
[selfObject qmui_updateTitleViewToMatchScrollOffsetInViewController:viewController contentScrollView:scrollView topLayoutType:topLayoutType];
};
});
});
}
- (void)qmui_updateTitleViewToMatchScrollOffsetInViewController:(UIViewController *)viewController contentScrollView:(UIScrollView *)contentScrollView topLayoutType:(NSInteger)topLayoutType {
UIView *titleView = viewController.navigationItem.titleView;
if (!titleView || ![titleView isKindOfClass:[QMUINavigationTitleView class]]) {
return;
}
QMUIAssert(viewController.navigationController == self, @"QMUINavigationTitleView", @"不存在 UINavigationController");
QMUINavigationTitleView *navigationTitleView = (QMUINavigationTitleView *)titleView;
UIView *largeTitleView = self.navigationBar.qmui_largeTitleView;
BOOL largeTitleLabelVisable = self.navigationBar.prefersLargeTitles && viewController.qmui_prefersLargeTitleDisplayed && largeTitleView.alpha != 0;
BOOL titleViewAlpha = largeTitleLabelVisable ? 0 : 1;
BOOL animated = contentScrollView.layer.presentationLayer && !CGRectEqualToRect(contentScrollView.layer.presentationLayer.bounds, contentScrollView.layer.bounds);
[navigationTitleView setAlpha:titleViewAlpha animated:animated];
}
@end
@implementation UIView (QMUINavigationTitleView)
static char kAssociatedObjectKey_useAsNavigationTitleView;
- (void)setQmui_useAsNavigationTitleView:(BOOL)useAsNavigationTitleView {
objc_setAssociatedObject(self, &kAssociatedObjectKey_useAsNavigationTitleView, @(useAsNavigationTitleView), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (useAsNavigationTitleView) {
[QMUIHelper executeBlock:^{
// 使 titleView
OverrideImplementation([UINavigationBar class], @selector(layoutSubviews), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UINavigationBar *selfObject) {
UIView *titleView = selfObject.topItem.titleView;
if (titleView.qmui_useAsNavigationTitleView) {
CGFloat titleViewMaximumWidth = CGRectGetWidth(titleView.bounds);// titleViewUINavigationBar
CGSize titleViewSize = [titleView sizeThatFits:CGSizeMake(titleViewMaximumWidth, CGFLOAT_MAX)];
titleViewSize.height = ceil(titleViewSize.height);// titleViewptyptpushtitleView
// UINavigationBar使titleViewtitleViewsizeThatFits:navigationBary
if (CGRectGetHeight(titleView.bounds) != titleViewSize.height) {
CGFloat titleViewMinY = flat(CGRectGetMinY(titleView.frame) - ((titleViewSize.height - CGRectGetHeight(titleView.bounds)) / 2.0));// titleViewyflat
titleView.frame = CGRectMake(CGRectGetMinX(titleView.frame), titleViewMinY, MIN(titleViewMaximumWidth, titleViewSize.width), titleViewSize.height);
}
// iOS 11 iOS 11 Beta 5 titleView titleView
if (CGRectGetWidth(titleView.bounds) != titleViewSize.width) {
titleView.frame = CGRectSetWidth(titleView.frame, titleViewSize.width);
}
}
// call super
void (*originSelectorIMP)(id, SEL);
originSelectorIMP = (void (*)(id, SEL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD);
};
});
} oncePerIdentifier:@"UIView (QMUINavigationTitleView)"];
}
}
- (BOOL)qmui_useAsNavigationTitleView {
return [((NSNumber *)objc_getAssociatedObject(self, &kAssociatedObjectKey_useAsNavigationTitleView)) boolValue];
}
@end