你好,歡迎來到IOS教程網

 Ios教程網 >> IOS使用技巧 >> IOS技巧綜合 >> swift項目初體驗

swift項目初體驗

編輯:IOS技巧綜合
[摘要]本文是對swift項目初體驗--教你打造一款個性化圖片浏覽器(篇幅過大,慎入)的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。

項目需求:做一個圖片浏覽器,點擊圖片查看大圖,大圖模式下,左右滾動能查看不同的圖片. 項目的主要核心技術:圖片的彈出和消失動畫 一.對代碼進行重構 1.對代碼進行抽取劃分 1.1 為什麼要對代碼進行抽取? swift中,代碼全部寫在一起,閱讀性極差 2.如何對代碼進行抽取? 2.1在oc中,可以把功能模塊抽取一個個方法 2.2swift中,專門提供 extension ,可以對原有的類進行擴展 3.怎麼使用extension 抽取代碼? 3.1 把一些方法寫在extension(擴展)裡面,這樣能減少viewDidLoad裡面的代碼 3.2 extension可以寫多個,這樣就可以把不同的功能模塊 ,寫在不同的擴展裡面 二.項目基本設置 1.修改bundleID 2.部署版本 3.設置項目圖片,啟動圖片 4.對文件夾目錄進行劃分 三.首頁布局 1.讓首頁為UICollectionViewController 2.設置數據源 3.自定義布局 3.1 創建一個源文件,繼承自UICollectionViewFlowLayout 3.2 重寫 prepareLayout 3.3 設置布局的相關屬性 4.如何設置StoryBoard中的UICollectionViewController的布局 4.1 在StoryBoard中選中collectionView 4.2 在屬性裡找到 layout 設置為自定義 custom 4.3 在下面的class裡面 把自定義布局的類名寫進去即可 四.網絡工具類的封裝 1.集成CocoaPods, 並導入AFNetworking框架 1.1 打開終端,進入項目路徑下 cd 路徑 1.2 創建PodFile文件 pod init 1.3 配置PodFile文件 ,寫入要導入的框架 1.4 導入框架 pod install —no-repo-update / 或 pod intall 1.41 pod install 會更新本地庫(本地已有的框架也會更新) 速度相對較慢 1.42pod install —no-repo-update 不會更新本地庫,速度相對來說快點 2.封裝工具類 2.1 將工具類設計成單例對象 防止別人修改 防止多線程訪問,創建多個對象 2.2 swift中單例的設置方式 static let shareInstance : NetworkTools = NetworkTools() 2.3 可以讓工具類,直接繼承自用到框架的一個類 好處:自己就是這個類的子類,擁有這個類的所有方法和屬性,用的時候直接自己就能調用 3.封裝網絡請求方法
 func requestData (type : Int , urlString : String , parameters : [ String : NSObject] , callBack : (result : AnyObject? , error : NSErroe?) -> ()   )
 func reqeustData(type : RequestType, urlString : String, parameters : [String : NSObject], finishedCallback : (result : AnyObject?, error : NSError?) -> ()) {   } 
4.把方法裡面的閉包抽取出來 4.1 為什麼要抽取? 方法裡面閉包很長,代碼很亂,造成閱讀性差 4.2 怎麼抽取? 定義一個成員屬性 為閉包類型 把方法裡面的閉包,用屬性名 替換 五.項目集成工具類 把封裝好的工具類,直接拖到項目文件中 六.請求網絡數據 1.在控制器中調用工具類封裝好的網絡請求方法 2.解析數據 要對獲取到的數據進行類型轉換,應為從網絡加載的數據類型為AnyObject
      guard let resultDict = result as? [String : NSObject] else {
                return
            }
           
            guard let dataArray = resultDict["data"] as? [[String : NSObject]] else {
                return
            }
