This commit is contained in:
2025-11-11 14:56:57 +08:00
parent 57bd4ba109
commit 17b8bf2bfd
6 changed files with 556 additions and 1 deletions

View File

@@ -6,6 +6,7 @@
#import "KBMyHeaderView.h"
#import <Masonry/Masonry.h>
#import "UIColor+Extension.h"
#import "KBPersonInfoVC.h"
@interface KBMyHeaderView ()
@property (nonatomic, strong) UILabel *titleLabel;
@@ -193,7 +194,10 @@
#pragma mark - Actions
- (void)onKeyboardTap { if (self.onKeyboardTapped) { self.onKeyboardTapped(); } }
- (void)onAvatarTap { if (self.onAvatarTapped) { self.onAvatarTapped(); } }
- (void)onAvatarTap {
KBPersonInfoVC *vc = [[KBPersonInfoVC alloc] init];
[KB_CURRENT_NAV pushViewController:vc animated:true];
}
- (void)onLeftCardTap { if (self.onLeftCardTapped) { self.onLeftCardTapped(); } }
- (void)onRightCardTap { if (self.onRightCardTapped) { self.onRightCardTapped(); } }

View File

@@ -0,0 +1,26 @@
//
// KBPersonInfoItemCell.h
// keyBoard
//
// Created by Codex on 2025/11/11.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// 个人资料页面的条目 Cell左标题右文案/箭头/复制按钮)
@interface KBPersonInfoItemCell : UITableViewCell
/// 配置
- (void)configWithTitle:(NSString *)title
value:(NSString *)value
showArrow:(BOOL)showArrow
showCopy:(BOOL)showCopy
isTop:(BOOL)isTop
isBottom:(BOOL)isBottom;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,250 @@
//
// KBPersonInfoItemCell.m
// keyBoard
//
// Created by Codex on 2025/11/11.
//
@import UIKit;
#import "KBPersonInfoItemCell.h"
#import <Masonry/Masonry.h>
@interface KBPersonInfoItemCell ()
//
@property (nonatomic, strong) UIView *cardView;
//
@property (nonatomic, strong) UILabel *titleLabel;
//
@property (nonatomic, strong) UILabel *valueLabel;
// >
@property (nonatomic, strong) UIImageView *arrowView;
//
@property (nonatomic, strong) UIButton *copyBtn;
// 线
@property (nonatomic, strong) UIView *line;
// layoutSubviews mask
@property (nonatomic, assign) UIRectCorner cornerRecord;
@end
@implementation KBPersonInfoItemCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
self.backgroundColor = UIColor.clearColor;
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self.contentView addSubview:self.cardView];
[self.cardView addSubview:self.titleLabel];
[self.cardView addSubview:self.valueLabel];
[self.cardView addSubview:self.arrowView];
[self.cardView addSubview:self.copyBtn];
[self.cardView addSubview:self.line];
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.contentView).offset(16);
make.right.equalTo(self.contentView).offset(-16);
make.top.equalTo(self.contentView);
make.bottom.equalTo(self.contentView);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.cardView).offset(16);
make.centerY.equalTo(self.cardView);
}];
[self.arrowView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.cardView).offset(-16);
make.centerY.equalTo(self.cardView);
make.width.mas_equalTo(10);
make.height.mas_equalTo(16);
}];
[self.copyBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.cardView);
make.right.equalTo(self.cardView).offset(-16);
make.width.height.mas_equalTo(22);
}];
[self.valueLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.cardView);
make.right.equalTo(self.arrowView.mas_left).offset(-8);
}];
[self.line mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.cardView).offset(16);
make.right.equalTo(self.cardView).offset(-16);
make.bottom.equalTo(self.cardView);
make.height.mas_equalTo(0.5);
}];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
//
self.cardView.layer.cornerRadius = 12.0;
// corner mask
if (self.cornerRecord != 0 && !CGRectIsEmpty(self.cardView.bounds)) {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.cardView.bounds
byRoundingCorners:self.cornerRecord
cornerRadii:CGSizeMake(12, 12)];
CAShapeLayer *mask = [CAShapeLayer layer];
mask.frame = self.cardView.bounds; mask.path = path.CGPath;
self.cardView.layer.mask = mask;
} else {
self.cardView.layer.mask = nil;
}
}
- (void)prepareForReuse {
[super prepareForReuse];
self.arrowView.hidden = YES; self.copyBtn.hidden = YES;
self.line.hidden = NO;
}
- (void)configWithTitle:(NSString *)title
value:(NSString *)value
showArrow:(BOOL)showArrow
showCopy:(BOOL)showCopy
isTop:(BOOL)isTop
isBottom:(BOOL)isBottom {
self.titleLabel.text = title ?: @"";
self.valueLabel.text = value ?: @"";
self.arrowView.hidden = !showArrow;
self.copyBtn.hidden = !showCopy;
self.valueLabel.textColor = showArrow ? [UIColor blackColor] : [UIColor colorWithWhite:0.2 alpha:1.0];
//
UIView *rightAnchor = showCopy ? self.copyBtn : self.arrowView;
[self.valueLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self.cardView);
make.right.equalTo(rightAnchor.mas_left).offset(-8);
}];
// / + 线
UIRectCorner corners = 0;
if (isTop && isBottom) {
corners = UIRectCornerAllCorners;
self.line.hidden = YES;
} else if (isTop) {
corners = UIRectCornerTopLeft | UIRectCornerTopRight;
self.line.hidden = NO;
} else if (isBottom) {
corners = UIRectCornerBottomLeft | UIRectCornerBottomRight;
self.line.hidden = YES; // 线
} else {
corners = 0; self.line.hidden = NO;
}
// 使
// layoutSubviews mask
self.cornerRecord = corners;
[self setNeedsLayout];
}
#pragma mark - Lazy UI
- (UIView *)cardView {
if (!_cardView) {
_cardView = [UIView new];
_cardView.backgroundColor = UIColor.whiteColor;
_cardView.layer.cornerRadius = 12.0;
_cardView.layer.masksToBounds = YES; //
}
return _cardView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [UILabel new];
_titleLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
_titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightRegular];
}
return _titleLabel;
}
- (UILabel *)valueLabel {
if (!_valueLabel) {
_valueLabel = [UILabel new];
_valueLabel.textColor = [UIColor blackColor];
_valueLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_valueLabel.textAlignment = NSTextAlignmentRight;
[_valueLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
}
return _valueLabel;
}
- (UIImageView *)arrowView {
if (!_arrowView) {
_arrowView = [[UIImageView alloc] init];
_arrowView.contentMode = UIViewContentModeScaleAspectFit;
UIImage *img = nil;
if (@available(iOS 13.0, *)) {
img = [UIImage systemImageNamed:@"chevron.right"];
}
if (!img) {
//
CGSize size = CGSizeMake(10, 16);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
UIBezierPath *p = [UIBezierPath bezierPath];
[p moveToPoint:CGPointMake(2, 2)];
[p addLineToPoint:CGPointMake(8, 8)];
[p addLineToPoint:CGPointMake(2, 14)];
[[UIColor colorWithWhite:0.7 alpha:1] setStroke];
p.lineWidth = 2; [p stroke];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
_arrowView.image = img;
_arrowView.tintColor = [UIColor colorWithWhite:0.7 alpha:1];
_arrowView.hidden = YES;
}
return _arrowView;
}
- (UIButton *)copyBtn {
if (!_copyBtn) {
_copyBtn = [UIButton buttonWithType:UIButtonTypeSystem];
UIImage *img = nil;
if (@available(iOS 13.0, *)) {
img = [UIImage systemImageNamed:@"doc.on.doc"];
}
if (!img) {
// copy
CGSize sz = CGSizeMake(22, 22);
UIGraphicsBeginImageContextWithOptions(sz, NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor colorWithWhite:0.85 alpha:1] setStroke];
CGContextSetLineWidth(ctx, 2);
CGContextStrokeRect(ctx, CGRectMake(6, 6, 12, 12));
CGContextStrokeRect(ctx, CGRectMake(4, 4, 12, 12));
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
[_copyBtn setImage:img forState:UIControlStateNormal];
_copyBtn.tintColor = [UIColor colorWithWhite:0.6 alpha:1];
_copyBtn.hidden = YES;
[_copyBtn addTarget:self action:@selector(onCopy) forControlEvents:UIControlEventTouchUpInside];
}
return _copyBtn;
}
- (UIView *)line {
if (!_line) {
_line = [UIView new];
_line.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
}
return _line;
}
#pragma mark - Event
- (void)onCopy {
// valueLabel
NSString *txt = self.valueLabel.text ?: @"";
if (txt.length == 0) return;
UIPasteboard.generalPasteboard.string = txt;
}
@end

View File

@@ -0,0 +1,16 @@
//
// KBPersonInfoVC.h
// keyBoard
//
// Created by Mac on 2025/11/11.
// 个人资料
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KBPersonInfoVC : BaseViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,247 @@
//
// KBPersonInfoVC.m
// keyBoard
//
// Created by Mac on 2025/11/11.
//
//
// KBPersonInfoVC.m
//
//
#import "KBPersonInfoVC.h"
#import <Masonry/Masonry.h>
#import "KBPersonInfoItemCell.h"
@interface KBPersonInfoVC () <UITableViewDelegate, UITableViewDataSource>
//
@property (nonatomic, strong) BaseTableView *tableView; //
//
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIImageView *avatarView; //
@property (nonatomic, strong) UIButton *editBadge; //
@property (nonatomic, strong) UILabel *modifyLabel; // Modify
// 退
@property (nonatomic, strong) UIView *footerView;
@property (nonatomic, strong) UIView *logoutBg;
@property (nonatomic, strong) UIButton *logoutBtn;
//
@property (nonatomic, copy) NSArray<NSDictionary *> *items; // {title,value,arrow,copy}
@end
@implementation KBPersonInfoVC
- (void)viewDidLoad {
[super viewDidLoad];
self.kb_titleLabel.text = @"Settings"; //
self.kb_navView.backgroundColor = [UIColor clearColor];
self.view.backgroundColor = [UIColor colorWithHex:0xF8F8F8];
//
self.items = @[
@{ @"title": @"Nickname", @"value": @"Nickname", @"arrow": @YES, @"copy": @NO },
@{ @"title": @"Gender", @"value": @"Choose", @"arrow": @YES, @"copy": @NO },
@{ @"title": @"User ID", @"value": @"8888888", @"arrow": @NO, @"copy": @YES },
];
[self.view addSubview:self.tableView];
[self.tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self.view);
make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT + 10);
}];
// &
self.tableView.tableHeaderView = self.headerView;
self.tableView.tableFooterView = self.footerView;
}
#pragma mark - UITableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.items.count; }
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 56.0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 12.0; }
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return [UIView new]; }
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 0.01; }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cid = @"KBPersonInfoItemCell";
KBPersonInfoItemCell *cell = [tableView dequeueReusableCellWithIdentifier:cid];
if (!cell) { cell = [[KBPersonInfoItemCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cid]; }
NSDictionary *it = self.items[indexPath.row];
BOOL isTop = (indexPath.row == 0);
BOOL isBottom = (indexPath.row == self.items.count - 1);
[cell configWithTitle:it[@"title"]
value:it[@"value"]
showArrow:[it[@"arrow"] boolValue]
showCopy:[it[@"copy"] boolValue]
isTop:isTop
isBottom:isBottom];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// / ID
if (indexPath.row == 0) {
// TODO:
} else if (indexPath.row == 1) {
// TODO:
}
}
#pragma mark - Actions
- (void)onTapAvatarEdit {
//
}
- (void)onTapLogout {
// 退
}
#pragma mark - Lazy UI
- (UITableView *)tableView {
if (!_tableView) {
// 使 Plain
_tableView = [[BaseTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.backgroundColor = UIColor.clearColor;
_tableView.showsVerticalScrollIndicator = NO;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone; //
_tableView.dataSource = self; _tableView.delegate = self;
_tableView.contentInset = UIEdgeInsetsMake(8, 0, 16, 0);
}
return _tableView;
}
- (UIView *)headerView {
if (!_headerView) {
CGFloat w = UIScreen.mainScreen.bounds.size.width;
UIView *hv = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, 180)];
hv.backgroundColor = UIColor.clearColor;
[hv addSubview:self.avatarView];
[hv addSubview:self.editBadge];
[hv addSubview:self.modifyLabel];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(hv);
make.top.equalTo(hv).offset(12);
make.width.height.mas_equalTo(96);
}];
[self.editBadge mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.height.mas_equalTo(24);
make.centerX.equalTo(self.avatarView.mas_right).offset(-8);
make.centerY.equalTo(self.avatarView.mas_bottom).offset(-8);
}];
[self.modifyLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.avatarView.mas_bottom).offset(10);
make.centerX.equalTo(hv);
}];
_headerView = hv;
}
return _headerView;
}
- (UIImageView *)avatarView {
if (!_avatarView) {
_avatarView = [[UIImageView alloc] init];
_avatarView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
_avatarView.layer.cornerRadius = 48; _avatarView.layer.masksToBounds = YES;
//
_avatarView.layer.borderWidth = 3.0; _avatarView.layer.borderColor = UIColor.whiteColor.CGColor;
//
UIGraphicsBeginImageContextWithOptions(CGSizeMake(96, 96), NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor colorWithRed:0.86 green:0.95 blue:0.90 alpha:1] setFill];
CGContextFillEllipseInRect(ctx, CGRectMake(0, 0, 96, 96));
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_avatarView.image = img;
_avatarView.userInteractionEnabled = YES;
}
return _avatarView;
}
- (UIButton *)editBadge {
if (!_editBadge) {
_editBadge = [UIButton buttonWithType:UIButtonTypeCustom];
_editBadge.backgroundColor = [UIColor colorWithRed:0.02 green:0.75 blue:0.67 alpha:1.0];
_editBadge.layer.cornerRadius = 12; _editBadge.layer.masksToBounds = YES;
UIImage *img = nil;
if (@available(iOS 13.0, *)) img = [UIImage systemImageNamed:@"pencil"]; //
[_editBadge setImage:img forState:UIControlStateNormal];
[_editBadge setTitle:(img ? @"" : @"✎") forState:UIControlStateNormal];
_editBadge.titleLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightBold];
[_editBadge setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
[_editBadge addTarget:self action:@selector(onTapAvatarEdit) forControlEvents:UIControlEventTouchUpInside];
}
return _editBadge;
}
- (UILabel *)modifyLabel {
if (!_modifyLabel) {
_modifyLabel = [UILabel new];
_modifyLabel.text = @"Modify";
_modifyLabel.textColor = [UIColor blackColor];
_modifyLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
}
return _modifyLabel;
}
- (UIView *)footerView {
if (!_footerView) {
CGFloat w = UIScreen.mainScreen.bounds.size.width;
UIView *fv = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, 120)];
fv.backgroundColor = UIColor.clearColor;
[fv addSubview:self.logoutBg];
[self.logoutBg mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(fv).offset(16);
make.right.equalTo(fv).offset(-16);
make.top.equalTo(fv).offset(14);
make.height.mas_equalTo(56);
}];
[self.logoutBg addSubview:self.logoutBtn];
[self.logoutBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.logoutBg);
}];
_footerView = fv;
}
return _footerView;
}
- (UIView *)logoutBg {
if (!_logoutBg) {
_logoutBg = [UIView new];
_logoutBg.backgroundColor = UIColor.whiteColor;
_logoutBg.layer.cornerRadius = 12; _logoutBg.layer.masksToBounds = YES;
}
return _logoutBg;
}
- (UIButton *)logoutBtn {
if (!_logoutBtn) {
_logoutBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_logoutBtn setTitle:@"Log Out" forState:UIControlStateNormal];
[_logoutBtn setTitleColor:[UIColor colorWithRed:0.85 green:0.15 blue:0.11 alpha:1.0] forState:UIControlStateNormal];
_logoutBtn.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
[_logoutBtn addTarget:self action:@selector(onTapLogout) forControlEvents:UIControlEventTouchUpInside];
}
return _logoutBtn;
}
@end