drawRect 觸發時機

在 iOS 開發中,UIViewdrawRect: 方法(或其底層 CALayer 的繪制)的觸發時機是由系統控制的,開發者不能直接調用這些方法。以下是觸發視圖繪制的完整機制:


一、核心觸發時機

1. 視圖首次顯示

當視圖被添加到視圖層級時:

[self.view addSubview:customView]; // 觸發首次繪制
2. 顯式標記需要重繪

調用以下方法強制重繪:

// 標記整個視圖需要重繪
[customView setNeedsDisplay];// 標記部分區域需要重繪(優化性能)
[customView setNeedsDisplayInRect:CGRectMake(0,0,50,50)];
3. 視圖幾何屬性變化
customView.frame = newFrame;     // 位置/尺寸變化
customView.bounds = newBounds;   // 坐標系變化
customView.transform = CGAffineTransformMakeRotation(M_PI/4); // 形變
4. 內容模式改變

contentMode 設為重繪模式:

customView.contentMode = UIViewContentModeRedraw; // 尺寸變化時觸發重繪
5. 關聯數據變化
// 數據變化時觸發重繪
- (void)setDataModel:(DataModel *)model {_model = model;[self setNeedsDisplay]; // 手動觸發
}

二、系統級自動觸發場景

1. RunLoop 周期處理
有setNeedsDisplay
無標記
RunLoop喚醒
檢查標記
執行drawRect
跳過繪制
提交渲染樹
2. 滾動視圖刷新
// UIScrollView 滾動時
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {[visibleCells makeObjectsPerformSelector:@selector(setNeedsDisplay)];
}
3. 系統事件觸發
// 設備旋轉
- (void)viewWillTransitionToSize:(CGSize)size {[self.view setNeedsDisplay];
}// 深色模式切換
- (void)traitCollectionDidChange:(UITraitCollection *)previous {[self updateAppearance]; // 觸發重繪
}

三、CALayer 專用觸發機制

1. 圖層屬性變化
layer.contents = newImage;      // 內容替換
layer.cornerRadius = 10.0;      // 外觀變化
layer.borderWidth = 2.0;        // 邊框變化
2. 強制重繪方法
[layer setNeedsDisplay];                     // 異步標記
[layer displayIfNeeded];                     // 同步立即繪制
[layer setNeedsDisplayInRect:invalidRect];   // 局部重繪
3. 動畫過渡重繪
[CATransaction begin];
[CATransaction setAnimationDuration:0.5];
layer.backgroundColor = [UIColor blueColor].CGColor; // 觸發隱式動畫
[CATransaction commit];

四、優化繪制的關鍵實踐

1. 避免過度繪制
// 檢查是否需要重繪
- (void)setData:(NSArray *)data {if ([_data isEqualToArray:data]) return;_data = data;[self setNeedsDisplay]; // 僅數據變化時觸發
}
2. 智能區域重繪
// 只重繪變化區域
- (void)updateItemAtIndex:(NSInteger)index {CGRect dirtyRect = [self rectForItemAtIndex:index];[self setNeedsDisplayInRect:dirtyRect];
}
3. 繪制狀態管理
// 在視圖中管理繪制狀態
- (void)drawRect:(CGRect)rect {if (self.isDrawingDisabled) return; // 跳過條件static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{[self drawStaticBackground]; // 只繪制一次});[self drawDynamicContentInRect:rect]; // 局部繪制
}
4. 離屏繪制優化
// 后臺預渲染
dispatch_async(renderQueue, ^{UIGraphicsBeginImageContextWithOptions(size, NO, 0);[self renderContent];UIImage *preRendered = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();dispatch_async(dispatch_get_main_queue(), ^{self.layer.contents = (id)preRendered.CGImage;});
});

五、調試與性能檢測

1. 繪制調試工具
// 顏色標記重繪區域
- (void)drawRect:(CGRect)rect {[[UIColor colorWithRed:1 green:0 blue:0 alpha:0.1] setFill];UIRectFill(rect); // 顯示重繪區域// 實際繪制內容...
}
2. Instruments 檢測
  1. 使用 Core Animation 工具:
    • 開啟 Color Misaligned Images
    • 開啟 Color Offscreen-Rendered Yellow
  2. 使用 Time Profiler 檢測 drawRect: CPU 耗時
3. 繪制耗時監控
- (void)drawRect:(CGRect)rect {CFTimeInterval start = CACurrentMediaTime();// 繪制操作...CFTimeInterval duration = CACurrentMediaTime() - start;if (duration > 0.016) { // 超過16ms警告NSLog(@"?? 繪制耗時過長: %.2fms", duration * 1000);}
}

六、特殊場景處理

1. 滾動視圖優化
// UITableViewCell 繪制控制
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];cell.drawingEnabled = tableView.isDecelerating || tableView.isDragging; // 滾動時禁用復雜繪制return cell;}
2. 內存警告處理
- (void)didReceiveMemoryWarning {[self clearRenderedCache]; // 釋放繪制緩存
}
3. 后臺繪制管理
// 進入后臺時暫停繪制
- (void)applicationDidEnterBackground {[self.layer.contents = nil]; // 釋放內容[self cancelAsyncDraw];      // 取消異步任務
}

總結:繪制觸發的黃金法則

  1. 絕不直接調用drawRect:
    始終通過 setNeedsDisplay 系列方法觸發
  2. 最小化重繪區域
    使用 setNeedsDisplayInRect: 精確控制
  3. 分離靜態與動態內容
    靜態內容預渲染,動態內容局部更新
  4. 監控繪制性能
    確保單幀繪制 ≤ 16ms(60FPS)
  5. 適配系統生命周期
    正確處理后臺/內存警告等場景

📌 關鍵提示:在 scrollViewDidScroll 等高頻回調中,避免無條件調用 setNeedsDisplay,應使用節流機制:

// 滾動時每幀最多觸發一次
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {static CFTimeInterval lastTime = 0;CFTimeInterval now = CACurrentMediaTime();if (now - lastTime > 0.016) { // 60FPS間隔[self setNeedsDisplay];lastTime = now;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87800.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87800.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87800.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1.1_4 計算機網絡的分類

在這個視頻中我們會探討計算機網絡的分類,從不同的角度可以對計算機網絡進行不同的分類,我們會從分布范圍、傳輸技術、拓撲結構、使用者和傳輸介質這樣的幾個維度進行討論,在這門課當中需要注意的是標紅色的幾個分類,其他的類別簡…

03每日簡報20250705

每日簡報 新聞簡報:AI行業信任危機浮現 標題:知名科技作者Alberto Romero發文《我對AI行業正在失去所有信任》 來源:The Algorithmic Bridge(算法之橋) 核心內容: 作者立場:長期支持AI技術…

Python 多版本環境治理理念驅動的系統架構設計:三維治理、四級隔離、五項自治 原則

Python 多版本與開發環境治理架構設計-CSDN博客 Python 多版本治理理念(Windows 平臺 零基礎友好)-CSDN博客 Python 多版本開發環境治理:理論架構與實踐-CSDN博客 【終極實戰】Conda/Poetry/Virtualenv/Pipenv/Hatch 多工具協同 AnacondaP…

C++ 第四階段 文件IO - 第一節:ifstream/ofstream操作

目錄 一、文件 IO 的基本概念 二、文件流的基本操作 1. 打開文件 2. 關閉文件 3. 檢查文件是否成功打開 三、文本文件的讀寫操作 1. 寫入文本文件(ofstream) 2. 讀取文本文件(ifstream) 四、二進制文件的讀寫操作 1. 寫…

容聲W60以光水離子科技實現食材“主動養鮮”

炎炎夏日,孩子沉迷電視手機屏幕,視力堪憂?高價買回的“超級食物”羽衣甘藍、車厘子,幾天就蔫了?切開的西瓜放進冰箱,卻怕沾染細菌?7月5日,容聲冰箱“WILL養鮮 高能一夏”新品發布會給…

力扣面試150(13/150)

7.3 380. O(1) 時間插入、刪除和獲取隨機元素 實現RandomizedSet 類: RandomizedSet() 初始化 RandomizedSet 對象bool insert(int val) 當元素 val 不存在時,向集合中插入該項,并返回 true ;否則,返回 false 。bool…

需要scl來指定編譯器的clangd+cmake在vscode/cursor開發環境下的配置

最近cursor更新了插件商店,只能使用默認它魔改的c/c插件(基于clangd的),手頭剛好在折騰一個cmake工程,試試水嘗試直接配置在cursor上可以編譯運行。 主要是本地環境使用scl來管理gcc/g,所以在配置過程中需要…

docker離線/在線環境下安裝elasticsearch

如果想離線安裝docker、redis、gninx、mysql可參照下面這個。 離線環境下,docker安裝redis、ngnix、mysql 獲取離線包 方式1 找一個能上網的環境,下載elasticsearch的鏡像,然后將這個鏡像導出 docker pull docker.elastic.co/elasticsear…

響應式編程入門教程第一節:揭秘 UniRx 核心 - ReactiveProperty - 讓你的數據動起來!

響應式編程入門教程第一節:揭秘 UniRx 核心 - ReactiveProperty - 讓你的數據動起來!-CSDN博客 響應式編程入門教程第二節:構建 ObservableProperty<T> — 封裝 ReactiveProperty 的高級用法-CSDN博客 今天我們來聊聊…

單片機:STM32F103的開發環境搭建

本文將詳細介紹如何搭建STM32F103的開發環境。STM32F103是STMicroelectronics推出的一款基于ARM Cortex-M3內核的32位微控制器(MCU),廣泛應用于嵌入式開發。以下是搭建開發環境的詳細步驟,涵蓋硬件準備、軟件安裝、工具鏈配置及簡…

eNSP中實現vlan間路由通信(路由器)

eNSP中實現vlan間路由通信(路由器) 拓撲圖PC配置 pc1:192.168.10.1255.255.255.0192.168.10.254pc2:192.168.20.1255.255.255.0192.168.20.254pc3: 192.168.10.2255.255.255.0192.168.10.254pc4:192.168.20.2255.255.2…

spring6合集——spring概述以及OCP、DIP、IOC原則

spring6合集——Spring6核心知識點總結啟示錄一、SOLID原則1. 單一職責原則(SRP)2. 開閉原則(OCP)3. 里氏替換原則(LSP)4. 接口隔離原則(ISP)5. 依賴倒置原則(DIP&#x…

Stata如何做機器學習?——SHAP解釋框架下的足球運動員價值驅動因素識別:基于H2O集成學習模型

SHAP解釋框架下的足球運動員價值驅動因素識別——基于H2O集成學習模型? 歡迎關注 「阿水實證通」,前沿方法時刻看!🌟🌟🌟 文章目錄 SHAP解釋框架下的足球運動員價值驅動因素識別——基于H2O集成學習模型?聚焦&…

基于Android的益智游戲學習系統

博主介紹:java高級開發,從事互聯網行業多年,熟悉各種主流語言,精通java、python、php、爬蟲、web開發,已經做了多年的畢業設計程序開發,開發過上千套畢業設計程序,沒有什么華麗的語言&#xff0…

Oracle11G Linux版本(linux_x86_64_oracle11.2.0.4)

Oracle11G Linux版本 linux_x86_64_oracle11.2.0.4 文件分割成 七個 壓縮包,必須集齊 七個 文件后才能一起解壓一起使用: p13390677_112040_Linux-x86-64_7of7.zip下載地址: https://download.csdn.net/download/weixin_43800734/20303421 p1…

C++20中的counting_semaphore的應用

一、std::counting_semaphore 在前面介紹過C20中的同步庫,其中就提到過std::counting_semaphore。但當時的重點是同步庫的整體介紹,本文則會對std::counting_semaphore這個信號量進行一個全面的分析和說明,并有針對性的給出具體的例程。 C20中…

mongo常用命令

1 連接mongo服務器 mongo ip:端口/庫名 -u 用戶名 -p 密碼 2 選擇數據庫 show dbs; 顯示數據庫列表 use 數據庫名稱; 3 集合操作 (1) 顯示集合列表 show tables; (2)刪除集合 db.集合名稱.drop(); (3&#x…

華為云 銀河麒麟 vscode遠程連接

解決方案 檢查 SSH 服務器配置: 在遠程主機上編輯 /etc/ssh/sshd_config 文件 關鍵配置說明: AllowTcpForwarding yes # 允許TCP端口轉發(必須開啟) AllowAgentForwarding yes # 允許SSH代理轉發(可選&#xf…

有限狀態機(Finite State Machine)

文章目錄有限狀態機(Finite State Machine)簡介狀態機的組成六要素(1) 狀態集合(2) 初態(3) 終態(4) 輸入符號集(5) 輸出符號集(6) 狀態轉移函數狀態機的工作四要素(1) 現態(2) 輸入(3) 輸出(4) 次態FPGA中的狀態機模型1. Moore型狀態機(1) Moore l型(2)…

前端框架中注釋占位與Fragment內容替換的實現與優化

在現代前端開發中,使用注釋占位符替換Fragment內容是一種常見的需求,尤其在處理動態內容、模板預加載和組件復用場景中。React和Vue作為當前最主流的前端框架,提供了不同的實現方式和優化策略,但核心目標都是減少不必要的DOM操作&…