3.字典轉模型 3.1 創建模型 3.2 通過kvc手動轉模型 , 要重寫override func setValue(value: AnyObject?, forUndefinedKey key: String) {} 3.3 注意: 在閉包中 self. 也不可以省略 七,自定義cell,展示數據 1.創建cell繼承自UICollectionViewCell 2.在cell裡面定義模型屬性 3.監聽屬性改變(相當於oc的重寫set方法) 在屬性監聽器(willSet, didSet) 這裡用didSet方法裡面給模型裡面的屬性賦值 八.加載更多數據 1.什麼時候加載更多的數據? 當最後一個cell出現的時候 2.怎麼監聽最後一個cell是否出現在屏幕上 通過cell(item)的下標值(從0開始)是否等於數組長度 - 1
   // 最後一個cell已經出現
        if indexPath.item == shops.count - 1 {  
           indexPath.item 相當於 tableView 的  indexPath.row 
            loadHomeData(shops.count)
        }
3.怎麼加載更多數據 和加載數據一樣,只不過多傳一個參數offset 九.彈出圖片浏覽器 1.創建圖片浏覽器的控制器對象UIViewController 2.彈出控制器 2.1 監聽cell的點擊 2.2 創建圖片浏覽器控制器對象 2.3 設置圖片浏覽器控制器對象的彈出樣式 photoBrowserVc.modalTransitionStyle = .FlipHorizontal 2.4 把控制器modal出來 十.布局圖片浏覽器 1.布局UICollectionView 1.1 創建UICollectionView 1.2 把UICollectionView添加到控制器的View上 1.3 設置數據源 1.4 自定義布局 2.布局兩個按鈕 2.1 創建兩個按鈕 2.2 設置按鈕的frame 2.3 對UIButton進行extension(擴展) 2.31 為什麼要進行擴展 創建出來的按鈕,要設置圖片,字體,和文字,一個個設置太麻煩,想讓按鈕創建出來就有這些屬性 2.32 怎麼進行擴展? 對UIButton進行extension(擴展) 擴充一個類型方法,在類方法裡面封裝好這些屬性
   class func createBtn(title : String, bgColor : UIColor, fontSize : CGFloat) -> UIButton {
        let btn = UIButton()
       
        btn.backgroundColor = bgColor
        btn.setTitle(title, forState: .Normal)
        btn.titleLabel?.font = UIFont.systemFontOfSize(fontSize)
       
        return btn
    }
2.4 這樣創建還不是很方便,我們可以給UIbutton擴展構造函數,創建的時候直接設置這些屬性 2.41 注意:在extension中擴充構造函數,只能擴充便利構造函數 2.42 什麼是便利構造函數? 1.必須在init前面加上convenience 2.必須在init方法中 調用self.init()
     convenience init(title : String, bgColor : UIColor, fontSize : CGFloat) {
         self.init()
        
         setTitle(title, forState: .Normal)
         backgroundColor = bgColor
         titleLabel?.font = UIFont.systemFontOfSize(fontSize)
     }
3.監聽按鈕的點擊 3.1 xcode7.2 和xcode7.3中監聽方法的寫法不太一樣 Xcode7.2 --> 1> Selector("方法的名稱") 2> "" Xcode7.3 --> #selector(類.方法名稱) 3.2 如果點擊按鈕調用的方法前面加上private 調用會報錯 3.21 為什麼會報錯 找不到方法 3.22 監聽事件實質就是發送一條消息 3.23 發送消息的過程是: 1.將消息包裝成@SEL 2.通過@SEL去類中的方法列表中找對相應的方法(函數) 3.34 在swift中,如果一個函數前面加上private,那麼該函數就不會被添加到消息(映射)列表中 3.35 如果在private前面加上@objc ,就會保留oc的特性, 該方法依然會添加到消息列表中 3.3 解決問題的方法就是 在private前面加上@objc 或者不寫private 十一.傳遞數據 1.傳遞什麼數據? 要在PhotoBrowserVc中查看大圖,首先要拿到圖片數據 2.怎麼傳遞數據?直接傳遞圖片? 直接傳遞圖片url也可以,不過要從模型數組中抽離出來,不太好 最好的做法是:直接把模型數組傳遞給PhotoBrowserVc 十二.自定義PhotoBrowserCell,用於展示數據 1.PhotoBrowserVc是UICollectionViewController,要想展示圖片,需要在cell上添加UIImageView 注意:如果一個構造函數前有required,那麼重寫了其他構造函數時,那麼該構造函數也必須被重寫
     // MARK:- 重寫構造函數
     override init(frame: CGRect) {
         super.init(frame: frame)
        
         setupUI()
     }
     // required : 如果一個構造函數前有required,那麼重寫了其他構造函數時,那麼該構造函數也必須被重寫
     required init?(coder aDecoder: NSCoder) {
         fatalError("init(coder:) has not been implemented")
     }
