مساحة اعلانية

آخر المواضيع

استطلاع اطار عمل اتصال Multipeer: بدء تشغيل المشروع

استطلاع اطار عمل اتصال Multipeer: بدء تشغيل المشروع
استطلاع اطار عمل اتصال Multipeer: بدء تشغيل المشروع

كما هو الحال مع كل اطلاق رئيسي, فان iOS 7 يتضمن عددا من APIs الجديدة التي يمكن للمطورين ان يستفسدوا من هذه التطبيقات. في هذا الدرس, سنلقي نظرة على اطار العمل الجديد المقدم في iOS 7, اطار عمل اتصال Multipeer. يضيف اطار العمل هذا دعما لكشف, الاتصال بـ, و التواصل مع الخدمات القريبة, مثل أجهزة iOS. في هذا الدرس, سأريك كيفية انشاء لعبة متعددة اللاعبين و بسيطة باستخدام اطار العمل الجديد هذا.


المقدمة

تم تقديم سلسلة واسعة من APIs الجدد في iOS 7, متيحا للمطورين خيارات و فرص جديدة. اطار العمل الذي لفت نظري هو اطار عمل اتصال Multipeer. يقوم بعرض عددا من الميزات الانيقة التي ارغب بعرضها في هذا الدرس.

1. نظرة عامة عن اطار العمل

قبل البدء بالعمل مع اطار عمل اتصال Multipeer, من الضروري معرفة اطار العمل أولا. ان الهدف من اطار العمل هو السماح للأجهزة القريبة بالاتصال مع ببعضها الاخر, و الذي يتضمن تبادل البيانات مثل الصور و الفيديوهات.

من الضروري فهم معنى مصطلح القرب في مجال اطار عمل اتصال Multipeer. يعرف اطار العمل الأجهزة القريبة كالأجهزة المرتبطة عن طريق نفس شبكة Wi-Fi أو يمكن التواصل عبر Bluetooth. لا يمكن استخدام اطار عمل اتصال Multipeer للتواصل بين الأجهزة عبر الشبكة على سبيل المثال. لم يصمم اطار عمل اتصال Multipeer للعمل بهذا الشكل.

قبل ان يبدأ الجهازان بتبادل البيانات, يجب ان يرتبطا ببعضهما الاخر أولا. يتضمن ذلك مرحلتين, 1 اكتشاف الأجهزة القريبة و 2 تأسيس جلسة بينهما. في مرحلة الاكتشاف, يقوم احد الأجهزة بالبحث عن او تصفح الأجهزة المجاورة. اذا رغب جهاز ما ان يكون قابل للاكتشاف, يجب ان يعلن عن نفسه. هذا يمكن الجهاز من إيجاد الجهاز المعلن عنه.

عندما يجد الجهاز الأول الجهاز المعلن عنه, يقوم بارسال طلب الى الجهاز لانشاء ارتباط, يمكن للمعلن ان يقبله او يرفضه.  مع قبول الدعوة, يتم انشاء جلسة, مما يعني ان كلا الجهازين يمكنهما البدء بالتواصل. لدى الارتباط بين هذين الجهازين واحد من ثلاثة حالات, 1 ليس متصلا, 2 جاري الاتصال, أو 3 متصل. في أي وثت, يمكن للجهاز ان ينهي الجلسة, التي تقوم باغلاق الاتصال بين الأجهزة.

التصفح

يعمل اطار عمل اتصال Multipeer على تزويد جادتين للتصفح للأجهزة الأخرى. يكمن الخيار الأول في استخدام صنف فرعي لمتحكم مشهد متخصص يقوم بعمل كل شيء لك. هذه هي النظرية التي سنستخدمها في هذا الدرس. لقد تم الاهتمام بارسال الدعوة و انشاء جلسة لك. بالطبع, هذا لا يتناسب مع كل قضية استخدام. يقضي الخيار الاخر بانشاء المنطق بشكل يدوي لتصفح الأجهزة القريبة. و لكن لن نقوم بمناقشة هذا الموضوع في هذا الدرس.

في مجال اطار عمل اتصال Multipeer, يتم الإشارة الى جهاز كنظير و كل نظير لديه معرف خاص و peerID و اسم عرض. الأخير هو الاسم الذي يعطيه المستخدم لجهازه, على سبيل المثال, "Gabriel's iPhone". يعد اسم العرض جزء مهما من المعلومات ذلك لانها الطريقة الوحيدة التي يعرف من خلالها المستخدمون اية أجهزة تنتمي لمن.

البيانات

تقتصر البيانات التي يمكن تناقلها بين النظيرين على ثلاثة أنواع, نماذج 1 NSData, موارد 2, مثل الملفات و الوثائق, و  3البيانات الجارية. في هذا الدرس, سأريك كيفية ارسال و استلام كائنات البيانات, أي نماذج 1 NSData. لن نقوم بتغطية الموارد و الجداول في هذا الدرس.

