diff --git a/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/Contents.json b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/Contents.json new file mode 100644 index 0000000..727d2c7 --- /dev/null +++ b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "myperson_edit_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "myperson_edit_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@2x.png b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@2x.png new file mode 100644 index 0000000..1a3ead9 Binary files /dev/null and b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@2x.png differ diff --git a/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@3x.png b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@3x.png new file mode 100644 index 0000000..87d2abd Binary files /dev/null and b/keyBoard/Assets.xcassets/My/myperson_edit_icon.imageset/myperson_edit_icon@3x.png differ diff --git a/keyBoard/Class/Me/V/KBPersonInfoItemCell.m b/keyBoard/Class/Me/V/KBPersonInfoItemCell.m index d5feafa..58699b7 100644 --- a/keyBoard/Class/Me/V/KBPersonInfoItemCell.m +++ b/keyBoard/Class/Me/V/KBPersonInfoItemCell.m @@ -21,7 +21,7 @@ // 复制按钮 @property (nonatomic, strong) UIButton *copyBtn; // 分割线 -@property (nonatomic, strong) UIView *line; +// 废弃分割线:按需求不显示底部分割线 // 记录圆角位置,layoutSubviews 时更新 mask @property (nonatomic, assign) UIRectCorner cornerRecord; @end @@ -38,7 +38,6 @@ [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); @@ -70,20 +69,15 @@ 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; + // 根据位置设置圆角:顶部或底部为 12,中间为 0 + self.cardView.layer.cornerRadius = (self.cornerRecord != 0 ? 12.0 : 0.0); // 根据记录的 corner 重新生成 mask,保证尺寸变化后仍正确 if (self.cornerRecord != 0 && !CGRectIsEmpty(self.cardView.bounds)) { UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.cardView.bounds @@ -100,7 +94,6 @@ - (void)prepareForReuse { [super prepareForReuse]; self.arrowView.hidden = YES; self.copyBtn.hidden = YES; - self.line.hidden = NO; } - (void)configWithTitle:(NSString *)title @@ -126,15 +119,12 @@ 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; + corners = 0; } // 使用遮罩实现指定圆角 @@ -149,7 +139,7 @@ if (!_cardView) { _cardView = [UIView new]; _cardView.backgroundColor = UIColor.whiteColor; - _cardView.layer.cornerRadius = 12.0; + _cardView.layer.cornerRadius = 0.0; // 具体圆角在 layoutSubviews 中按位置设置 _cardView.layer.masksToBounds = YES; // 配合圆角 } return _cardView; @@ -158,7 +148,7 @@ - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [UILabel new]; - _titleLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0]; + _titleLabel.textColor = [UIColor colorWithHex:KBBlackValue]; _titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightRegular]; } return _titleLabel; @@ -167,7 +157,7 @@ - (UILabel *)valueLabel { if (!_valueLabel) { _valueLabel = [UILabel new]; - _valueLabel.textColor = [UIColor blackColor]; + _valueLabel.textColor = [UIColor colorWithHex:KBBlackValue]; _valueLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold]; _valueLabel.textAlignment = NSTextAlignmentRight; [_valueLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; @@ -230,13 +220,7 @@ return _copyBtn; } -- (UIView *)line { - if (!_line) { - _line = [UIView new]; - _line.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0]; - } - return _line; -} +// 无分割线 #pragma mark - Event diff --git a/keyBoard/Class/Me/VC/KBPersonInfoVC.m b/keyBoard/Class/Me/VC/KBPersonInfoVC.m index a3e328c..3f86d53 100644 --- a/keyBoard/Class/Me/VC/KBPersonInfoVC.m +++ b/keyBoard/Class/Me/VC/KBPersonInfoVC.m @@ -13,8 +13,9 @@ #import "KBPersonInfoVC.h" #import #import "KBPersonInfoItemCell.h" +#import -@interface KBPersonInfoVC () +@interface KBPersonInfoVC () // 列表 @property (nonatomic, strong) BaseTableView *tableView; // 懒加载 @@ -25,13 +26,13 @@ @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 *items; // {title,value,arrow,copy} +// 压缩后的头像 JPEG 数据(可用于上传) +@property (nonatomic, strong) NSData *avatarJPEGData; @end @@ -56,9 +57,22 @@ make.top.equalTo(self.view).offset(KB_NAV_TOTAL_HEIGHT + 10); }]; - // 表头 & 表尾 + // 表头 self.tableView.tableHeaderView = self.headerView; - self.tableView.tableFooterView = self.footerView; + + // 底部退出按钮固定在屏幕底部 + [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 @@ -101,9 +115,7 @@ #pragma mark - Actions -- (void)onTapAvatarEdit { - // 示例:更换头像 -} +- (void)onTapAvatarEdit { [self presentImagePicker]; } - (void)onTapLogout { // 示例:退出登录 @@ -141,14 +153,18 @@ }]; [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); + 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; @@ -177,14 +193,9 @@ - (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"]; // 铅笔图标 + UIImage *img = [UIImage imageNamed:@"myperson_edit_icon"]; [_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; @@ -194,54 +205,98 @@ if (!_modifyLabel) { _modifyLabel = [UILabel new]; _modifyLabel.text = @"Modify"; - _modifyLabel.textColor = [UIColor blackColor]; + _modifyLabel.textColor = [UIColor colorWithHex:KBBlackValue]; _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 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 *)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 _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 *)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 diff --git a/keyBoard/Info.plist b/keyBoard/Info.plist index c798311..94ec165 100644 --- a/keyBoard/Info.plist +++ b/keyBoard/Info.plist @@ -20,5 +20,11 @@ NSAllowsArbitraryLoads + + NSPhotoLibraryUsageDescription + 更换头像需要访问您的相册 + + NSPhotoLibraryAddUsageDescription + 保存图片需要写入您的相册