UILabel alignTop / alignBottom

本文介绍了如何使用Objective-C中的UILabel类实现文本垂直居中对齐,并提供了使用示例代码,帮助开发者轻松实现文本在UILabel中的垂直定位。


#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end


@implementation UILabel (VerticalAlign)
- (void)alignTop
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i<= newLinesToPad; i++)
    {
        self.text = [self.text stringByAppendingString:@" \n"];
    }
}

- (void)alignBottom
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i< newLinesToPad; i++)
    {
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
    }
}
@end
//----------------HOW TO USE-----------------//
[myLabel alignTop];
[myLabel alignBottom];

非常好的问题! 你看到的这些: ```swift @objc dynamic var coverImageView: UIImageView! @objc dynamic var maskImageView: UIImageView! @objc dynamic var maskLabel: UILabel! @objc dynamic var helpButton: UIButton? @objc dynamic var helpLabel: UILabel! // Constraints that may be modified externally @objc dynamic var coverLeadingConst: NSLayoutConstraint! @objc dynamic var coverTrailConst: NSLayoutConstraint! ``` 虽然是在 **纯代码实现中定义和创建视图**,但仍然保留这些 `@IBOutlet` 风格的属性,是有明确目的的。下面我们来逐个解释它们的作用、为什么需要保留、以及 `@objc dynamic` 的含义。 --- ## ✅ 一、这些变量的本质:不是 IBOutlet,而是“可被外部访问的公有属性” 即使我们不再使用 `.xib` 文件,这些变量依然保留,是因为: > 🔹 **它们是供外部代码(如 Cell、Controller)访问和修改内部子视图或约束用的公共接口** 换句话说: 虽然视图现在由代码创建,但其他类可能依赖于能直接操作 `coverImageView.image` 或调整 `coverLeadingConst.constant`。 ### 📌 典型使用场景举例: ```swift // 外部控制器或 cell 中 containerView.coverImageView.image = downloadedImage containerView.maskLabel.text = "离线" containerView.coverLeadingConst?.constant = 10 // 缩进左边距 ``` 如果你把这些属性删了,外面调用的地方就会编译失败 ❌ 所以: ✅ 它们不是真正的 `@IBOutlet`(因为没有连接 XIB), ✅ 但它们是 **设计上的公开 API 接口**,必须保留。 --- ## ✅ 二、为什么要加 `@objc dynamic`? 我们来拆解这两个关键字: ### 1. `@objc` - 表示这个属性可以被 Objective-C 运行时识别 - 常用于: - KVO(Key-Value Observing) - Runtime 动态存取(比如通过字符串 keyPath 获取值) - Swift 和 OC 混编项目中的反射机制 - 某些第三方库(如 RxSwift、Kingfisher)可能会用到 runtime 特性 📌 示例:如果某个地方写了: ```swift value(forKey: "coverImageView") ``` 你就必须让这个属性对 runtime 可见 → 所以加 `@objc` ### 2. `dynamic` - 告诉 Swift 编译器:这个属性要交给 **Objective-C runtime 来动态派发** - 启用 KVO 监听能力 - 即使没有显式 addObserver,有些框架也可能依赖此特性做自动化处理 ⚠️ 注意:只有 `@objc` 成员才能被 `dynamic` 修饰。 --- ### ✅ 综合解释: | 属性 | 作用 | |------|------| | `@objc` | 让 Swift 属性暴露给 OC runtime,支持 KVC/KVO、方法交换等 | | `dynamic` | 强制使用动态分发(non-inlined),允许 runtime hook | | 结合使用 `@objc dynamic` | 最大程度兼容旧架构、中间件、调试工具 | 👉 在大型项目中,很多 UI 组件会被各种中间层、AOP(面向切面编程)、埋点 SDK 动态读取,因此保留 `@objc dynamic` 是一种 **防御性编程**。 --- ## ✅ 三、具体每个变量用途详解 | 变量名 | 类型 | 用途说明 | |--------|------|----------| | `coverImageView` | `UIImageView!` | 主封面图,外部常用来设置图片、动画等 | | `maskImageView` | `UIImageView!` | 显示播放按钮、遮罩图层(如离线状态) | | `maskLabel` | `UILabel!` | 显示“无设备”、“离线”等提示文字 | | `helpButton` | `UIButton?` | 显示帮助图标按钮(可点击) | | `helpLabel` | `UILabel!` | 显示辅助说明文本,例如“未连接” | | `coverLeadingConst` | `NSLayoutConstraint!` | 指向 `coverImageView` 左边距约束,可用于动态缩进 | | `coverTrailConst` | `NSLayoutConstraint!` | 指向右边距约束,同上 | 🔍 特别注意两个 constraint 变量: ```swift @objc dynamic var coverLeadingConst: NSLayoutConstraint! @objc dynamic var coverTrailConst: NSLayoutConstraint! ``` 它们原本是从 XIB 中拖出来的 Outlet,指向具体的 Auto Layout 约束对象。 在纯代码中,我们这样重建它: ```swift coverImageView.snp.makeConstraints { make in make.leading.equalToSuperview() make.trailing.equalToSuperview() // ... } coverLeadingConst = coverImageView.snp.leading.constraint coverTrailConst = coverImageView.snp.trailing.constraint ``` 这样做的目的是: > ✅ 让外部代码仍然可以写: ```swift containerView.coverLeadingConst.constant = 8 ``` 来实现卡片内边距变化(比如折叠/展开状态) --- ## ✅ 四、能否去掉 `@objc dynamic`?(技术可行 vs 实际风险) | 方式 | 是否推荐 | 说明 | |------|---------|------| | 改成 `public var`(无 @objc dynamic) | ⚠️ 不推荐 | 如果项目中有 KVO 或 runtime 调用会 crash | | 完全删除这些属性 | ❌ 绝对不行 | 外部引用会崩溃 | | 改为 private + 提供 setter 方法 | ✅ 理论可行,但成本高 | 需要重构所有调用方,破坏兼容性 | 📌 结论: 在迁移过程中,**应保持原有接口不变**,只替换底层实现(从 XIB → 代码布局)。这是安全重构的核心原则。 --- ## ✅ 五、总结:这些变量为什么还在? | 问题 | 回答 | |------|------| | 为什么还有这些属性? | 它们是公开 API,供外部组件访问内部视图 | | 为什么用 `@objc dynamic`? | 保证与 Objective-C runtime 兼容,支持 KVO/KVC,避免运行时 crash | | 是不是多余的? | ❌ 不是!删除会导致外部调用失败或 runtime 异常 | | 和 IBOutlet 有什么区别? | 现在是代码创建 + 手动赋值,不再是 Interface Builder 拖出来的,但语义一致 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值