يمكن ارسال البيانات باستخدام واحد من وضعين, وضع يعتمد عليه, و هو بطيء, و لكنه يضمن ان البيانات المنقولة  يتم استلامها من قبل المستلم, و وضع لا يعتمد عليه, و الذي لديه أداء عال, و لكنه لا يضمن ان البيانات المرسلة من شخص قد تم استلامها من قبل الشخص الاخر أو استلامها بالتسلسل الذي أرسلت به. يعتمد أي وضع تختاره على احتياجات تطبيقك.

هذا كل ما تحتاجه معرفته عن بدء اطار عمل اتصال Multipeer. دعني انهي هذه المقدمة بذكر الأصناف الاربعة التي سنعمل بها في هذا الدرس.

  • MCPeerID: يمثل نموذج الصنف هذا نظيرا في جلسة multipeer.
  • MCSession: يستخدم هذا الصنف لادارة الاتصال بين النظائر في الجلسة.
  • MCAdvertiserAssistant: يستخدم هذا الصنف للمساعدة بالإعلان عن نظيرا للأجهزة القريبة.
  • MCBrowserViewController: يتولى صنف  UIViewControllerالفرعي تقديم الأجهزة القريبة للمستخدم و المساعدة بتأسيس جلسة multipeer.

2. نظرة عامة عن التطبيق

دعونا نأخذ نظرة خاطفة عن تطبيق النموذجي الذي على الوشك البدء بإنشائه. الهدف من هذا الدرس هو عرض كيفية انشاء الارتباط بين الأجهزة القريبة و تبادل البيانات, نماذج NSData, بين الأجهزة المتصلة.

انني متأكد انك تفطر بعدة قضايا للاستخدام من اجل اطار عمل اتصال Multipeer. ولكن في هذا الدرس, اخترت لعبة بسيطة, سميتها احزر الرقم السري. في اللعبة, يقوم شخص باختيار رقم ضمن سلسة معرفة مسبقا حيث يجب على اللاعبين الاخرين ان يخمنوا. بعد كل تخمين, يخبر مضيف اللعبة, الشخص الذي اختار الرقم, اللاعب فيما اذا أعطت تخمينا صحيحا و فيما اذا كان الرقم السري اعلى او أخفض. انها بهذا البساطة, اليس كذلك؟

3. بدء تشغيل المشروع

دعونا نبدأ بانشاء مشروعا جديدا في Xcode كما هو موضح اسفلا. اختر عارضة تطبيق المشهد الفردي من عارضة القوائم في مجموعة تطبيق iOS و اضغط تاليا.

اعط مشروعك اسما, ادخل معرفة الشركة, و اختر iPhone من قائمة الأجهزة. انقر الزر التالي للمتابعة.

اخبر Xcode اين ترغب بحفظ ملفات المشروع و انقر على زر الانشاء للمتابعة.

4. معالجة اتصال Multipeer

يجب ان نضيف كود لانشاء و إدارة جلسة multipeer. من أجل ان نتأكد اننا لا نكرر شيء ما, سنمركز هذا المنطق في صنف تقليدي يمكننا إعادة استخدامه في مشروعنا. هذا ما سنفعله في هذا القسم.

الخطوة 1

قم بانشاء صنف جديد Xcode من خلال اختيار جديد < ملف... من قائمة الملف. اختر صنف Objective-C من مجموعة iOS Cocoa Touch و انقر تاليا.

سم الصنف الجديد MPCHandler, عين صنفها الفرعي لـ NSObject, و انقر الزر تاليا. اخبر Xcode اين ترغب بتخزين ملفات الصنف و انقر انشاء.

الخطوة 2

افتح ملف الرأس التابع لصنف MPCHandler و ابدأ  بإضافة حالة مستوردة لاطار عمل اتصال Multipeer كما هو موضح اسفلا.     

#import <Foundation/Foundation.h>

#import <MultipeerConnectivity/MultipeerConnectivity.h>



@interface MPCHandler : NSObject



@end

قبل Xcode 5,, كنا قد احتجنا أولا الى ربط المشروع باطار عمل اتصال Multipeer. و لكن في  Xcode 5, هذا لم يعد ضروريا بفضل ميزة الربط الذاتي للمجمع. يعد المجمع ذكي كفاية ليربط المشروع بالمكتبات و اطر العمل المناسبين, مما يعني اننا لن نقلق حيال هذا.   

بما ان صنف MPCHandler سيكون مسؤولا عن إدارة جلسة multipeer, يجب ان تتوافق مع بروتوكول MCSessionDelegate كما هو موضح اسفلا. سنعلن أيضا عن أربعة خواص ستساعدنا مع هذه المهمة.

      

#import <Foundation/Foundation.h>

#import <MultipeerConnectivity/MultipeerConnectivity.h>