2.要想展示圖片,需要設置什麼? 2.1 設置UIImageView的image 直接從 SDWebImage緩存中取出原來cell的圖片(小圖) 注意:取出的圖片類型是可選類型,要先進行判斷再使用 2.2 設置UIImageView的frame 2.21 根據取出的圖片的尺寸,計算圖片的frame(設置UIImageView的寬度等於屏幕寬度) 2.22 讓圖片的寬度等於UIImageView的寬度 2.23UIImageView的高度,就等於 圖片高度 *UIImageView的寬度 / 圖片寬度 (讓圖片等寬高比拉伸) 3.加載高清圖片 3.1 為什麼要加載高清圖片? 上面取出的圖片是小圖,不清晰. 查看大圖的時候,要換成高清圖片 3.2 怎麼設置? 用SDWebImage加載大圖,把小圖設置為占位圖片 占位圖片:圖片還沒加載的時候,先用內存中的一張圖片顯示到屏幕上,加載好圖片, 就顯示加載的圖片 4.設置完成後,查看大圖,發現滾動到後面,發現圖片被壓縮了,為什麼? 4.1 因為在MainVc(首頁)展示小圖的時候,給小圖也設置了占位圖片 4.2 在PhotoBrowserVc中查看大圖,滾動到後面的時候,MainVc中的cell還沒顯示,小圖就不會被加載,就把占位圖片賦值給小圖
    // 2.獲取小圖片
             var smallImage = SDWebImageManager.sharedManager().imageCache.imageFromDiskCacheForKey(shop.q_pic_url)
             if smallImage == nil {
                 smallImage = UIImage(named: "empty_picture")
             }
  4.3 這是UIImageView的尺寸就是根據占位圖片的尺寸計算出來的,跟實際圖片的尺寸會有差別,實際顯示的圖片就可能被壓縮 5.怎麼解決圖片壓縮為題? 大圖請求成功時,重新計算UIImageView的尺寸就可以了 十三.把collectionView滾動到正確的位置 1.為什麼要滾動collectionView? PhotoBrowserVc的cell是從第0個cell開始顯示的, 所以每次點擊查看大圖都是從第0張圖片開始顯示 當點擊MainVc(首頁)cell的時候,要顯示對應的大圖,不一定是第0張圖片 2.怎麼滾動? 2.1 在PhotoBrowserVc中定義indexPath屬性, 在MainVc中拿到cell的indexPath,對PhotoBrowserVc的indexPath賦值 2.2 滾動到對應的位置(用下面這個方法)
collectionView.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .CenteredHorizontally, animated: false)
2.3 滾動代碼應該寫到哪裡? 點擊MainVc的cell,就彈出查看大圖控制器(PhotoBrowserVc),就要滾動要對應的位置 所以,代碼可以寫到viewDidLoad裡面 3. ??的使用
 // ?? : 先判斷前面的可選鏈是否有值, 如果有值,解包並且獲取對應類型的值. 如果沒有值直接取後面的值
         return shops?.count ?? 0
