Files
keyboard/keyBoard/Class/Me/VC/KBPersonInfoVC.m
2025-11-17 20:07:39 +08:00

370 lines
16 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// KBPersonInfoVC.m
// keyBoard
//
// Created by Mac on 2025/11/11.
//
//
// KBPersonInfoVC.m
// 个人资料
//
#import "KBPersonInfoVC.h"
#import <Masonry/Masonry.h>
#import "KBPersonInfoItemCell.h"
#import <PhotosUI/PhotosUI.h>
#import "LSTPopView.h"
#import "KBChangeNicknamePopView.h"
#import "KBGenderPickerPopView.h"
@interface KBPersonInfoVC () <UITableViewDelegate, UITableViewDataSource, PHPickerViewControllerDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate>
// 列表
@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) UIButton *logoutBtn;
// 数据
@property (nonatomic, copy) NSArray<NSDictionary *> *items; // {title,value,arrow,copy}
// 压缩后的头像 JPEG 数据(可用于上传)
@property (nonatomic, strong) NSData *avatarJPEGData;
@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.view addSubview:self.logoutBtn];
[self.logoutBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.right.equalTo(self.view).offset(-16);
make.bottom.equalTo(self.view.mas_safeAreaLayoutGuideBottom).offset(-12);
make.height.mas_equalTo(56);
}];
// 列表底部腾出空间,避免被按钮挡住
UIEdgeInsets inset = self.tableView.contentInset;
inset.bottom = 56 + 24; // 按钮高度 + 额外间距
self.tableView.contentInset = inset;
}
#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 {
if (indexPath.row == 0) {
// 昵称编辑 -> 弹窗
CGFloat width = KB_SCREEN_WIDTH;
KBChangeNicknamePopView *content = [[KBChangeNicknamePopView alloc] initWithFrame:CGRectMake(0, 0, width, 230)];
content.prefillNickname = self.items.firstObject[@"value"] ?: @"";
LSTPopView *pop = [LSTPopView initWithCustomView:content
parentView:self.view
popStyle:LSTPopStyleSmoothFromBottom
dismissStyle:LSTDismissStyleSmoothToBottom];
pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
pop.hemStyle = LSTHemStyleBottom; // 居中
pop.isClickBgDismiss = YES; // 点击背景关闭
pop.isAvoidKeyboard = YES; // 规避键盘
pop.avoidKeyboardSpace = 10;
__weak typeof(self) weakSelf = self;
__weak typeof(pop) weakPop = pop;
content.closeHandler = ^{ [weakPop dismiss]; };
content.saveHandler = ^(NSString *nickname) {
if (nickname.length > 0) {
// 更新第一行展示
NSMutableArray *m = [weakSelf.items mutableCopy];
NSMutableDictionary *d0 = [m.firstObject mutableCopy];
d0[@"value"] = nickname; m[0] = d0; weakSelf.items = m;
[weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}
[weakPop dismiss];
};
[pop pop];
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [content focusInput]; });
} else if (indexPath.row == 1) {
// 性别选择 -> 弹窗
NSArray *genders = @[ @{@"id":@"1",@"name":@"Male"}, @{@"id":@"2",@"name":@"Female"}, @{@"id":@"3",@"name":@"The Third Gender"} ];
CGFloat width = KB_SCREEN_WIDTH;
KBGenderPickerPopView *content = [[KBGenderPickerPopView alloc] initWithFrame:CGRectMake(0, 0, width, 300)];
content.items = genders;
// 取当前展示值对应的 id如果有的话
NSString *curName = self.items[1][@"value"];
NSString *selId = nil;
for (NSDictionary *d in genders) { if ([d[@"name"] isEqualToString:curName]) { selId = d[@"id"]; break; } }
content.selectedId = selId;
LSTPopView *pop = [LSTPopView initWithCustomView:content
parentView:nil
popStyle:LSTPopStyleSmoothFromBottom
dismissStyle:LSTDismissStyleSmoothToBottom];
pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
pop.hemStyle = LSTHemStyleBottom;
pop.isClickBgDismiss = YES;
__weak typeof(self) weakSelf = self; __weak typeof(pop) weakPop = pop;
content.closeHandler = ^{ [weakPop dismiss]; };
content.saveHandler = ^(NSDictionary *selected) {
NSString *name = selected[@"name"] ?: @"";
NSMutableArray *m = [weakSelf.items mutableCopy];
NSMutableDictionary *d1 = [m[1] mutableCopy];
d1[@"value"] = name; m[1] = d1; weakSelf.items = m;
[weakSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[weakPop dismiss];
};
[pop pop];
}else if (indexPath.row == 2){
NSString *userID = self.items[2][@"value"];
if (userID.length == 0) return;
UIPasteboard.generalPasteboard.string = userID;
[KBHUD showInfo:KBLocalized(@"Copy Success")];
}
}
#pragma mark - Actions
- (void)onTapAvatarEdit { [self presentImagePicker]; }
- (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(-15);
make.centerY.equalTo(self.avatarView.mas_bottom).offset(-15);
}];
[self.modifyLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.avatarView.mas_bottom).offset(10);
make.centerX.equalTo(hv);
}];
// 头像可点击:弹系统相册
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapAvatarEdit)];
[self.avatarView addGestureRecognizer:tap];
_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.layer.cornerRadius = 12; _editBadge.layer.masksToBounds = YES;
UIImage *img = [UIImage imageNamed:@"myperson_edit_icon"];
[_editBadge setImage:img 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 colorWithHex:KBBlackValue];
_modifyLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightSemibold];
}
return _modifyLabel;
}
- (UIButton *)logoutBtn {
if (!_logoutBtn) {
_logoutBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[_logoutBtn setTitle:@"Log Out" forState:UIControlStateNormal];
[_logoutBtn setTitleColor:[UIColor colorWithHex:0xFF0000] forState:UIControlStateNormal];
_logoutBtn.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
_logoutBtn.backgroundColor = UIColor.whiteColor;
_logoutBtn.layer.cornerRadius = 12; _logoutBtn.layer.masksToBounds = YES;
[_logoutBtn addTarget:self action:@selector(onTapLogout) forControlEvents:UIControlEventTouchUpInside];
}
return _logoutBtn;
}
#pragma mark - Image Picker
- (void)presentImagePicker {
if (@available(iOS 14.0, *)) {
PHPickerConfiguration *config = [[PHPickerConfiguration alloc] init];
config.selectionLimit = 1; // 只选一张
config.filter = [PHPickerFilter imagesFilter];
PHPickerViewController *picker = [[PHPickerViewController alloc] initWithConfiguration:config];
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
} else {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
}
#pragma mark - PHPickerViewControllerDelegate
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14.0)) {
[picker dismissViewControllerAnimated:YES completion:nil];
PHPickerResult *first = results.firstObject; if (!first) return;
NSItemProvider *p = first.itemProvider;
if ([p canLoadObjectOfClass:UIImage.class]) {
__weak typeof(self) weakSelf = self;
[p loadObjectOfClass:UIImage.class completionHandler:^(__kindof id<NSItemProviderReading> _Nullable object, NSError * _Nullable error) {
UIImage *img = ([object isKindOfClass:UIImage.class] ? (UIImage *)object : nil);
if (!img) return;
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *compressed = [weakSelf kb_compressImage:img maxPixel:512 quality:0.85];
weakSelf.avatarView.image = compressed;
weakSelf.avatarJPEGData = UIImageJPEGRepresentation(compressed, 0.85);
});
}];
}
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
UIImage *img = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];
if (img) {
UIImage *compressed = [self kb_compressImage:img maxPixel:512 quality:0.85];
self.avatarView.image = compressed;
self.avatarJPEGData = UIImageJPEGRepresentation(compressed, 0.85);
}
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
/// 压缩图片:最长边不超过 maxPixel输出近似 quality 的 JPEG
- (UIImage *)kb_compressImage:(UIImage *)image maxPixel:(CGFloat)maxPixel quality:(CGFloat)quality {
if (!image) return nil;
maxPixel = MAX(64, maxPixel);
CGSize size = image.size;
CGFloat maxSide = MAX(size.width, size.height);
CGSize target = size;
if (maxSide > maxPixel) {
CGFloat scale = maxPixel / maxSide;
target = CGSizeMake(floor(size.width * scale), floor(size.height * scale));
}
UIGraphicsBeginImageContextWithOptions(target, YES, 1.0);
[image drawInRect:CGRectMake(0, 0, target.width, target.height)];
UIImage *scaled = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *jpeg = UIImageJPEGRepresentation(scaled ?: image, MIN(MAX(quality, 0.2), 0.95));
UIImage *result = [UIImage imageWithData:jpeg] ?: scaled ?: image;
return result;
}
@end