IOS UIKit详解

241 阅读8分钟

UIKit 是 iOS/macOS(iPadOS)平台用于构建用户界面的核心框架,封装了从基础视图到复杂交互的全套解决方案。以下从架构、核心组件、布局系统、事件处理等多个维度详细解析,并提供 Swift 和 Objective-C 示例。

一、UIKit 架构与核心概念

1. 框架依赖

UIKit 基于 Foundation(数据类型、内存管理)、CoreGraphics(绘图)、QuartzCore(图层与动画)构建,形成完整的界面渲染与交互闭环。

2. 核心层级

  • UIResponder:所有可响应事件的基类(UIView、UIViewController 均继承自它)。
  • UIView:可视化组件基类,管理图层(CALayer)、子视图层级。
  • UIViewController:视图控制器,管理视图生命周期、业务逻辑。
  • UIWindow:顶级视图容器,一个应用至少有一个窗口。

二、UIView 与图层系统

UIView 是界面渲染的基础,其视觉呈现由底层 CALayer 负责,二者分工:

  • UIView:处理用户交互(事件响应)、管理子视图。
  • CALayer:负责绘制内容(背景、边框、阴影等)、动画效果。

示例:自定义视图与图层属性

Swift

import UIKit

class CustomLayerView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayer()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayer()
    }
    
    private func setupLayer() {
        // 视图属性
        backgroundColor = .white
        // 图层属性
        layer.borderColor = UIColor.systemGreen.cgColor
        layer.borderWidth = 2
        layer.cornerRadius = 10
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOpacity = 0.3
        layer.shadowOffset = CGSize(width: 0, height: 3)
        layer.shadowRadius = 5
        // 阴影路径(优化性能)
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
    }
    
    // 视图大小变化时更新阴影路径
    override func layoutSubviews() {
        super.layoutSubviews()
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
    }
}

// 使用
let layerView = CustomLayerView(frame: CGRect(x: 50, y: 50, width: 200, height: 100))
view.addSubview(layerView)

Objective-C

// CustomLayerView.h
#import <UIKit/UIKit.h>

@interface CustomLayerView : UIView
@end

// CustomLayerView.m
#import "CustomLayerView.h"

@implementation CustomLayerView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupLayer];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self setupLayer];
    }
    return self;
}

- (void)setupLayer {
    // 视图属性
    self.backgroundColor = [UIColor whiteColor];
    // 图层属性
    self.layer.borderColor = [UIColor systemGreenColor].CGColor;
    self.layer.borderWidth = 2;
    self.layer.cornerRadius = 10;
    self.layer.shadowColor = [UIColor blackColor].CGColor;
    self.layer.shadowOpacity = 0.3;
    self.layer.shadowOffset = CGSizeMake(0, 3);
    self.layer.shadowRadius = 5;
    // 阴影路径
    self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:10].CGPath;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:10].CGPath;
}

@end

// 使用
CustomLayerView *layerView = [[CustomLayerView alloc] initWithFrame:CGRectMake(50, 50, 200, 100)];
[self.view addSubview:layerView];

三、UIViewController 生命周期

UIViewController 管理视图的创建、显示、销毁,核心生命周期方法调用顺序: initloadViewviewDidLoadviewWillAppearviewDidLayoutSubviewsviewDidAppearviewWillDisappearviewDidDisappeardealloc

示例:生命周期方法实践

Swift

import UIKit

class LifeCycleVC: UIViewController {
    // 初始化
    init() {
        super.init(nibName: nil, bundle: nil)
        print("1. 初始化完成")
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 加载视图(若未使用xib,需手动创建view)
    override func loadView() {
        super.loadView()
        view.backgroundColor = .white
        print("2. 视图加载完成")
    }
    
    // 视图加载完成(适合初始化数据、添加子视图)
    override func viewDidLoad() {
        super.viewDidLoad()
        print("3. 视图准备就绪")
    }
    
    // 视图即将显示
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("4. 视图即将显示")
    }
    
