//前言:仿weixin實現一個即時通信的案例,支持版本7.0以上
"準備工做 建立項目時使用git"
/* 顯示隱藏文件,看到git的文件夾 defaults write com.apple.finder AppleShowAllFiles Yes && killall Finder defaults write com.apple.finder AppleShowAllFiles No && killall Finder 設置git的(全局)用戶與郵箱 git config --global user.name Fung git config --global user.email fung@qq.com */
/** 掌握的知識點 1.配置應用程序的iPad與iPhone的各個設備的AppIcon 2.配置應用程序的iPad與iPhone的各個設備的啓動圖片(LaunchImage) */ 1、設置AppIcon與LaunchImage 1> 設置iPhone-iOS8.0/iOS7.0 iPad-iOS7.0的AppIcon 2> 設置LaunchImage時, a> Xcode6在TARGETS中去除Launch Sreen File裏的值 b> 在Lauch Images Source中添加在assests中的啓動圖片 c> iPhone橫屏與iPad橫屏的啓動圖片不設置 d> iPad1024x768圖片不設置 "由於微信資源包沒有找到對應尺寸的圖片,說明有些啓動圖片能夠不用設置"
"微信iPad運行,若是是橫屏啓動時,是沒有顯示圖片的" 2、實現登陸排版 1> 建立一個Login.storyboard,使用導航控制器包裝登陸控制器 2> 自定義導航控制器,設置導航條背景與狀態欄樣式 3> 添加登陸框容器,使用AutoLayout,固定大小,頂點間距,水平居中 4> 使用Images.xcassets對登陸按鈕的背景圖片進行slicing(切片操做) 5> 設置密碼的文本輸入框樣式 *設置borderStyle = UITextBorderStyleNone;"storyboard設置,提醒爲何不能設置高度"
*設置textFiled的高度42 *設置backgroudImage爲拉伸後的圖片"代碼實現,由於背景圖片放在xcassets時,xcode會崩潰"
*設置密碼文件框左邊的圖片 設置圖片控制的尺寸、圖片內容模式 設置文本框的leftViewMode爲老是顯示(UITextFieldViewModeAlways)"不然不顯示左邊的view"
5> 對控制器view進行拖拽手勢監聽實現上,實現登陸輸入框的上下滑動及反彈 "注:提交git版-微信目錄結構分層+登陸界面排版" 3、註冊界面排版 1> 添加一個註冊框容器,設置頂部,左邊,右邊距離固定,高度固定 2> 添加手機與密碼文本輸入框、註冊按鈕,並設置背影圖片,文字居中顯示 3> 設置按鈕的可用狀態 "注"
4> 判斷當前設置是否爲iphone,若是爲iphone,註冊框容器的左右兩邊間距爲10 //[UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone
5> 設置導航條Item的樣式-白色字體 4、實現其它登陸排版 0>在登陸界面的底部添加 "其它方式登陸>"按鈕 1> 將"註冊界面"的視圖拷貝到新的控制器 2> 去除橫屏,這樣在ipad上能夠旋轉,iphone上不能夠 5、完成主界面排版[MainStoryboard] 1> 微信、通信錄、發現、我[拖四個導航控制器到Tabbar控制器] 2> 設置tabbarBtn的背景及文字,及導航欄的背景 微信 tabbar_mainframe/tabbar_mainframeHL 通信錄 tabbar_contacts/tabbar_contactsHL 發現 tabbar_discover/tabbar_discoverHL 我 tabbar_me/tabbar_meHL "注:選設置普通狀態的圖片與文件,有時間再自定義tabbar"
3> 設置全局的狀態欄樣式 *在info.plist中添加View controller-based status bar appearance=NO *在自定導航控制器是設置[UIApplication shareApplication].statusBarStyle = UIStatusBarStyleLightContent *設置導航欄標題 字體爲粗體18/顏色爲白色, iOS7或者iOS以上使用NSFontAttributeName/NSForegroundColorAttributeName屬性 iOS7如下UITextAttributeFont/UITextAttributeColor
//前言:仿weixin實現一個即時通信的案例,支持版本7.0以上 /* 日誌 git log 恢復指定版本 git reset --hard 版本號 查看指令使用記錄 git reflog */
"未完成"
/* * 前題:備置mysql密碼時參照 "資源/MySql命令行/設置mysql的root密碼" */ 6、使用xmpp框架實現登陸功能 1> 導入框架 a> 導入CocoaLumberjack 日誌框架 "無須依賴" b> 導入CocoaAsyncSocket 底層網絡框架,實現異步socket網絡通信 "依賴:CFNetwork&Security框架" c> 導入KisssXML XML解析框架 依賴libxml2.dylib "還須要配置編譯選項"" "Other Linker Flags = -lxml2"
"Header Search Path = /usr/include/libxml2" e> libidn 直接導入 f> 導入下面四個文件夾 Authentication Categories Core Utilities "依賴libresolv.dylib" g> 添加Extensions h> 將Sample_XMPPFramework.h導入,並將其更名爲XMPPFramework.h "注在xcode6中系統有的Framework能夠不用添加,可是dylib必定要添加"
2> 實現登陸 *> 選分析登陸的步驟 *> 初始化xmppStream核心類,並添加代理爲全局隊列dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) *> 鏈接服務器時,設置主機,端口與登陸帳號[先寫死代碼] *> 在"代理中鏈接成功"時發送密碼[先寫死代碼] *> 在"代理中受權成功"時, 打印 *> 在"代理中受權失敗"時, 打印 *> 在"代理中受權成功"時,通知用戶上線 *> 官方把登陸數據放在沙盒裏 "咱們放在單例對象"
*> 登陸成功用block的方式通知 "其它方式登陸控制器"
*> 在其它方式登陸控制器中,用MBProgessHUB 顯示進度,"在IPad中顯示必定要用toView的顯示方法,由於窗口沒有橫豎屏的概念,只有控制器的view纔有橫豎屏的概念"
*> 隱藏HUB時,引出block是在子線程調用的,不能直接刷新UI *> 接收block的成功登陸時,切換到主界面,"在切換到主界面前,要清除當前模態窗口" 強調本身寫的block引用控制器時,要用__weak 7、使用xmpp框架實現註冊功能 *> 先分析註冊的步驟,與登陸造成一個對比 *> 在AppDelegate中添加用戶註冊的方法 *> 在AppDelegate中添加一個userRegister的屬性 *> "鏈接服務器,鏈接成功後判斷userRegister在代理中作登陸仍是註冊的操做"
*> 在代理中監聽註冊成功仍是失敗,再以block的方式通知"註冊控制器"
*> "註冊控制器" 再以 "代理的方式" 通知 "登陸控制器",並設置label的註冊後的賬號 *> 抽取 "其它登陸控制器" 登陸的代碼 到一個父類,實現登陸 8、細節 0> 用戶註銷[斷開鏈接,回到登陸界面] *> 通知用戶下線、斷開鏈接、顯示登陸界面 1> 用戶登陸過,未註銷,不論是程序在後臺仍是從新啓動,直接來到主界面 2> 用戶登陸過,未註銷,退出臺後時與服務器斷開鏈接,獲取焦點時獲取自動 "在UserInfo單例中添加一個login屬性來記錄登陸狀態"
3> 登陸界面設置上次登陸的賬號 9、自動登陸提示 0> 分析自動登陸監聽中的狀況 /* 1.開始鏈接服務器 2.鏈接失敗 3.登陸成功 4.登陸失敗 */
1> 在歷史控制器中添加通知監聽 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(autoLoginStatus:) name:WXAutoLoginStatusNotification object:nil]; 2> 在APPDelegate中發送通知 3> "自動登陸時延遲兩秒登陸,好演示效果,鏈接的主機用域名比較慢"
4> 轉圈圈指示器是否轉仍是中止,應該放在主線程中 10、socket後臺支持 /* * 注 iOS8不用寫支持也能夠後臺支持 * iOS7 和 iOS8 下都要演示 */
1> "Required background modes"的key,值設置爲voip,來指定類型 2> xmppStream.enableBackgroundingOnSocket = YES 3> xmpp代理接收到消息時,若是不在前臺實現本地通知 [UIApplication sharedApplication] applicationState] != UIApplicationStateActive 設置本地通知的內容(用戶名\n內容前多少個字) 聲音(default) 4> 在iOS8要註冊通知類型registerUserNotificationSettings
//模塊
1、自動鏈接模塊 1> 當用戶網絡不穩定時,若是斷線,自動鏈接 2> 打開自動鏈接模塊在xmppframework.h文件中打開XMPPReconncet 3> 在設置xmppStream時,開始自動鏈接並"激活"
4> "引用官方示例程序的teardown方法" 釋放資源 2、電子名片模塊與頭像模塊 0> "查看電子名片的模型文件,數據結構"
0> 什麼是電子名片模塊/頭像模塊 1> 在xmppframework.h中打開電子名片模塊/頭像模塊的頭文件 2> 在設置xmppStream中,設置電子名片模塊/頭像模塊並激活, "獲取用戶的我的信息,裏面是否是有頭像,也一併獲取"
3> 在teardownxmppStream作相應的資源釋放 4> 在 "Me"中打印 doc,不要使用SimPhodlers 5> 在document看CoreData生成的數據庫,用SQLiteManager查看 6> 在"我" 中添加靜態單元格,顯示圖片與帳號 /* 》XMPPvCardTemp *myvCard = xmppDelegate.vCardModule.myvCardTemp; 》warning 看PPT,查看有些數據是沒有解析的 》取消頭像,顯示日誌,使用XMLJSON格式化工具格式化,解釋電子名片的電話未解析 */ 3、電子名片詳細頁面進行設置 0> 進入詳細頁面進行靜態表格的排版 1> 綁定數據 2> 若是識別不一樣類型cell的點擊,設置每一個cell的tag不一樣 3> 點擊cell,取消選擇中、進行頭相選擇 4> "編輯信息界面,傳一個cell到編輯控制控制器,在調用代理時時調用
[self.profileCell layoutSubviews];//從新刷新cell就會顯示detailTextLabel,由於沒文件,默認是不會建立label"
5> 保存數據 4、獲取好友列表(花名冊) 0> "查看花名冊的模型文件,數據結構"
0> 在xmppframework中打開花名冊模塊 1> setupXmppStream中添加花名冊模塊 2> teardown中釋放花名刪模塊 3> 在聯繫人控制串獲取好友列表 /** * 獲取好友列表時,必定要添加過濾條件?,由於多人登陸,可能用同一個數據庫? * 答案不會,演示數據,每次登陸會清除之前的好友,作得不夠好,實際開發本身建立數據庫來管理 */
4> "使用CoreData的普通方式獲取好友列表"
5> 使用NSFetchedResultsController 獲取好友列表 6> "在cell中演示sectionNum的在線狀態,而後在排序中多添加個sectionNum的升序",從而演示出NSFetchedResultsController的做用 7> 獲取頭像 自定聯繫人Cell if (friend.photo) { cell.imageView.image = friend.photo; }else{ NSData *data = [xmppDelegate.vCardAvatarModule photoDataForJID:friend.jid]; if (data) { cell.imageView.image = [UIImage imageWithData:data]; }else{ cell.imageView.image = [UIImage imageNamed:@"login_defaultAvatar"]; } } 5、添加好友+刪除好友 0> TextField左邊的圖片放在分類裏 0> 是否添加爲手機號友 /**注:寫在分類UITextField裏 NSString *telRegex = @"^1[3578]\\d{9}$"; NSPredicate *prediate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", telRegex]; return [prediate evaluateWithObject:self.text]; */
1> 不能添加本身爲好友 2> 添加的好友已經存在 [xmppDelegate.rosterStorage userExistsWithJID:friendJid xmppStream:xmppDelegate.xmppStream] [xmppDelegate.roster subscribePresenceToUser:friendJid]; 3> 刪除好友 [xmppDelegate.roster removeUser:friend.jid] 6、聊天界面排版 0> 使用VFL排行聊天表格與輸入框 tableViewHConst "H:|-0-[tableView]-0-|" inputViewHConst "H:|-0-[inputView]-0-|" vConst "V:|-0-[tableView]-0-[inputView(50)]-0-|"
1> 鍵盤監聽,判斷iphone仍是ipad 再判斷ipad的是橫屏仍是豎屏 UIInterfaceOrientationIsPortrait(self.interfaceOrientation) #define iSiPhoneDevice ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
"重點"
2> 返回按鈕監聽 *> 設置textfiel的代理 實現textViewDidChange *> 判斷是否有換行符 "\n"
*> 有就去除掉首尾的空白字符和換行字符 [textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; // 7、獲取聊天消息+發送聊天消息 "聊天消息模塊:什麼是聊天消息模塊,xmpp幫咱們作數據存儲 查看0135模塊的數據結構"
0> xmppframework.h中打開消息歸檔模塊與消息數據存儲 1> 在setupXmppStream中消息歸檔模塊與消息數據存儲 2> 在teardown中釋放消息歸檔模塊與消息數據存儲 3> 在通信錄控制器傳遞好友jid到聊天控制器 4> 從數據庫加載聊天數據,一次性加載所有"使用NSFecthResultsController"
"強調:查詢時要帶條件,必定要屬於當前登陸用戶與好友的聊天記錄"
5> 發送消息 XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; [message addBody:msg]; [xmppDelegate.xmppStream sendElement:message]; 6> 聊天數據滾動到底部 7> 講解怎麼判斷本身發送的消息仍是別人發的消息 8、實現輸入框隨文字的伸縮 9、實現圖片的發送級語音發送的原理 #pragma mark 發送圖片
-(void)sendImage:(UIImage *)image{ XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid]; NSData *data = UIImagePNGRepresentation(image); [msg addBody:@"image"]; XMPPElement *attachment = [XMPPElement elementWithName:@"attachment" stringValue:[data base64EncodedStringWithOptions:0]]; [msg addChild:attachment]; [[WXXMPPTools sharedWXXMPPTools].xmppStream sendElement:msg]; } #parm mark cell顯示 XMPPMessage *xmppMsg = msg.message; chatCell.imageView.image = nil; if ([msg.body isEqual:@"image"]) { NSArray *child = xmppMsg.children; for (XMPPElement *node in child) { if([[node name] isEqualToString:@"attachment"]){ NSString *base64 = [node stringValue]; NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64 options:0]; chatCell.imageView.image = [UIImage imageWithData:imageData]; } } } 第二種 [message addAttributeWithName:@"bodyType" stringValue:@"image"]; [xmppMsg attributeStringValueForName:@"bodyType"] 郵箱解析 NSArray *emailEles = [self elementsForName:@"EMAIL"]; if (emailEles.count != 0) { NSXMLElement *email = emailEles[0]; NSXMLElement *userID = [email elementForName:@"USERID"]; return @[[userID stringValue]]; } NSArray *emailEles = [self elementsForName:@"EMAIL"]; if (emailEles.count != 0 && emails.count!=0) { NSXMLElement *email = emailEles[0]; NSXMLElement *userID = [NSXMLElement elementWithName:@"USERID" stringValue:emails[0] ]; [email removeElementForName:@"USERID"]; [email addChild:userID]; }