@interface MPCHandler : NSObject <MCSessionDelegate>



@property (nonatomic, strong) MCPeerID *peerID;

@property (nonatomic, strong) MCSession *session;

@property (nonatomic, strong) MCBrowserViewController *browser;

@property (nonatomic, strong) MCAdvertiserAssistant *advertiser;



@end

لاحظ ان هذه هي الأصناف التي ذكرتها مسبقا في هذا الدرس. سيمثل كائن peerID الجهاز و سيتم تضمينه بكل ارتباط بين النظائر بالإضافة الى اية بيانات يتم تبادلها بين النظائر. يحوي كائن الجلسة و يدير جلسة multipeer.  سنتحدث أكثر عن هذا الصنف لاحقا في هذا الدرس. يحوي كائن المتصفح على مرجع لنموذج MCBrowserViewController, الذي سنستخدمه لاكتشاف الأجهزة القريبة و انشاء جلسة multipeer بينهم. يتولى كائن المعلن مهمة الإعلان عن الجهاز بالإضافة الى معالجة الدعوات القادمة.

قبل ان نأخذ نظرة على انشاء صنف MPCHandler, يجب ان نقوم بإعلان الطرق الأربعة كما هو موضح اسفلا. الطريقة الأولى, setupPeerWithDisplayName:, تقبل معاملة واحدة, العرض أو الاسم العام الذي سيستخدم للجهاز. تأخذ طريقة advertiseSelf: معاملة واحدة لتدل فيما اذا يجب على الجهاز ان يكون مرئيا للأجهزة الأخرى. ستكون طريقة setupSession مسؤولة عن تعيين جلسة multipeer, بينما ستكون طريقة setupBrowser مسؤولة عن تعيين نموذج MCBrowserViewController.

#import <Foundation/Foundation.h>

#import <MultipeerConnectivity/MultipeerConnectivity.h>



@interface MPCHandler : NSObject <MCSessionDelegate>



@property (nonatomic, strong) MCPeerID *peerID;

@property (nonatomic, strong) MCSession *session;

@property (nonatomic, strong) MCBrowserViewController *browser;

@property (nonatomic, strong) MCAdvertiserAssistant *advertiser;



- (void)setupPeerWithDisplayName:(NSString *)displayName;

- (void)setupSession;

- (void)setupBrowser;

- (void)advertiseSelf:(BOOL)advertise;



@end

دعونا نبدأ بانشاء طريقة setupPeerWithDisplayName:. في هذه الطريقة, سنقوم بانشاء صنف MCPeerID و تمريرها الى displayName. هذه هي.

      

- (void)setupPeerWithDisplayName:(NSString *)displayName {

    self.peerID = [[MCPeerID alloc] initWithDisplayName:displayName];

}

في setupSession, سنقوم بانشاء نموذج MCSession من خلال تمرير متغير نموذج peerID و تعيين مفوض الجلسة لنموذج  MPCHandler.  من الضروري استدعاء setupPeerWithDisplayName: قبل بدء تشغيل الجلسة, لان متغير نموذج peerID لا يمكن ان يكون صفرا.

- (void)setupSession {

    self.session = [[MCSession alloc] initWithPeer:self.peerID];

    self.session.delegate = self;

}



لا يمكن ان يكون انشاء setupBrowser اسهل من ذلك. جل ما نفعله في هذه الطريقة هو بدء نموذج MCBrowserViewController من خلال نوع الخدمة و الجلسة التي ننشئها في setupSession. تكون المعاملة الأولى من نوع الخدمة للتصفح. تعرف الخدمة بشكل مميز و يجب انه تكون بطول من 1 الى 15 رمز. لاحظ ان نوع الخدمة يمكن ان يحوي فقط على احرف    ASCII صغيرة, ارقام, و خطوط صغيرة تصل بين الكلمات. نقوم بتخزين مرجع لمتحكم مشهد المتصفح_ متغير نموذج المتصفح.

      

- (void)setupBrowser {

    self.browser = [[MCBrowserViewController alloc] initWithServiceType:@"my-game" session:_session];

}



في advertiseSelf:, نقوم بانشاء صنف MCAdvertiserAssistant من خلال تمرير نفس نوع الخدمة الذي استخدمناه لبدء نموذج MCBrowserViewController. سنقوم أيضا بتمرير كائن الجلسة و تعيين معلومات الاكتشاف صفر بما اننا لن نقوم بتزويد اية معلومات عن الاكتشاف حول الجهاز الى الأجهزة القريبة خلال مرحلة الاكتشاف.

اذا تم تعيين معاملة المعلن advertiseSelf: نعم, فاننا سندعو للبدء بكائن MCAdvertiserAssistant من اجل البدء المساعد و خدمة الإعلان. اذا تم تعيين الإعلان لا, سنوقف مساعد الإعلان و نعين خاصية الإعلان صفر.