    // 视图布局完成(适合获取最终frame)
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("5. 视图布局完成")
    }
    
    // 视图已显示
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("6. 视图已显示")
    }
    
    // 视图即将消失
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("7. 视图即将消失")
    }
    
    // 视图已消失
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("8. 视图已消失")
    }
    
    // 销毁
    deinit {
        print("9. 控制器销毁")
    }
}

Objective-C

// LifeCycleVC.h
#import <UIKit/UIKit.h>

@interface LifeCycleVC : UIViewController
@end

// LifeCycleVC.m
#import "LifeCycleVC.h"

@implementation LifeCycleVC

- (instancetype)init {
    self = [super initWithNibName:nil bundle:nil];
    if (self) {
        NSLog(@"1. 初始化完成");
    }
    return self;
}

- (void)loadView {
    [super loadView];
    self.view.backgroundColor = [UIColor whiteColor];
    NSLog(@"2. 视图加载完成");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"3. 视图准备就绪");
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    NSLog(@"4. 视图即将显示");
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    NSLog(@"5. 视图布局完成");
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"6. 视图已显示");
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    NSLog(@"7. 视图即将消失");
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    NSLog(@"8. 视图已消失");
}

- (void)dealloc {
    NSLog(@"9. 控制器销毁");
}

@end

四、布局系统详解

UIKit 提供三种布局方式:Frame 布局(固定坐标)、Autoresizing Mask(简单适配)、Auto Layout(动态约束)。

1. Auto Layout 核心概念

  • 约束(Constraint):定义视图间的位置/尺寸关系(如“A的左边缘 = B的右边缘 + 20”)。
  • 参照系:常用 superviewsafeAreaLayoutGuide(安全区域)、layoutMarginsGuide(边距)。
  • 优先级:约束冲突时,高优先级(1-1000)约束优先生效。
  • 内容拥抱(Content Hugging):视图拒绝被拉伸的优先级(值越高越“抱紧”内容)。
  • 内容压缩阻力(Content Compression Resistance):视图拒绝被压缩的优先级(值越高越“抵抗”压缩)。

示例:复杂 Auto Layout 约束

Swift

import UIKit

class AutoLayoutDemoVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // 红色视图
        let redView = UIView()
        redView.backgroundColor = .systemRed
        redView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(redView)
        
        // 蓝色视图(依赖红色视图)
        let blueView = UIView()
        blueView.backgroundColor = .systemBlue
        blueView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(blueView)
        
        // 激活约束
        NSLayoutConstraint.activate([
            // 红色视图:左、上距安全区20,宽100,高等于宽(1:1)
            redView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
            redView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            redView.widthAnchor.constraint(equalToConstant: 100),
            redView.heightAnchor.constraint(equalTo: redView.widthAnchor),
            
            // 蓝色视图:左接红色视图右边缘(间距20),顶部与红色对齐,
            // 右距安全区20,高度与红色相同
            blueView.leadingAnchor.constraint(equalTo: redView.trailingAnchor, constant: 20),
            blueView.topAnchor.constraint(equalTo: redView.topAnchor),
            blueView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
            blueView.heightAnchor.constraint(equalTo: redView.heightAnchor)
        ])
    }
}

Objective-C

// AutoLayoutDemoVC.h
#import <UIKit/UIKit.h>

@interface AutoLayoutDemoVC : UIViewController
@end

// AutoLayoutDemoVC.m
#import "AutoLayoutDemoVC.h"

@implementation AutoLayoutDemoVC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    // 红色视图
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor systemRedColor];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redView];
    
    // 蓝色视图
    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor systemBlueColor];
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];
    
    // 激活约束
    [NSLayoutConstraint activateConstraints:@[
        // 红色视图
        [redView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor constant:20],
        [redView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:20],
        [redView.widthAnchor constraintEqualToConstant:100],
        [redView.heightAnchor constraintEqualToAnchor:redView.widthAnchor],
        
        // 蓝色视图
        [blueView.leadingAnchor constraintEqualToAnchor:redView.trailingAnchor constant:20],
        [blueView.topAnchor constraintEqualToAnchor:redView.topAnchor],
        [blueView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor constant:-20],
        [blueView.heightAnchor constraintEqualToAnchor:redView.heightAnchor]
    ]];
}