十四.設置大圖之間的間距 1.怎麼設置大圖之間的間距?用 minimumLineSpacing? 不可以,雖然能讓大圖之間有間距,但是會把後面的cell往後移 ,後面的cell就不能完全顯示在屏幕上 2.思考:可以collectionView的cell的寬度比屏幕寬度大一點,多出來的寬度就當做間距 不可行,collectionView(scrollView)的分頁效果,會讓用戶看到多出來的那部分 scrollView分頁效果的滾動距離 是由scrollView的寬度來決定的 3.最終解決方案 3.1 只用一句代碼就可以搞定,把控制器的view的寬度增大一點就可以了 view.frame.size.with += 15 3.2 注意collectionView的寬度和 cell的寬度 要等於控制器的view的寬度才可以 4.全局函數的定義 4.1 什麼是全局函數? 就是在工程目錄下的任何地方都能使用的函數 4.2 怎麼定義全局函數? 只要把函數定義到AppDelegate裡面就可以了 十五.保存圖片 1.先要拿到對應的圖片,根據indexPath拿? 點擊查看大圖後可能會被滾動,所以不能根據indexPath拿 2.怎麼拿到正在顯示的圖片 2.1 先拿到正在顯示的cell 2.2 cell裡面保存的就有image 3.怎麼拿到cell 可以通過蘋果自帶的api拿到正在顯示的cell
  // 1.1.拿到正在顯示的Cell
         // visibleCells 返回所有在屏幕中顯示的Cell
         let cell = collectionView.visibleCells()[0] as! PhotoBrowserViewCell
         guard let image = cell.imageView.image else {
             return
         }
4.保存圖片到相冊 蘋果自帶api保存到相冊 UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) 十六.點擊大圖關閉控制器 1.需求:點擊大圖或關閉按鈕,把控制器dismiss掉 2.點擊按鈕關閉 給按鈕設置點擊方法就可以了 addTarget 3.點擊大圖關閉怎麼實現? 點擊大圖相當於點擊了cell,在cell代理方法裡面dismiss即可 十七.自定義轉場(淡入淡出) 1.怎麼自定義轉場動畫? 遵守轉場的代理協議UIViewControllerTransitioningDelegate,實現代理方法 2.實現了代理方法,發現程序還是報錯,為什麼? 代理方法都有一個返回值,返回值要遵守一個協議UIViewControllerContextTransitioning才能作為返回值 3.在UIViewControllerContextTransitioning代理方法裡面設置動畫(具體看代碼) 4.設置消失動畫 4.1 設置完顯示動畫後,消失的時候也自動會有一個動畫效果,為什麼? 因為,大圖view消失的時候,也是主控制器的view顯示的時候 看到的消失動畫,實際上是主控制器的view的顯示動畫 4.2 怎麼判斷是顯示,還是消失? 定義一個屬性記錄即可 在UIViewControllerTransitioningDelegate代理方法中記錄
 // MARK:- 遵守轉場的代理協議,和實現對應的方法
 extension HomeViewController : UIViewControllerTransitioningDelegate {
     // 為彈出控制器做一個動畫
     func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
      //記錄當前為顯示階段
      isPresented = true
         return self
     }
   
     // 為消失控制器做一個動畫
     func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
       //記錄當前為消失階段
       isPresented = false
         return self
     }
 }
  
 extension HomeViewController : UIViewControllerAnimatedTransitioning {
     // 返回動畫執行的時間
     func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
         return 3
     }
    
     // transitionContext : 轉場上下文
     // 作用 : 可以通過上下文獲取到彈出的View和消失的View
     // UITransitionContextFromViewKey : 獲取消失的View
     // UITransitionContextToViewKey : 獲取彈出的View
     func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
         if isPresented {
             // 獲取彈出的View
             let presentedView = transitionContext.viewForKey(UITransitionContextToViewKey)!
            //需要把view添加到父控件上,才能有動畫效果  
           //父控件就是widow的containerView,    通過transitionContext.containerView()拿到
             transitionContext.containerView()?.addSubview(presentedView)
            
             // 修改View alpha值
             presentedView.alpha = 0.0
            
             // 執行動畫
             UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
                 presentedView.alpha = 1.0
             }) { (isFinished : Bool) in
                //告訴控制器,轉場動畫完成
                 transitionContext.completeTransition(isFinished)
             }
         } else {
             // 1.獲取消失的View
             let dismissedView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
            
             // 2.執行動畫
             UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
                 dismissedView.alpha = 0.0
                 }, completion: { (isFinished : Bool) in
                     //移除view,顯示主控制器的view
                     dismissedView.removeFromSuperview()
                     transitionContext.completeTransition(isFinished)
             })
         }
     }
 }