- (void)advertiseSelf:(BOOL)advertise {

    if (advertise) {

        self.advertiser = [[MCAdvertiserAssistant alloc] initWithServiceType:@"my-game" discoveryInfo:nil session:self.session];

        [self.advertiser start];

       

    } else {

        [self.advertiser stop];

        self.advertiser = nil;

    }

}

الخطوة 3

لابد انك لاحظت ان Xcode يعرض بعض الانذارات بما اننا لم ننشئ طرق MCSessionDelegate المطلوبة بعد. دعونا نتخلص من هذه التحذيرات من خلال انشاء بروتوكول MCSessionDelegate. يعرض اسفلا الطرق التي سننشئها.       

- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {



}



- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {



}



- (void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress {



}



- (void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error {



}



- (void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID {



}

دعونا نلقي نظرة على كل طريقة بروتوكول.

session:peer:didChangeState:: طريقة التفويض هذه تستدعى كل مرة تتغير فيها حالة اتصال النظير. تذكر ان هنالك ثلاث حالات محتملة, غير متصل, جاري الاتصال, و متصل. سنستخدم هذه الطريقة لنتابع النظراء الذين يتصلون و يقطعون الاتصال من اللعبة.

session:didReceiveData:fromPeer:: تستدعى هذه الطريقة كل مرة يستلم الجهاز بيانات من نظير اخر.

session:didStartReceivingResourceWithName:fromPeer:withProgress:: عندما يبدأ التطبيق باستلام المورد, مثل ملف, من نظير اخر, يتم استدعاء هذه الطريقة. لن نغطي هذه الطريقة هذا الدرس.

session:didFinishReceivingResourceWithName:fromPeer:atURL:withError:: و مكا يوحي الاسم, فان هذه الطريقة تستدعى عندما يتم استلام مورد من قبل التطبيق.

session:didReceiveStream:withName:fromPeer:: تستدعى هذه الطريقة  عندما يتم استلام جدول من قبل التطبيق. بما اننا لن نعمل مع الجداول, لن نغطي هذه الطريقة أيضا.

على الرغم من اننا لن نستخدم الرق الثلاث الأخيرة في هذا الدرس, يجب ان ننشئهم في متحكم المشهد للتخلص من تحذيرات المجمع.

في session:peer:didChangeState: و session:didReceiveData:fromPeer:, سننشر اشعارا باستخدام مركز الاشعارات لنحرص على ان يتم اعلام أي جزء من التطبيق حول الحدث. يسمح هذا لمتحكمي المشهد بإضافة انفسهم كمراقب لهذه الاشعارات و الإجابة عنهم ان امكن بشكل مناسب. هذا يعني أيضا ان انشاء هذه الطرق هي بسيطة بعض الشيء كما يمكنك ان ترى اسفلا.

      

- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {

    NSDictionary *userInfo = @{ @"peerID": peerID,

                                @"state" : @(state) };

   

    dispatch_async(dispatch_get_main_queue(), ^{

        [[NSNotificationCenter defaultCenter] postNotificationName:@"MPCDemo_DidChangeStateNotification"

                                                            object:nil

                                                          userInfo:userInfo];

    });

}



سنبدأ بانشاء قاموس userInfo للاشعارات و نشر الاشعار من خلال تمرير القاموس كمعاملة لـ postNotificationName:object:userInfo:. لاحظ اننا نشرنا الاشعار داخل كتلة dispatch_async للتأكد من انه تم نشر الاشعار على الخيط الرئيسي. هذا مهم بما اننا لا يمكننا ان نضمن استدعاء طريقة التفويض على الخيط الرئيسي. و لكن الاشعار يحتاج الى ان ينشر على الخيط الرئيسي من اجل الحرص على ان يستلم كل مراقب الاشعار.

يبدو انشاء session:didReceiveData:fromPeer: مشابها جدا كما هو موضح اسفلا.   

- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {

    NSDictionary *userInfo = @{ @"data": data,

                                @"peerID": peerID };

   

    dispatch_async(dispatch_get_main_queue(), ^{

        [[NSNotificationCenter defaultCenter] postNotificationName:@"MPCDemo_DidReceiveDataNotification"

                                                            object:nil

                                                          userInfo:userInfo];

    });

}

اصبح صنف MPCHandler جاهزا للاستخدام و لذلك دعونا نبدأ الصنف في تفويض التطبيق.

الخطوة 4

افتح AppDelegate.h, اضف حالة مستوردة لصنف MPCHandler, و اعلن عن خاصية من نوع MPCHandler.    

#import <UIKit/UIKit.h>



#import "MPCHandler.h"



@interface AppDelegate : UIResponder <UIApplicationDelegate>



@property (strong, nonatomic) UIWindow *window;



@property (nonatomic, strong) MPCHandler *mpcHandler;



@end

انتقل الى ملف انشاء صنف AppDelegate و ابدأ متغير النموذج في application:didFinishLaunchingWithOptions:.

      

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    self.mpcHandler = [[MPCHandler alloc] init];

   

    return YES;

}

5. متحكم مشهد الخيارات

كما ذكرت مسبقا, سيكون لدى التطبيق متحكمان للمشهد. لدى عارضة تطبيق المشهد الفردي Xcode واحد افتراضي. دعونا نضيف واحدا اخرا الان. اختر جديد > ملف... من قائمة الملف, اختر صنف Objective-C من مجموعة iOS Cocoa Touch على اليسار, انقر تاليا.

تأكد ان الصنف الجديد يرث من UIViewController و عين حقل  الصنف لـ OptionsViewController. يجب ان تبقى الصناديق غير مفحوصة. اضغط للمتابعة.

اخبر Xcode اين ترغب بحفظ ملفات الصنف و اضغط انشاء.

6. واجهة تطبيق المستخدم

حان الوقت لانشاء واجهة تطبيق المستخدم. افتح لوح المشروع الأساسي (Main.storyboard) للبدء.

الخطوة 1

اختر متحكم المشهد في الوح و اختر تضمين > متحكم الملاحة من قائمة التحرر. بالنهاية يصبح متحكم المشهد متضمن في متحكم الملاحة و لوح الملاحة يضاف الى اعلى متحكم المشهد. 

اختر لوح ملاحة المتحكم, افتح مفتش اللواحق على اليمين, و عين حقل العنوان MPCDemo.

اسحب نموذجان UIBarButtonItem من مكتبة الكائنات الى لوح الملاحة و قم بتحديث عناوينهم كما هو موضح اسفلا.

هذا هو المشهد الذي سيراه و يستخدمه اللاعب لادخال التخمين, ارسله الى اللاعبين الاخرين, و ابق متابعا للتخمينات القديمة المعمولة مسبقا. لجعل كل هذه ممكننا, نحتاج الى إضافة حقل نصي, زران, و مشهد نصي. الق نظرة على الصورة التالية. قمت بإدخال لواحق لكل مشهد فرعي من اجل مساعدتك بهذه المهمة.


    UITextField

        Frame: X=20, Y=77, Width=280, Height=30

        Placeholder Text: Guess a number (1 - 100)...

    UIButton

        Frame: X=254, Y=115, Width=46, Height=30

        Title: Send

    UIButton

        Frame: X=20, Y=115, Width=48, Height=30

        Title: Cancel

    UITextView

        Frame: X=20, Y=153, Width=280, Height=395

        Text: Nothing (Delete the existing text)

        Background Color: Light Gray Color

        Text Color: White Color

        Editable: No

هذا هو كيف يجب ان يبدو متحكم المشهد بعد إضافة أربعة مشاهد فرعية الى المشهد.

الخطوة 2

قبل الانتقال الى OptionsViewController, دعونا نعلن و نربط المنافذ الضرورية و النشاطات في صنف ViewController. افتح ViewController.h و قم بتحديث محتوياته كما هو موضح اسفلا.

#import <UIKit/UIKit.h>



@interface ViewController : UIViewController



@property (weak, nonatomic) IBOutlet UITextField *txtGuess;

@property (weak, nonatomic) IBOutlet UITextView *tvHistory;

@property (weak, nonatomic) IBOutlet UIButton *btnSend;

@property (weak, nonatomic) IBOutlet UIButton *btnCancel;



- (IBAction)startGame:(id)sender;

- (IBAction)sendGuess:(id)sender;

- (IBAction)cancelGuessing:(id)sender;



@end

قم بالرجوع الى لوح المشروع الرئيسي, اختر نموذج ViewController, و اربط المنافذ و النشاطات بواجهة تطبيق المشهد. اذا لم تكن تعرف هذه الخطوة, دعني أريك كيفية عمل هذا. ابدأ بتوسيع مشهد متحكم المشهد لذا يمكنك ان ترى ارتباطها بالكائنات.

تقضي الخطوة التالية بالنقر على كونترول او انقر باليمين على كائن متحكم مشهد – MPCDemo في القائمة, الذي يجب ان يجلب نافذة سوداء. في هذه النافذة, يجب ان ترى النوافذ و النشاطات التي اعنا عنها مسبقا بالإضافة الى عدد من المواد الاخرى. لربط المنفذ بحقل النص, انقر الدائرة على يمين منفذ txtGuess و اسحب الى حقل النص في واجهة التطبيق. الق نظرة على الصورة التالية لتفهم العملية بشكل افضل.

قبل المتابعة, احرص على ان تربط كل منفذ و نشاط بالمشهد الموافق في واجهة التطبيق.

الخطوة 3

لصنف OptionsViewController نحتاج الى إضافة مشهد جديد للواجهة. اسحب نموذج UIViewController من مكتبة الكائنات الى الكانافا. و لربطها بالمشهد الموجود, انقر كونترول و انقر باليمين على مادة زر الوح المعرفة بالخيارات و اسحب الى متحكم المشهد الجديد. من النافذة السوداء, اختر ادفع من قائمة الخيارات.

اسم الصنف التابع الى افتراضي المشهد UIViewController, و لكن يجب تغيير هذا. في اسفل المشهد الثاني, اختر المادة الأولى, افتح متحري الهويات على اليمين, و عين حقل الصنف OptionsViewController في قسم الصنف التقليدي.



اختر لوح ملاحة متحكم المشهد, و عين اسم الخيارات في متحري اللواحق. اضف خمسة مشاهد فرعية لمتحكم المشهد كما هو موضح اسفلا.


    UITextField

        Frame: X=20, Y=77, Width=280, Height=30

        Placeholder Text: Your player name...

    UISwitch

        Frame: X=136, Y=141, Width=51, Height=31

    UIButton

        Frame: X=76, Y=231, Width=168, Height=30

        Title: Browse for other players

    UITextView

        Frame: X=20, Y=269, Width=280, Height=241

        Text: Nothing (Delete existing contents)

        Editable: NO

    UIButton

        Frame: X=121, Y=518, Width=78, Height=30

        Title: Disconnect

يجب ان تعطيك الصورة التالية فكرة عما يجب ان تكون عليه النتيجة النهائية.

الخطوة 4

لانهاء واجهة التطبيق من متحكم مشهد الخيارات, دعونا نوضح بعض المنافذ و النشاطات في ملف متحكم المشهد. افتح OptionsViewController.h و قم بتحديث محتوياته كما هو موضح اسفلا.

      

#import <UIKit/UIKit.h>



@interface OptionsViewController : UIViewController



@property (weak, nonatomic) IBOutlet UITextField *txtPlayerName;

@property (weak, nonatomic) IBOutlet UISwitch *swVisible;

@property (weak, nonatomic) IBOutlet UITextView *tvPlayerList;



- (IBAction)disconnect:(id)sender;

- (IBAction)searchForPlayers:(id)sender;

- (IBAction)toggleVisibility:(id)sender;



@end

قم بإعادة زيارة لوح المشروع الرئيسي و اربط المنافذ و النشاطات مع واجهة التطبيق. لقد اكملنا واجهة تطبيق اللعبة و نحن جاهزين للبدء بكتابة بعض الكودات لنرى اطار عمل اتصال Multipeer يعمل.

7. اكتشاف اللاعبين الاخرين

تقتضي الخطوة التالية بانشاء صنف OptionsViewController لأننا نحتاج الى الربط بين الجهازين قبل ان نلعب اللعبة و نركز على منطق اللعبة. ارغب بالبدء من خلال اطلاعك على اكتشاف اللاعبين القريبين (الأجهزة) من خلال استخدام حلوا يبنى داخليا في اطار عمل اتصال Multipeer.

الخطوة 1

تقتضي الخطوة الأولى بالحصول على كائن mpcHandler الذي انشأناه مسبقا في صنف AppDelegate. يوحي هذا بأننا نحتاج الى الوصول الى كائن AppDelegate عبر تفويض التطبيق. افتح OptionsViewController.m, و قم بالملاحة الى اعلى الملف, و اضف الحالة المستوردة لملف صنف AppDelegate.

                  

#import "OptionsViewController.h"



#import "AppDelegate.h"



@interface OptionsViewController ()



@end

في توسيع الصنف, سنعلن عن خاصية ستخزن  مرجعا لنموذج  AppDelegate.

#import "OptionsViewController.h"



#import "AppDelegate.h"



@interface OptionsViewController ()



@property (strong, nonatomic) AppDelegate *appDelegate;



@end

لن نحتاج الى بدء نموذج AppDelegate لان ذلك يحدث أوتوماتيكيا خلال اطلاق التطبيق. في طريقة viewDidLoad التابعة لمتحكم المشهد, سنأخذ المرجع الى تفويض التطبيق و نعين كائن  mpcHandler. سنقوم ببدء peerID و خواص جلسة صنف MPCHandler من خلال استدعاء الطرق التي اعلنا عنها و انشأناها مسبقا في هذا الدرس.

      

- (void)viewDidLoad {

    [super viewDidLoad];



    self.appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];



    [self.appDelegate.mpcHandler setupPeerWithDisplayName:[UIDevice currentDevice].name];

    [self.appDelegate.mpcHandler setupSession];

    [self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];

}



في viewDidLoad, سنعين النظير من خلال تمرير اسم الجهاز كاسم العرض. سنبدأ أيضا خاصية جلسة نموذج MPCHandler الذي سنستخدمه لاحقا لانشاء اتصال بين النظراء. من المهم ان نفعل ذلك بعد بدء نموذج MCPeerID, لان الجلسة تتطلب كائن peerID صالح خلال عملية الانشاء. أخيرا, سنستدعي advertiseSelf: و نمرر حالة الانتقال في واجهة تطبيق مشهد الخيارات. تعد حالة الافتراضي للانتقال هي نعم, و التي تعني ان جهازنا يمكن اكتشافه من قبل الأجهزة الأخرى.

الخطوة 2

يعد تقديم متحكم مشهد المتصفح سهلا جدا. و لكن, تحتاج خاصية متصفح نموذج MPCHandler ان يتم بدؤها أولا. نرغب بان نري متحكم مشهد المتصفح عندما ينقر المستخدم  تصفح لازرار اللاعبين الاخرين. دعونا ننشئ نشاط searchForPlayers: تاليا.

      

- (IBAction)searchForPlayers:(id)sender {

    if (self.appDelegate.mpcHandler.session != nil) {

        [[self.appDelegate mpcHandler] setupBrowser];



        [self presentViewController:self.appDelegate.mpcHandler.browser

                           animated:YES

                         completion:nil];

    }

}

يتطلب عرض متحكم مشهد المتصفح خطوتان. أولا, سنستدعي   setupBrowserعلى كائن mpcHandler التطبيق لبدء نموذج MCBrowserViewController و من ثم عرض ذلك النموذج للمستخدم. لاحظ اننا نقوم أولا بالتحقق فيما اذا كان كائن الجلسة لنموذج  MPCHandler لا يساوي الصفر, لان كائن الجلسة يستخدم لبدء نموذج MCBrowserViewController.

اذا قمت بتشغيل التطبيق و البحث عن لاعبين اخرين, يجب ان ترى مشهدا مشابها لذلك الذي في الصورة.

لفحص التطبيق, تحتاج الى جهازين, على سبيل المثال, محفز iOS و جهاز فيزيائي. لانشاء الصورة التي في الأعلى, قمت بتشغيل التطبيق في محفز iOS على جهاز. اذا نقرت على اسم الجهاز, فان الجهاز المختار سيتحرك الى قسم مشار اليه بـالمدعوين كما هو موضح اسفلا.

و في نفس الوقت, قان مشهد التنبيه يعرض على الجهاز الثاني, معطيا اشعارا للاعب الاخر بانك ترغب بانشاء اتصال. يمكن للمدعو ان يقبل او يرفض الدعوة. اذا قبل اللاعب الدعوة, فان المتصفح سينشئ اتصالا بين الجهازين.

عندما يقبل اللاعب الثاني الدعوة, يتم  تفعيل زر الانهاء في متحكم مشهد المتصفح. و لكن, اذا حاولت النقر على ازرار الاكتمال او الانهاء, ستلاحظ اه لن يحث أي شيء. لم ذلك؟ شرح ذلك بسيطا, نحتاج أولا الى انشاء بروتوكول MCBrowserViewControllerDelegate في صنف OptionsViewController. دعونا نفعل ذلك لان. و لكن الوا, قم بتحديث نشاط searchForPlayers: من خلال تعيين متحكم مشهد الخيارات كمفوض عن متحكم مشهد المتصفح.

      

- (IBAction)searchForPlayers:(id)sender {

    if (self.appDelegate.mpcHandler.session != nil) {

        [[self.appDelegate mpcHandler] setupBrowser];

        [[[self.appDelegate mpcHandler] browser] setDelegate:self];



        [self presentViewController:self.appDelegate.mpcHandler.browser

                           animated:YES

                         completion:nil];

    }

}

و لكن هذا ينتج بتحذير المجمع بما ان OptionsViewController لا تتوافق مع بروتوكول MCBrowserViewControllerDelegate بعد. لاصلاح ذلك, افتح OptionsViewController.h, استورد ملفات اطار عمل اتصال Multipeer, و اجعل OptionsViewController تتوافق مع برتوكول MCBrowserViewControllerDelegate.

      

@interface OptionsViewController : UIViewController <MCBrowserViewControllerDelegate>



@property (weak, nonatomic) IBOutlet UITextField *txtPlayerName;

@property (weak, nonatomic) IBOutlet UISwitch *swVisible;

@property (weak, nonatomic) IBOutlet UITextView *tvPlayerList;



- (IBAction)disconnect:(id)sender;

- (IBAction)searchForPlayers:(id)sender;

- (IBAction)toggleVisibility:(id)sender;



@end

نحتاج الى انشاء طريقتين لبرتوكول MCBrowserViewControllerDelegate من اجل تفعيل ازرار الانهاء و الاكتمال. يعد الانشاء متطابقا, أي صرف متحكم مشهد المتصفح. افتح ملف انشاء صنف OptionsViewController و اضف طريقتي التفويض كما هو موضح اسفلا.

- (void)browserViewControllerDidFinish:(MCBrowserViewController *)browserViewController {

    [self.appDelegate.mpcHandler.browser dismissViewControllerAnimated:YES completion:nil];

}



- (void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController {

    [self.appDelegate.mpcHandler.browser dismissViewControllerAnimated:YES completion:nil];

}

اذا شغلت التطبيق مرة أخرى, سترى ان ازرار الانهاء و الاكتمال تعمل كما تتوقع. بالنقر على زر الاكتمال, سيتم انشاء اتصال خلف الكواليس و سيتم صرف متحكم مشهد المتصفح.

8. اكتشاف تغيرات حالة الاتصال

ان الاتصال السابق كان أساسيا في اطار عمل اتصال  Multipeer. و لكن بعد النقر على زر الاكتمال, لم نلحظ اية تغيرات. لم ذلك؟ خلال عملية الاتصال, فان الأجهزة التي ترتبط تنتقل من غير من متصل الى جاري الاتصال, و اذا سار كل شيء على ما يرام, ينتهي بحالة متصل.                                                                       بينما يحدث هذا الانتقال, فانه يتم استدعاء طريقة session:peer:didChangeState: اطار عمل اتصال Multipeer. و بالنتيجة فانه سيتم تعيين اشعار تقليدي في صنف MPCHandler عند تغيير كل حالة.

من مهامنا الاطلاع على الاشعارات و الرد بشكل مناسب عندما يتصل النظير او ينفصل من جهاز المستخدم. في أي وقت تتغير حالة الاتصال, ناهيك عن حالة جاري الاتصال, سنحدث مشهد نص صنف OptionsViewController مع اللاعبين المتصلين بجهاز المستخدم.

أولا, يجب إضافة نموذج متحكم المشهد كمراقب لاشعارات MPCDemo_DidChangeStateNotification. قم بتحديث طريقة متحكم المشهد viewDidLoad كما هو موضح اسفلا.

      

- (void)viewDidLoad {                    

    [super viewDidLoad];

   

    self.appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

   

    [self.appDelegate.mpcHandler setupPeerWithDisplayName:[UIDevice currentDevice].name];

    [self.appDelegate.mpcHandler setupSession];

    [self.appDelegate.mpcHandler advertiseSelf:self.swVisible.isOn];

   

    [[NSNotificationCenter defaultCenter] addObserver:self

                                             selector:@selector(peerChangedStateWithNotification:)

                                                 name:@"MPCDemo_DidChangeStateNotification"

                                               object:nil];

}

تذكر ان الاشعار ذات الاسم MPCDemo_DidChangeStateNotification قد نشر في كل مرة تتغير فيها حالة الاتصال. عندما يحدث ذلك, يتم استدعاء peerChangedStateWithNotification:. لا يعد انشاء peerChangedStateWithNotification: صعبا كما ترى اسفلا. سنقوم باستخراج حالة النظير من اشعار قاموس userInfo, و اذا لم يكن مساويا لـ MCSessionStateConnecting, سنقوم بتحديث مشهد نص المتحكم لعرض حالات كل نظير.
- (void)peerChangedStateWithNotification:(NSNotification *)notification {

    // Get the state of the peer.

    int state = [[[notification userInfo] objectForKey:@"state"] intValue];



    // We care only for the Connected and the Not Connected states.

    // The Connecting state will be simply ignored.

    if (state != MCSessionStateConnecting) {

        // We'll just display all the connected peers (players) to the text view.

        NSString *allPlayers = @"Other players connected with:\n\n";



        for (int i = 0; i < self.appDelegate.mpcHandler.session.connectedPeers.count; i++) {

            NSString *displayName = [[self.appDelegate.mpcHandler.session.connectedPeers objectAtIndex:i] displayName];



            allPlayers = [allPlayers stringByAppendingString:@"\n"];

            allPlayers = [allPlayers stringByAppendingString:displayName];

        }



        [self.tvPlayerList setText:allPlayers];

    }

}

كما يشر الاسم, فان خاصية connectedPeers لكائن الجلسة تعد مصفوفة تحوي على النظائر المتصلة. سنستخدم عقدة for لنكرر المصفوفة و تأهيل مشهد النص بأسماء النظائر المتصلة.

قم بتشغيل نموذجان للتطبيق و أنشئ اتصالا لترى نتيجة العمل. عندما تنقر على زر الاكمال, سيعرض مشهد النص قائمة بنظائر المتصلة بجهاز المستخدم.

الخاتمة

هذا ينهي الجزء الأول من هذا الدرس عن اطار عمل اتصال Multipeer. قمنا بتغطية عدة APIs جديدة و وضعنا الأسس للعبة. في الدرس التالي, سنكمل بناء اللعبة من خلال انشاء منطق اللعبة بالإضافة الى توحيد اطار عمل اتصال Multipeer لارسال البيانات بين اللاعبين في اللعبة .

الكــاتــب

    • مشاركة

ليست هناك تعليقات:

جميع الحقوق محفوظة لــ الشبح للمعلوميات 2019 ©