@end

五、事件处理机制

UIKit 事件体系基于 UIResponder 链,支持三类事件:

  • 触摸事件(UITouch):用户手指与屏幕的交互(点击、滑动等)。
  • 运动事件(UIEvent.Subtype):设备运动(如摇一摇)。
  • 远程控制事件:如耳机线控。

1. 触摸事件传递流程

UIApplicationUIWindow → 顶级视图 → ... → 目标视图(通过 hitTest:withEvent: 查找)。

2. 手势识别器(UIGestureRecognizer)

封装常见手势(点击、捏合、旋转等),简化事件处理。

示例:手势识别器使用

Swift

import UIKit

class GestureDemoVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        let gestureView = UIView(frame: CGRect(x: 100, y: 200, width: 200, height: 200))
        gestureView.backgroundColor = .systemPurple
        view.addSubview(gestureView)
        
        // 点击手势(双击)
        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        tap.numberOfTapsRequired = 2 // 双击
        gestureView.addGestureRecognizer(tap)
        
        // 捏合手势(缩放)
        let pinch = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        gestureView.addGestureRecognizer(pinch)
    }
    
    @objc private func handleTap(_ gesture: UITapGestureRecognizer) {
        print("双击触发")
    }
    
    @objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        // 缩放视图
        gesture.view?.transform = (gesture.view?.transform.scaledBy(x: gesture.scale, y: gesture.scale))!
        gesture.scale = 1 // 重置缩放比例,确保连续缩放
    }
}

Objective-C

// GestureDemoVC.h
#import <UIKit/UIKit.h>

@interface GestureDemoVC : UIViewController
@end

// GestureDemoVC.m
#import "GestureDemoVC.h"

@implementation GestureDemoVC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIView *gestureView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 200, 200)];
    gestureView.backgroundColor = [UIColor systemPurpleColor];
    [self.view addSubview:gestureView];
    
    // 点击手势(双击)
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tap.numberOfTapsRequired = 2;
    [gestureView addGestureRecognizer:tap];
    
    // 捏合手势
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
    [gestureView addGestureRecognizer:pinch];
}

- (void)handleTap:(UITapGestureRecognizer *)gesture {
    NSLog(@"双击触发");
}

- (void)handlePinch:(UIPinchGestureRecognizer *)gesture {
    gesture.view.transform = CGAffineTransformScale(gesture.view.transform, gesture.scale, gesture.scale);
    gesture.scale = 1;
}

@end

六、容器控制器

容器控制器用于管理多个视图控制器的切换与组合,常见类型:

1. UINavigationController(导航控制器)

以栈式管理 VC,支持 push/pop 切换,顶部有导航栏(UINavigationBar)。

Swift 示例

// 初始化导航控制器
let rootVC = UIViewController()
rootVC.view.backgroundColor = .white
rootVC.title = "首页" // 导航栏标题
let navVC = UINavigationController(rootViewController: rootVC)

// 跳转到新页面
let detailVC = UIViewController()
detailVC.view.backgroundColor = .systemYellow
detailVC.title = "详情页"
// 自定义导航栏按钮
detailVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
    title: "返回", 
    style: .plain, 
    target: self, 
    action: #selector(popBack)
)
rootVC.navigationController?.pushViewController(detailVC, animated: true)

@objc private func popBack() {
    navigationController?.popViewController(animated: true)
}

Objective-C 示例

// 初始化导航控制器
UIViewController *rootVC = [[UIViewController alloc] init];
rootVC.view.backgroundColor = [UIColor whiteColor];
rootVC.title = @"首页";
UINavigationController *navVC = [[UINavigationController alloc] initWithRootViewController:rootVC];

// 跳转到新页面
UIViewController *detailVC = [[UIViewController alloc] init];
detailVC.view.backgroundColor = [UIColor systemYellowColor];
detailVC.title = @"详情页";
// 自定义导航栏按钮
detailVC.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" 
                                                                             style:UIBarButtonItemStylePlain 
                                                                            target:self 
                                                                            action:@selector(popBack)];