5.性能優化,代碼抽取 5.1 把轉場動畫的代理設置為主控制器,代理要全部寫在主控制器中,代碼臃腫,閱讀性差 5.2 怎麼優化? 把代理設置為其它對象,讓其它對象實現代理方法即可 5.3 具體實現步驟 5.31 創建一個對象(任何對象) 5.32 設置這個對象為轉場動畫的代理 5.33 在對象中實現代理方法即可 5.34 注意:代理屬性為弱引用,要讓一個強引用指向它 十八.最終動畫效果 1.想要做動畫,必須要拿到三個元素 1.1 圖片的起始位置(相對於控制器view的坐標系) 1.2 圖片的終點位置(相對於控制器view的坐標系) 1.3 轉場圖片的父控件UIImageView 2.彈出動畫 2.1 獲取動畫的三個元素 圖片的起始位置,終點位置和UIImageView 只有主控制器(mainVc)最清楚,可以定義代理 2.2 mainVc成為動畫代理對象的代理,提供三個元素 3.消失動畫 3.1 消失的時候,圖片的終點位置有可能發生變化,需要重新計算 3.2 怎麼計算消失的圖片的終點位置? 只要拿到對應cell的indexPath就可以計算位置 3.3 怎麼拿到indexPath? cell的indexPath只有PhotoBrowserVc最清楚,可以設置代理,讓PhotoBrowserVc提供indexPath indexPath就是最後顯示在屏幕上cell的indexPath // 1.獲取在屏幕中顯示的cell let cell = collectionView.visibleCells()[0] // 2.獲取cell對應的indexPath let indexPath = collectionView.indexPathForCell(cell)! 3.4 根據indexPath計算終點位置,完成動畫 4.性能優化 4.1 當查看大圖滾動的時候,indexPath會大於屏幕上顯示的indexPath最大值,這個時候,就獲取不到終點位置,就會沒有消失動畫(直接消失) 不滾動的時候消失的時候,圖片是不清晰的圖片 4.2 為什麼直接消失? 不滾動的時候,設置的圖片是小圖,所以不清晰 返回的時候獲取不到cell,獲取不到cell就直接返回空的ImageView ImageView還沒有設置圖片就直接返回了 4.3 怎麼解決? 在PhotoBrowserVc中可以拿到高清圖片 設置代理拿到Image,消失動畫的時候,直接顯示高清圖片 4.4 消失的時候,發現還是直接消失為什麼? 因為滾動到後面,mainVc的cell不在屏幕上,就獲取不到cell, 所以消失時獲取的startRect = CGRectZero 從0消失到0 所以沒有動畫 4.5 點擊mainVc最後一個cell,看看大圖,往後滾,返回的時候,發現消失動畫最終消失到左上角為什麼? 因為,後面的cell還沒出現,就獲取不到最終位置,系統默認在左上角 4.6 怎麼解決? 方法一: 當超出的時候,給定一個終點位置,讓它在指定的位置消失 效果可以,但是滿足不了需求 4.7 最終方案(參考微信的解決方案) 當獲取不到終點位置的時候,讓圖片消失的動畫 設置為漸變消失動畫 十九.版本適配bug的解決 1.當項目運行到6s Plus上的時候,collectionView只能顯示兩列(需求是三列) 產生bug的原因是蘋果對臨界值得處理不太好 具體來說就是,屏幕寬度三等分,得到的數值是無限循環小數,蘋果會根據數據類型對小數向前進一位 這時,屏幕的寬度就不足以放三個cell,就會把第三個cell擠到下一行顯示,就變成了兩列 2.bug解決 讓得到的cell的寬度減去一個臨界值小數即可(0.000001) 這個數值隨便寫
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved