前言
在寫網(wǎng)易云音樂以及3GShare包括后面的學(xué)生管理系統(tǒng)時,用到許多界面?zhèn)髦捣椒ǎ刈珜懖┛陀涗浤壳皩W(xué)過的幾種多界面?zhèn)髦捣椒?/p>
一、屬性傳值
屬性傳值是通過定義屬性并設(shè)置值來實(shí)現(xiàn)傳遞數(shù)據(jù)的方式,多用于前一個頁面向后一個頁面?zhèn)髦?/mark>
假設(shè)有兩個視圖控制器:ViewControllerA 和 ViewControllerB。
在 ViewControllerA.h 文件中定義一個屬性:
#import <UIKit/UIKit.h>
@interface ViewControllerA : UIViewController
@property (nonatomic, strong) NSString *dataToPass;
@end
在 ViewControllerA.m 文件中設(shè)置一個按鈕點(diǎn)擊事件,當(dāng)點(diǎn)擊按鈕時,跳轉(zhuǎn)到 ViewControllerB 并傳遞數(shù)據(jù):
#import "ViewControllerA.h"
#import "ViewControllerB.h"
@implementation ViewControllerA
- (void)buttonTapped {
ViewControllerB *viewControllerB = [[ViewControllerB alloc] init];
viewControllerB.receivedData = self.dataToPass;
[self.navigationController pushViewController:viewControllerB animated:YES];
}
@end
在 ViewControllerB.h 文件中定義一個屬性來接收從 ViewControllerA 傳遞過來的數(shù)據(jù):
#import <UIKit/UIKit.h>
@interface ViewControllerB : UIViewController
@property (nonatomic, strong) NSString *receivedData;
@end
在 ViewControllerB.m 文件中可以使用接收到的數(shù)據(jù)進(jìn)行相應(yīng)的操作:
> #import "ViewControllerB.h"
>
> @implementation ViewControllerB
>
> - (void)viewDidLoad {
> [super viewDidLoad];
>
> // 使用接收到的數(shù)據(jù)
> NSLog(@"Received data: %@", self.receivedData); }
>
> @end
這樣,當(dāng)在 ViewControllerA 視圖控制器中點(diǎn)擊按鈕,切換到 ViewControllerB 視圖控制器時,就可以將 dataToPass 的值傳遞給 receivedData,并在 ViewControllerB 中使用接收到的數(shù)據(jù)。
二、協(xié)議傳值
協(xié)議傳值是通過定義協(xié)議和代理方法,在不同的視圖控制器之間傳遞數(shù)據(jù)的方式。多用于后一個頁面將數(shù)據(jù)回傳給前一個頁面
這里以3GShare注冊界面回傳賬號密碼給登錄界面的協(xié)議傳值作為示例并講解其步驟:
-
定義協(xié)議:
在發(fā)送數(shù)據(jù)的視圖控制器(通常是源視圖控制器)的頭文件中定義一個協(xié)議,其中包含需要傳遞的數(shù)據(jù)的代理方法。
這步需要我們定義協(xié)議的名稱后再去定義回傳數(shù)據(jù)的方法 -
聲明代理屬性:
在發(fā)送數(shù)據(jù)的視圖控制器的頭文件中聲明一個代理屬性,用于保存代理對象。 -
觸發(fā)代理方法:
在發(fā)送數(shù)據(jù)的視圖控制器中,在適當(dāng)?shù)臅r機(jī),觸發(fā)代理方法,并將需要傳遞的數(shù)據(jù)作為參數(shù)傳遞給代理方法。 -
實(shí)現(xiàn)代理方法:
在接收數(shù)據(jù)的視圖控制器,也就是前一個控制器(通常是目標(biāo)視圖控制器)中,遵循協(xié)議并實(shí)現(xiàn)代理方法,以接收傳遞過來的數(shù)據(jù)。 -
設(shè)置代理:(這是最重要的一步,很多人會忘了這步)
在發(fā)送數(shù)據(jù)的視圖控制器中,在跳轉(zhuǎn)到接收數(shù)據(jù)的視圖控制器之前,設(shè)置目標(biāo)視圖控制器的代理為當(dāng)前視圖控制器。
通俗的理解就是將后面的視圖控制器的代理對象設(shè)為前一個視圖控制器,讓后一個為前一個代理,也就是手下替老板做事,然后將結(jié)果告訴老板。
三、block傳值
block傳值同樣也用于后面向前面?zhèn)髦?/mark>,與協(xié)議傳值有些相似的地方,但它使用代碼塊進(jìn)行傳值
- 定義 Block:
在發(fā)送方(當(dāng)前視圖控制器)中定義一個 Block 屬性,用于接收傳遞的數(shù)據(jù)。Block 的類型取決于你要傳遞的數(shù)據(jù)類型。
typedef void(^testblock)(NSString *);
@property(nonatomic, copy)testblock send;
typedef: 這是一個關(guān)鍵字,用于定義一個數(shù)據(jù)類型的別名,將后面的復(fù)雜類型定義簡化為一個簡潔的名字。
void(^testblock): 這部分是 Block 的類型定義,其中 testblock 是這個 Block 的別名,也就是我們定義的數(shù)據(jù)類型名。
(NSString *): 這是 Block 的參數(shù)列表,用括號括起來,表示 Block 接受一個 NSString 類型的參數(shù)。
這里需要注意的是:
typedef void(^testblock)(NSString *);:這是一個 Block 類型的定義,其中 testblock 是我們給這個 Block 類型起的別名,類似于自定義的數(shù)據(jù)類型。這個 Block 接受一個 NSString 類型的參數(shù),并且沒有返回值。
- 發(fā)送方設(shè)置 Block:
在發(fā)送方視圖控制器中設(shè)置 Block,將需要傳遞的數(shù)據(jù)作為 Block 的參數(shù)傳入,并執(zhí)行 Block。
在這段代碼中,我將self.textField.text作為參數(shù)傳入我的代碼塊,前一個視圖控制器接收到的信息就是我傳進(jìn)去的參數(shù) - 接收數(shù)據(jù):
在需要接受的地方將傳回來的值進(jìn)行使用,這里的(NSString *sendtext)中的sendtext就是回傳回來的值
這里給出block傳值的實(shí)現(xiàn)動畫
四、KVO傳值
KVO(Key-Value Observing)是一種iOS編程中的一種觀察者模式,用于監(jiān)聽對象屬性值的變化。通過KVO,一個對象可以監(jiān)視另一個對象的屬性,當(dāng)被監(jiān)視的屬性值發(fā)生變化時,會收到通知并執(zhí)行相應(yīng)的操作。
KVO實(shí)現(xiàn)步驟:
- 注冊觀察者:
在需要監(jiān)聽屬性變化的對象(被觀察者)中,調(diào)用
addObserver:forKeyPath:options:context:
方法來注冊觀察者。這個方法告訴系統(tǒng)哪個對象要觀察哪個屬性,以及觀察的選項(xiàng)和上下文。
假設(shè)我們有一個 Person 類,其中有一個屬性 age,我們希望在另一個視圖控制器中監(jiān)聽 age 的變化。
self.person = [[Person alloc] init];
self.person.age = 3;
// 注冊觀察者
[self.person addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
observer: 這是觀察者對象,即要接收屬性變化通知的對象。通常是當(dāng)前視圖控制器或其他感興趣的對象。
keyPath: 要監(jiān)聽的屬性的名稱,以字符串表示。當(dāng)該屬性的值發(fā)生變化時,KVO 就會通知觀察者。
options: 一個枚舉值,用于指定監(jiān)聽的選項(xiàng)。這個參數(shù)可以設(shè)置為多個選項(xiàng)的組合,使用按位或(|)進(jìn)行連接。常見的選項(xiàng)有:
NSKeyValueObservingOptionNew: 當(dāng)屬性的值發(fā)生變化時,提供新的屬性值作為通知的參數(shù)。
NSKeyValueObservingOptionOld: 當(dāng)屬性的值發(fā)生變化時,提供舊的屬性值作為通知的參數(shù)。
NSKeyValueObservingOptionInitial: 在添加觀察者時,立即發(fā)送一次通知,提供當(dāng)前屬性的值作為通知的參數(shù)。
NSKeyValueObservingOptionPrior: 在屬性值發(fā)生實(shí)際變化之前,先發(fā)送一次通知,提供舊的屬性值作為通知的參數(shù)。
context: 這是一個指針類型的參數(shù),用于傳遞額外的上下文信息。通常情況下可以傳入 NULL,表示不需要傳遞上下文信息。如果你需要在觀察者中處理一些額外的信息,可以使用自定義的指針類型來傳遞數(shù)據(jù)。
- 實(shí)現(xiàn)監(jiān)聽方法:
在觀察者對象中,實(shí)現(xiàn)一個監(jiān)聽方法- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
,監(jiān)聽方法會在被觀察的屬性發(fā)生變化時被調(diào)用。
// 實(shí)現(xiàn)監(jiān)聽方法,當(dāng) age 屬性變化時會調(diào)用該方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"age"]) {
// 獲取舊值和新值
NSNumber *oldAge = [change valueForKey:NSKeyValueChangeOldKey];
NSNumber *newAge = [change valueForKey:NSKeyValueChangeNewKey];
NSLog(@"old age: %@ --- new age: %@", oldAge, newAge);
}
}
我們常用[change valueForKey:NSKeyValueChangeOldKey]
與[change valueForKey:NSKeyValueChangeNewKey]
來獲得我們的新值與舊值
- 移除觀察者:
在觀察者對象不再需要監(jiān)聽屬性變化時,記得調(diào)用 removeObserver:forKeyPath: 方法來移除觀察者,避免潛在的內(nèi)存泄漏。
實(shí)現(xiàn)效果
五、KVO的自動觸發(fā)與手動觸發(fā)
一般來說,我們使用KVO來監(jiān)聽對象的屬性,都是針對對象的單個屬性,但如果我們想監(jiān)聽一個集合,上面的方法就不適用了。這是因?yàn)镵VO的觸發(fā)類型分為自動與手動,我們上面講述的僅僅只是KVO的自動觸發(fā)
- 自動觸發(fā)KVO通常適用于以下情況:
監(jiān)聽對象屬性的變化:對于簡單的對象屬性,例如字符串、數(shù)字等,可以使用自動觸發(fā)KVO。就像我們上面設(shè)置了一個age,這就是簡單的對象屬性,只需要在被監(jiān)聽屬性聲明前加上@property關(guān)鍵字,并使用nonatomic修飾符即可。當(dāng)屬性值發(fā)生變化時,KVO會自動發(fā)送通知給觀察者。
使用@synthesize合成屬性:在使用@synthesize合成屬性時,如果指定了觀察者,則合成的屬性會自動觸發(fā)KVO通知。
- 手動觸發(fā)KVO通常適用于以下情況:
監(jiān)聽集合屬性的變化:集合屬性不支持自動觸發(fā)KVO通知,所以需要使用手動觸發(fā)KVO來監(jiān)聽集合屬性的變化。在修改集合屬性之前調(diào)用willChangeValueForKey:方法,在修改完成后調(diào)用didChangeValueForKey:方法。
監(jiān)聽非對象類型屬性:對于非對象類型的屬性,例如C語言基本數(shù)據(jù)類型,由于它們不是對象,無法自動觸發(fā)KVO。這時需要使用手動觸發(fā)KVO通知來監(jiān)聽屬性的變化。
自定義KVO通知:有時候我們可能需要在一些特定的場景下自定義KVO通知,這時可以使用手動觸發(fā)KVO來實(shí)現(xiàn)。
這樣一來我們知道了如果對象的設(shè)置的屬性是集合的話,我們需要手動來觸發(fā)我們的KVO,接下來講一下原因:
當(dāng)我們監(jiān)聽一個集合屬性時(如NSMutableArray類型屬性),KVO默認(rèn)只會監(jiān)聽這個集合屬性的變化,但不會監(jiān)聽集合中的元素的變化。也就是說,如果我們直接修改集合中的元素(比如使用addObject:方法),而沒有通過集合屬性的setter方法進(jìn)行修改,KVO是無法察覺到集合中元素的變化的。
通俗的講,我們的KVO的自動觸發(fā)只適用于用setter方法修改的屬性,也就是用點(diǎn)語法修改的屬性都會自動觸發(fā)我們的KVO,但是集合無法用點(diǎn)語法進(jìn)行修改,那么這個時候就需要用到我們的手動觸發(fā)
為了解決這個問題,我們需要在修改集合屬性之前,調(diào)用willChangeValueForKey:方法,在修改之后,調(diào)用didChangeValueForKey:方法。這樣做可以手動觸發(fā)KVO通知,告訴KVO機(jī)制集合屬性發(fā)生了變化,使得KVO能夠正確地監(jiān)聽到集合中元素的變化。
-
我們在原來的對象中添加一個集合屬性
-
我們將簡單的對象屬性添加到我們的集合中,同時將我們的監(jiān)聽對象改為我們的集合
此時我還沒有去手動觸發(fā)我們的KVO,我們試著運(yùn)行一下程序:
我們的內(nèi)容并沒有變化,這也說明了我們并沒有觸發(fā)我們的KVO的監(jiān)聽方法 -
接下來我們將實(shí)現(xiàn)手動監(jiān)聽
willChangeValueForKey::在對象屬性發(fā)生變化之前調(diào)用,用于通知KVO即將開始監(jiān)聽屬性的變化。
didChangeValueForKey::在對象屬性發(fā)生變化后調(diào)用,用于通知KVO屬性的變化已經(jīng)完成。
willChangeValueForKey 和 didChangeValueForKey 方法通過配對,這樣可以通知觀察者(監(jiān)聽者)該屬性值即將發(fā)生變化和已經(jīng)發(fā)生變化。這對于手動觸發(fā) KVO 監(jiān)聽非常重要,需要注意的是,這兩種方法一定是配對出現(xiàn)的
自此,我們實(shí)現(xiàn)了我們KVO對集合類型的監(jiān)聽
六、通知傳值
通知傳值是一種在不同對象之間進(jìn)行信息傳遞的方式,它使用了通知中心來實(shí)現(xiàn)觀察者模式,允許一個對象在發(fā)生改變時通知其他觀察者對象。其可以用于跨多個界面?zhèn)髦?/mark>
其步驟簡單分為四步
- 創(chuàng)建并發(fā)送通知:
首先,在發(fā)送者對象中創(chuàng)建一個通知,并指定通知的名稱(通常使用字符串來表示)??梢酝ㄟ^NSNotification類或NSNotificationName宏來創(chuàng)建通知。需要傳遞信息給其他對象時,可以通過NSNotificationCenter的postNotificationName:object:userInfo:方法來發(fā)送通知。在發(fā)送通知時,可以附帶一些額外的信息(如字典)作為通知的userInfo參數(shù)。
我們接下來詳細(xì)看一下創(chuàng)建與發(fā)送通知的方法:
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
aName: 通知的名稱,是一個字符串類型的參數(shù)。通過這個名稱來標(biāo)識不同的通知。發(fā)送通知時,需要指定要發(fā)送的通知的名稱,接收通知時,也需要監(jiān)聽相應(yīng)名稱的通知。
anObject: 通知的發(fā)送者,是一個可選參數(shù)。通知可以有一個發(fā)送者,表示是哪個對象發(fā)送了這個通知。通常情況下,我們不需要傳遞發(fā)送者,可以傳入nil。
aUserInfo: 通知的附加信息,是一個可選參數(shù)。可以通過這個字典傳遞一些額外的信息給接收通知的對象。通常情況下,我們在發(fā)送通知時,可以將一些需要傳遞的數(shù)據(jù)放入這個字典中。
這里需要注意的是我們的userInfo:后跟的參數(shù)是一個字典類型,我們通過傳回一個字典,并通過查找字典的key來獲取我們需要的數(shù)據(jù)
- 注冊觀察者:
在接收者對象中,需要注冊對某個通知感興趣的觀察者。這樣,當(dāng)通知被發(fā)送時,觀察者就能接收到通知并做出相應(yīng)的響應(yīng)。使用NSNotificationCenter的addObserver:selector:name:object:方法來注冊觀察者。
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
observer: 要注冊的觀察者對象。觀察者對象將接收到與指定通知名稱匹配的通知。通常,這個參數(shù)是當(dāng)前對象,即要接收通知的對象。
selector: 觀察者對象中用于處理通知的方法(函數(shù))的選擇器。這個方法必須帶有一個參數(shù),通常是 NSNotification 對象,用于接收傳遞的通知信息。該方法的聲明通常形如 -(void)methodName:(NSNotification *)notification;。
name: 要觀察的通知名稱。這是一個字符串,用于標(biāo)識要監(jiān)聽的通知。當(dāng)發(fā)送通知時,只有與這個名稱匹配的通知才會被發(fā)送給觀察者。
object: 通知發(fā)送者的對象。如果設(shè)置為 nil,則會接收任何發(fā)送給指定名稱的通知。如果設(shè)置為特定對象,只有該對象發(fā)送的與指定名稱匹配的通知才會被發(fā)送給觀察者。
這里有一點(diǎn)需要特別注意的就是要觀察的通知名稱必須于創(chuàng)造的通知的名稱相同,否則無法接受來自通知的數(shù)據(jù)
-
接收通知:
接收者對象需要實(shí)現(xiàn)一個方法,用于處理接收到的通知。這個方法是在觀察者注冊時通過selector參數(shù)指定的。當(dāng)通知被發(fā)送時,通知中心會調(diào)用這個方法,并傳遞相關(guān)的信息給觀察者。
以我們上面的方法為例,receiveNotice:就是我們處理通知的方法 -
移除觀察者:
在接收者對象被銷毀之前,需要將其從通知中心中移除,避免出現(xiàn)潛在的內(nèi)存泄漏??梢允褂肗SNotificationCenter的removeObserver:系列方法來移除觀察者。
- (void)dealloc {
// 移除通知觀察者
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
接下來讓我們看一下效果:
這樣一來,我們就實(shí)現(xiàn)了通知傳值文章來源:http://www.zghlxwxcb.cn/news/detail-602368.html
總結(jié)
多界面?zhèn)髦凳莍OS中十分重要的知識,筆者還有很多知識還沒學(xué)到例如KVC等,以后學(xué)到了會加以補(bǔ)充文章來源地址http://www.zghlxwxcb.cn/news/detail-602368.html
到了這里,關(guān)于【iOS】多界面?zhèn)髦档奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!