[rootVC.navigationController pushViewController:detailVC animated:YES];

- (void)popBack {
    [self.navigationController popViewControllerAnimated:YES];
}

2. UITabBarController(标签栏控制器)

底部有标签栏(UITabBar),可切换多个平级 VC。

Swift 示例

let tabVC = UITabBarController()

// 首页VC
let homeVC = UIViewController()
homeVC.view.backgroundColor = .white
homeVC.tabBarItem = UITabBarItem(
    title: "首页", 
    image: UIImage(systemName: "house"), 
    selectedImage: UIImage(systemName: "house.fill")
)

// 我的VC
let mineVC = UIViewController()
mineVC.view.backgroundColor = .systemGray5
mineVC.tabBarItem = UITabBarItem(
    title: "我的", 
    image: UIImage(systemName: "person"), 
    selectedImage: UIImage(systemName: "person.fill")
)

// 设置子VC
tabVC.viewControllers = [homeVC, mineVC]
// 选中第1个(索引从0开始)
tabVC.selectedIndex = 0

七、高级组件:UITableView(列表)

UITableView 是滚动列表组件,通过 数据源(UITableViewDataSource)代理(UITableViewDelegate) 协议驱动。

示例:基础列表实现

Swift

import UIKit

class TableDemoVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
    let data = ["苹果", "香蕉", "橙子", "草莓", "葡萄"]
    let tableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        
        // 配置表格
        tableView.frame = view.bounds
        tableView.dataSource = self
        tableView.delegate = self
        // 注册单元格(复用)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        view.addSubview(tableView)
    }
    
    // 数据源:行数
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        data.count
    }
    
    // 数据源:单元格内容
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = data[indexPath.row]
        return cell
    }
    
    // 代理:点击单元格
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true) // 取消选中状态
        print("点击了:\(data[indexPath.row])")
    }
}

Objective-C

// TableDemoVC.h
#import <UIKit/UIKit.h>

@interface TableDemoVC : UIViewController <UITableViewDataSource, UITableViewDelegate>
@end

// TableDemoVC.m
#import "TableDemoVC.h"

@implementation TableDemoVC {
    NSArray *data;
    UITableView *tableView;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    data = @[@"苹果", @"香蕉", @"橙子", @"草莓", @"葡萄"];
    
    // 配置表格
    tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
    tableView.dataSource = self;
    tableView.delegate = self;
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    [self.view addSubview:tableView];
}

#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return data.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
    cell.textLabel.text = data[indexPath.row];
    return cell;
}

#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSLog(@"点击了:%@", data[indexPath.row]);
}

@end

八、UIView 动画

UIKit 提供简洁的动画 API,支持基础动画、弹簧动画等。

Swift 示例

// 基础动画
UIView.animate(withDuration: 0.3) {
    self.view.alpha = 0.5 // 透明度变化
} completion: { _ in
    print("动画结束")
}

// 弹簧动画
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5) {
    self.view.center.x += 100 // 水平移动
}

Objective-C 示例

// 基础动画
[UIView animateWithDuration:0.3 animations:^{
    self.view.alpha = 0.5;
} completion:^(BOOL finished) {
    NSLog(@"动画结束");
}];

// 弹簧动画
[UIView animateWithDuration:0.5 
                      delay:0 
     usingSpringWithDamping:0.5 
      initialSpringVelocity:0.5 
                    options:0 
                 animations:^{
    self.view.center.x += 100;
} completion:nil];

九、总结

UIKit 是 iOS 界面开发的基石,核心在于理解:

  • 视图与控制器分离:UIView 负责展示,UIViewController 负责逻辑。
  • 层级与约束:通过视图树和 Auto Layout 实现复杂界面。
  • 事件响应链:从触摸到处理的完整传递机制。
  • 复用机制:表格/集合视图的单元格复用优化性能。

实际开发中,需结合 Interface Builder(Storyboard/XIB)与代码,灵活运用布局、动画和交互,构建适配多设备的界面。