30天學習編寫30個Swift小程序

更新:全部代碼已經更新到Swift4.1,請移步 github下載

=======================================================html

iOS開發已經作了快4年了,據說Swift也已經有兩年多,可是一直都只是把學習停留在表面。無心中據說了有一個叫Sam Lu在Twitter上發起了一個100天作40個Swift小程序的活動,再加上國內看到了Allen_朝輝寫的Swift學習的文章,內心暗自下了一個決定:30天寫30個Swift小程序,但願能推進本身學習Swift的計劃。這30個小程序難度不一樣,有的一個晚上就能寫完,有的要佔用週末大部分時間來細研究。大部分不會的東西Google都能找到,就算Swift版本沒有找到Objective-C版本而後用Swift重寫就好,好在他們對應關係比較明確。git

用例方面,既參考了Sam Lu的40個小項目,也參考了Allen_朝輝的項目,還有的是我本身仿寫的知名App。github

其實我並非惟一在國內發起這個30天30個Swift小程序而且將其開源的做者,可是我多是惟一一個從頭至尾用XCode 8 + Swift3環境編寫的做者。並且,爲了讓代碼更加可讀,全部代碼徹底手寫,而非用Storyboard(除了只能用Storyboard的,例如apple watch app)。實際上多人協做的項目中咱們儘量少用Storyboard,由於很容易出現衝突問題。何況從學習的角度,storyboard很難說清楚操做步驟是什麼。在這上面我其實花了很多時間,可是我認爲很值得。小程序

但願能有更多對Swift感興趣的開發者加入這項#30天30個Swift小程序 的活動裏面來。如下爲Github連接: github.com/nimomeng/30…swift

Project 30 - Google Now App

GoogleNow.gif

我學到了

  • 此次Project演示了Present/Dismissd如何作Transition動畫,這和作Push/Pop的轉場動畫的基本原理都是同樣的
  • 此次的動畫參考了BubbleTransition的動畫效果,在它之上加了修改,支持傳入自定義的UI屬性,方便作組合型動畫(例如本例中按鈕不只放大並且上下移動)
  • 動畫變化的原理是將相應的ViewController進行Scale變換,再經過一個Bubble的蒙版看起來像是氣泡效果
  • 其它的細節知識以下:
    • 畫圓形按鈕的方法,必需要cornerRadius屬性爲邊長的1/2,具體代碼以下:
triggerButton.layer.cornerRadius = triggerButton.frame.width / 2
        triggerButton.layer.masksToBounds = true
複製代碼

Project 29 - Beauty Contest

BeautyContest.gif

我學到了

  • 這個項目是基於Yalantis的Koloda來製做的。 Koloda是一個很是好用的UIImage選擇器
  • Swift中的懶加載的使用方法:
    • 兩種方式:
lazy var firstWay = "first"
複製代碼

以及數組

lazy var secondWay: String = {return "Second"}()
複製代碼

注意:第二種方式要注意定義好字段類型,以便於編譯時的類型檢查;以及不要忘記最後的小括號緩存

  • 爲何要用Lazy:由於這裏面須要先知道KolodaView的尺寸,才能定Overlay的尺寸。所以這裏有一個依賴關係,所以用懶加載最合適。
  • Swift中的unowned和weak的區別:
    • unowned更像OC裏的unsafe_unretained; weak仍是那個weak。
    • 若是肯定使用時必定不會被釋放,能夠用unowned;不然最好用weak

Project 28 - SnapChat Like App

Snap Chat Like App.gif

我學到了

  • UIScrollView的基本使用和細節小點,例如禁止彈跳的bounces屬性,整頁切換的isPagingEnabled屬性,起始位置contentOffset屬性等bash

  • 加載子Viewcontroller的addChildViewController方法session

  • "xxx class has no initializers"問題:app

    You have to use implicitly unwrapped optionals so that Swift can cope with 
      circular dependencies (parent <-> child of the UI components in this case) 
      during the initialization phase.
    
      @IBOutlet var imgBook: UIImageView!
      @IBOutlet var titleBook: UILabel!
      @IBOutlet var pageBook: UILabel!
    複製代碼
  • 權限問題,具體錯誤描述爲: "This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data." 解決方法:iOS10以後的權限問題,在info.plist裏添加相應的權限以及描述便可。 本例中權限爲: NSCameraUsageDescription PhotoMe needs the camera to take photos. NSMicrophoneUsageDescription PhotoMe needs the microphone to record audio with Live Photos. NSPhotoLibraryUsageDescription PhotoMe will save photos in the Photo Library.

  • AVCaptureSession 的使用方法:

    • AVCaptureSession是AVFoundation的核心類,用於捕捉視頻和音頻,協調視頻和音頻的輸入和輸出流.
    • 建立AVCaptureSession實例,並設置其sessionPreset值,也就是設置畫面的質量。
    • 給Session添加Input。通常是Video或者Audio數據,也能夠二者都添加,即AVCaptureSession的輸入源AVCaptureDeviceInput。具體步驟是先獲取對應的device實例(此時決定是用Video仍是Audio),再由實例獲取其Input Source。最後將input source add到session中。
    • 給Session添加Output,即AVCaptureSession的輸出源。通常輸出源分紅:音視頻源,圖片源,文件源等。這裏以靜態圖片的輸出源爲例,指的是AVCapturePhotoOutput。最後將其也add到session中。
    • 設置預覽圖層,即AVCaptureVideoPreviewLayer。在input,output等重要信息都添加到session之後,能夠用session建立AVCaptureVideoPreviewLayer,這是攝像頭的視頻預覽層。這裏千萬別忘了將Layer添加到View中。
    • 啓動Session,即captureSesssion.startRunning()
  • Photo的捕獲方法

    • AVCaptureSession設置成功,並啓動
    • 建立AVCapturePhotoSettings對象,並配置相應的屬性,例如是否打開flash,是否開啓防抖模式等等
    • 執行輸出源的capture方法,並制定具體的AVCapturePhotoSettings對象以及delegate對象
    • 在capture的delegate方法:
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?)
複製代碼

中執行獲取圖像的具體邏輯。本例中是先將buffer轉換爲data,再轉換爲UIImage,最終write到相冊文件夾中。

Project 27: Carousel Effect (跑馬燈效果)

Carousel Effect.gif

我學到了

  • UICollectionView的使用

    • 與UItableView的不一樣在於,每個對應的Cell(不管是content的cell仍是header,footer的),都須要預先執行register方法
    • Cell間距太窄的問題,能夠經過minimumLineSpacingForSection的DataSource代理方法來解決掉
    • 若是選擇的layout爲UICollectionViewFlowLayout,能夠經過修改scrollDirection屬性來修改滾動方向
  • 自定義Layout要在對應的子類裏實現以下方法

    prepare()
      shouldInvalidateLayout(forBoundsChange newBounds: CGRect)
      targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) 
      layoutAttributesForElements(in rect: CGRect)
    複製代碼

其中:

  • prepare能夠定義初始化操做

  • shouldInvalidateLayout,判斷是否須要更新每一個Cell的bounds。若是咱們的layout是那種每一個cell須要動態變化的layout,則設置爲true;不然爲了性能考慮,請設置爲false。默認爲flase。

  • targetContentOffset,若是咱們須要圖片在滾動的過程當中在特定位置能夠停下來(相似iphone上專輯圖片的選擇),請在此函數中國年給出停下來的具體規則

  • layoutAttributesForElements 返回全部元素此時的全部佈局。咱們會在這裏定義在滾動過程當中全部其餘元素的attribute佈局相關屬性。例如本例中,離屏幕中間越近,圖片被縮放的越大;離屏幕越小,圖片被縮放的越小。

  • Reference:

  • Visual Effect View的使用

    • 儘可能在須要模糊化的圖層以後添加進去,會自動虛化所覆蓋的圖層

      let blurEffect: UIBlurEffect = UIBlurEffect(style: .light)
      let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
      blurView.frame = self.view.bounds
      bgView.addSubview(blurView)
      複製代碼
  • UISegmentedControl 的使用(略)

  • 其它:#selector()中的func若是帶有參數,請將具體參數也一塊兒寫進去,例如: #selector(action_segmentValueChanged(sender:)這個規則和OC不太同樣,要注意。

Project 26 - Twitter-like Splash

TwitterLikeSplash.gif

我學到了

  • 這個效果嘗試了用簡單的UIView的animation方法會比較吃力,所以轉而使用CAAnimation來作。
  • 咱們須要的效果,設置keyPath爲"bounds"。吐槽一下,蘋果爲何不作一個枚舉。。。完整的keyPath列表以下所示:
    KeyPath 對照表
  • 因爲logo的動畫定製化要求比較高,因此關於這個變化的動畫,選擇CAAnimation裏的CAKeyFrameAnimation來作。主要關注keyTimes,values屬性,也就是keyFrame的屬性。timingFunctions屬性是keyframe的count - 1, 也就是frame1到frame2,frame2到frame3的動畫過渡函數。這個很少說了,以前的Project有提到過。
  • 在logo變大的過程當中,logo中間的alpha值也應該有白色變爲透明,所以應該先添加一個maskView,藏在最上層,logo層之下,做爲白色的底。動畫trigger的時間和duration與logo的動畫保持步調一致,而且記得在動畫complete的時候被移除掉。這裏使用了CABasicAnimation的animationDidStop代理來完成。
  • logo的透明度變化既可使用簡單的UIView的animation方法來作,也能夠採用layer級別的CABasicAnimation來完成。由於對前者比較熟悉了,因此我在這裏使用後者,注意keyPath是opacity。代碼比較簡單,這裏不贅述。
  • 總體效果仍是很炫的:)
  • CAKeyFrameAnimation參考此篇文檔

Project 25 Custom Transition

CustomTransition.gif

我學到了

  • NavigationController的動畫是能夠自定義的,去實現UINavigationControllerDelegate裏的方法就好
  • 若是切換動畫只須要關注以前的VC和以後的VC,不須要關注中間過程,直接實現如下方法便可:
navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
複製代碼
  • 上述方法的返回值UIViewControllerAnimatedTransitioning須要自定義動畫,須要實現UIViewControllerAnimatedTransitioning代理,實現具體的兩個方法:
    • 轉場動畫時間,直接返回一個時間便可
transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
複製代碼
  • 轉場動畫過程,以下所示,這個比較複雜:
animateTransition(using transitionContext: UIViewControllerContextTransitioning) 
複製代碼
  • 第一步,得到轉場動畫的fromVC,toVC,container:
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! XXXController
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! YYYController
let container = transitionContext.containerView
複製代碼
  • 以後是動畫前的準備工做,例如image賦值,例如座標的計算:
let snapshotView = fromVC.selectedCell.imageView.snapshotView(afterScreenUpdates: false)
snapshotView?.frame = container.convert(fromVC.selectedCell.imageView.frame, from: fromVC.selectedCell)
....
複製代碼
  • 最後固然是Animation動畫的執行邏輯了,能夠經過UIView的animate方法去實現。具體參數和方法能夠參考以前的Project來進靈活組合。
    • 進入的動畫最後必定不能忘記加上 transitionContext.completeTransition(true) ,說明了讓navigationController來接管控制權利(在completion的block中)
    • 退出的動畫記得帶上 transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 說明動畫執行完成
  • 若是須要關注動畫的執行過程,則在上述的基礎之上還應該實現下述方法:
navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
複製代碼

其中,UIViewControllerInteractiveTransitioning是動畫過渡對象

  • 獲取iOS中手從左往右沿屏幕滑動的事件,是經過UIScreenEdgePanGestureRecognizer方法並設置其edges爲left實現的:
let edgePanGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(edgePanGestrueAction(_:)))
edgePanGesture.edges = UIRectEdge.left
複製代碼
  • 這篇教程針對的是Push與Pop的自定義動畫的製做
  • 參考文檔1文檔2,並在他們的基礎之上作了改動
  • 這個例子我很喜歡,圖片是羅斯科。

Project 24 - Vertical Menu Transition

Vertical Menu Transition.gif

我學到了

  • 本文和Google Now App項目思路一致,都是針對Present/Dismiss的操做進行自定義Transition
  • 因爲動畫須要局部截圖,所以建議將Present和Dismiss的Transition寫到一塊兒,經過一個變量來進行不一樣動畫的切換和控制。變量能夠經過animationController(forDismissed dismissed: UIViewController)animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController)來進行設置。
  • target-action方式,通常會將target設置爲self。若是設置爲對應的delegate,則action字段應該填寫#selector(CustomTransitionDelegate.functionName)
  • 在Present/Dismiss的自定義轉場動畫中,記得在complete回調中加入動畫結束語句塊:
transitionContext.completeTransition(true)
    fromViewController?.endAppearanceTransition()
    toViewController?.endAppearanceTransition()
複製代碼

Project 23 - Side Navigation App

SideNavigation.gif

####我學到了

  • Swift-OC混編方法
    • 新建一個頭文件,例如名爲Bridge.h
    • 單擊Project文件,選擇Build Setting,找到Objective-C Bridge Header,輸入Bridge.h的路徑
    • 以後全部須要在swift文件中引用的OC文件的頭文件放到Bridge.h中進行import

Swift-OC

  • 側滑效果借鑑了SWRevealViewController,使用步驟以下(原項目只提到了OC中的調用方法)
    • 項目中至少有如下幾類viewController:第一頁展現的VC,好比FrontViewController;tabeView所在的MenuViewController
    • 在AppDelegate中根據規則建立自定義Window,具體步驟爲:
      • 創建UIWindow
      • 新建兩個UINavigationController,分別以FrontViewController和MenuViewController爲rootViewController
      • 實例化SWRevealViewController,並設置rearViewController的值和frontViewController的值。其中,rearViewController是tableView所在的UINavigationController,frontViewController是FrontViewController所在的UINavigationController
      • 將實例化的SWRevealViewController設置爲Window的rootViewController
window = UIWindow(frame: UIScreen.main.bounds)
 let rearNavigationController = UINavigationController(rootViewController: MenuViewController())
let frontNavigationController = UINavigationController(rootViewController: FrontViewController())
let revealController = SWRevealViewController(rearViewController: rearNavigationController, frontViewController: frontNavigationController)
revealController?.delegate = self 
window?.rootViewController = revealController
window?.makeKeyAndVisible()
複製代碼
  • 須要在每個ViewController中加入左滑激活Menu的邏輯,一句話:
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
複製代碼
  • 效果須要,最好隱藏掉status bar以及navigationBar:
self.navigationController?.isNavigationBarHidden = true
複製代碼
override var prefersStatusBarHidden: Bool {  return true  }
複製代碼

Project 22 - Basic Animations

Basic Animations.gif

我學到了

  • 本次涉及到最基本的UIAnimation,不少複雜的Animation實際上是各類簡單的Animation的疊加,因此不能輕視
  • Position的Animation既能夠經過直接修改frame的origin屬性,也能夠直接經過UIView的transform來進行修改
  • Opacity直接改Alpha值就能夠了
  • Scale是修改了UIView的transform,傳入要縮放的相對比例並建立對應的CGAffineTransform對象。例如:
heartView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
複製代碼
  • Color是直接修改backgroundColor就能夠了
  • Rotation是經過修改UIView的transform,傳入要旋轉的值並建立對應的CGAffineTransform對象。其中,值爲0 - 2*Pi之間,表示0到360°之間。注意,正值爲逆時針轉動。例如:
self.rollingBallView.transform = CGAffineTransform(6.28)
複製代碼

Project 21 CoreData App

CoreDataAppDemo.gif

我學到了

  • 必定要勾選 UseCoreData,這樣在Appdelegate裏會自動生成部分代碼
    Paste_Image.png
  • 不必定非要經過Editor生成SubClass
  • 本例中Entity如圖所示:

Paste_Image.png

  • 在須要調用CoreData的類中,import CoreData
  • 本例比較簡單,只進行了getResult和Add的操做,思路分別爲:
    • getResult:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: EntityName)
        do {
            let searchResults = try getContext().fetch(fetchRequest)
            dataSource = searchResults as! [TodoList]
        } catch  {
           // todo error handler
        }
複製代碼

注意,或取出來的searchResult能夠直接實例化爲TodoList(TodoList是個人Entity名字),這樣後續就能夠直接使用TodoList的content方法了。

  • saveContent:
let context = getContext()
        // 定義一個entity,這個entity必定要在xcdatamodeld中作好定義
        let entity = NSEntityDescription.entity(forEntityName: EntityName, in: context)
        let todoList = NSManagedObject(entity: entity!, insertInto: context)
        todoList.setValue(content, forKey: "content"
        do {
            try context.save()
        }catch{}
複製代碼

對應getConent方法的代碼兩行:

let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
複製代碼

如此操做後使用的時候直接經過獲取TodoList對象,而後調用其content方法便可完成。 cell.textLabel?.text = (dataSource[indexPath.row]).content

  • UIAlertController添加輸入款的方法:
alertController.addTextField { (textField) in
textField.placeholder = "Please input the todo Item"}
複製代碼
  • 該方法在XCode8.3 + Swift3.2測試經過,CoreData在iOS10的變化很大,以前的版本可能和上述操做方法有出入
  • 參考文章

Project 20 - Apple Watch OS App - Guess Game

WatchApp_Guess.gif

我學到了

  • Watch程序,須要在create project的先選擇Watch OS的Section,以後選擇以下:

    watchOS.png

  • watch中的UI只能夠經過Storyboard來進佈局,佈局文件在WatchKit App中的Interface.storyboard中

  • 例子中涉及到了watch和主app的交互,這裏使用的是WCSession方法,使用步驟以下:

    • 肯定app所在設備是否支持WCSession
    • 生成一個WCSession對象,並設置其delegate
    • 激活此WCSession對象 至此部分,代碼爲:
let wcsession = WCSession.default()
        if WCSession.isSupported() {
            wcsession.delegate = self
            wcsession.activate()
        }
複製代碼
  • 發送通訊(watch與主app之間)經過WCSession對象的updateApplicationContext方法來進行,例如 try wcsession.updateApplicationContext(["numberToBeGuessed": number])
  • 接收方經過代理方法來接收並解析發送的消息
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) 
複製代碼

Project 19 - TodayWidget

TodayWidget.gif

我學到了

  • 建立Today Widget: File > New > Target…,而後選擇 iOS 中的 Application Extension 的 Today Extension
  • 爲了方便Widget與App數據共享,須要切換成App Group模式。步驟爲打開主target,選擇capability,找到App Group,打開:

AppGroup

  • 在主Target下爲這個app group添加一個名稱,而後去Extension的target下去採用相同操做,並勾選這個group
  • 咱們能夠採用UserDefault做爲主app與widget之間的共享存儲。可是此處不能使用standardUserDefaults,只能經過suiteName的方式來進行共享,且名字是以前在app group中添加的名稱,代碼以下:
let userDefault = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")
複製代碼
  • 在Widget的ViewController裏寫入相應的讀取邏輯代碼:
let userDefaults = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")
var leftTimeWhenQuit = userDefaults?.double(forKey: "lefttime")
複製代碼

爲了想讓widget裏的數據也進行同步更新,能夠在extension的代碼裏也加入一個timer來進行同步操做。這樣widge和主程序的widge便可同步

  • 若是想了解更多關於Widget的使用,請參考文檔

Project 18 - Spotlight Search

SpotlightSearch.gif

我學到了

  • Spotlight Search的使用:
    • 引入CoreSpotlight庫import CoreSpotlight
    • 這裏用CSSearchableItem來進行須要被索引的對象的添加。先建立一個CSSearchableItemAttributeSet,也就是Item的屬性類,對具體屬性進行添加,包括title,contentDescription,以及thumbData的設置。
    • 須要單獨說明的是,CSSearchableItemAttributeSet對象的thumbData能夠經過獲取UIImage對象後對其UIImageJPEGRepresentation方法來獲取或者UIImagePNGRepresentation方法來獲取(取決於image對應的文件是什麼類型)
    • 建立CSSearchableItem對象,並進一步經過indexSearchableItems方法將建立的CSSearchableItem添加到索引中:
let tmpItems = [searchItem]
CSSearchableIndex.default().indexSearchableItems(tmpItems) { (error) in
}
複製代碼
  • 若是調試過程當中,發現模擬器上從新了以前的spotlight緩存沒法清除的狀況,請更換新的模擬器,或者重置模擬器。或者乾脆切換成真機進行調試,真機這種狀況少一些:)

Project 17 - 3D Touch Quick Action

3DTouchQuickAction.gif

  • 3D Touch的具體功能分紅兩種:第一種是在SpringBoard里長按圖標進行直接功能跳轉,第二種是在APP內部對特定的視圖元素長按進行Peek & Pop
  • 在作任何3D Touch相關功能的引入以前,務必確保用戶機型支持3D Touch。 self.window?.traitCollection.forceTouchCapability == .available
  • 針對第一種功能,創建UIApplicationShortcutItem類型的Item,而後設置application的shortcutItems屬性便可。要注意,在設置icon時,只能夠設置系統內置的集中icon,不支持自定義圖標
  • 針對第二種功能,須要在想加入支持3D Touch的VC中註冊並添加相應事件
  • 添加UIViewControllerPreviewingDelegate
  • 在此VC種註冊3D Touch支持。self.registerForPreviewing(with: self, sourceView: self.view)
  • 實現兩種delegate:
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) 
複製代碼
  • 若是想實現例子中的額外Action button,須要override對應的previewActionItems屬性,並返回你須要的UIPreviewAction的Array

Project 16 - LoginAnimation

LoginAnimation.gif

我學到了

  • 開始覺得很簡單,普通UIView的Animation方法便可完成。後來發現彈跳效果並非我使用的常規方法能夠完成的
  • 彈跳動畫須要使用usingSpringWithDamping來完成,其中的屬性要注意:
    • usingSpringWithDamping:值越小動畫越誇張,借用網上圖來講明其區別:

      usingSpringWithDamping

    • initialSpringVelocity:值越大則起始速度越大,再借用網上圖片來講明其區別:

      initialSpringVelocity

    • options的各個動畫曲線有何區別:能夠看圖來進行區分:

options

  • UIView.animation的usingSpringWithDamping與不帶usingSpringWithDamping的參數動畫有什麼區別呢?能夠看下圖:

Animation Comparation

  • 帶Spring屬性的動畫太有意思了!:)
  • 此部分參考文檔1,文檔2

Project 15 - Tumblr Menu

Tumblr Menu.gif

我學到了

  • 這個例子本質上是對動畫+BlurEffect
  • 三排的動畫有一個前後順序,這個能夠經過animation的delay參數進行調節
  • button的上圖下文效果須要設置,這裏自定義了一個CustomButton,對樣式進行了封裝。參考了這篇文章

Project 14 - Video Splash

VideoSplash.gif

我學到了

  • 建立一個AVPlayerViewController,並將其view放到背景中
  • 以後結合AVPlayerViewController進行視頻播放,並自動循環
  • 視頻播放部分借鑑了此篇文章中的第十個用例,聽說也是參考了一個叫VideoSplashViewController的庫

Project 13: Animation In TableViewCell

AnimationInTableViewCell.gif

我學到了

  • 開始的思路是在willDisplay的delegate裏進行動畫操做,效果良好,可是發如今滾動cell時發生cell錯亂的現象,緣由是在滾動時cell重繪致使從新調用willDisplay進而座標錯誤。粗看了下,解決起來有點兒麻煩,因而換思路。以此這種「進場動畫」不該該在渲染過程當中的delegate中執行。
  • 將動畫放到ViewWillAppear裏來作。能夠經過tableView的visibleCells獲取將要顯示的全部cell的Array,逐一遍從來進行動畫操做。
  • 改變Cell的動畫,採用上一章所說的usingSpringWithDamping的動畫,usingSpringWithDamping設置爲0.8,initialSpringVelocity設置爲0.(否則動畫會彈跳過大,形成順次露出白色間隙,很不美觀)
  • 改變Cell的具體方式,既能夠直接操做cell.frame.origin.y,也能夠經過cell.transform = CGAffineTransform(translationX: 0, y: tableHeight),效果是同樣的。不過若是要用到縮放或者旋轉的動畫,恐怕只能使用後者了。
  • 動畫確實是頗有意思的:)

Project 12 - Emoji Slot Machine

Emoji Slot Machine.gif

我學到了

  • 乍一看沒思路,原本打算用三個collectionView來作,可是發現有點兒複雜
  • 後來轉變思路,用UIPickerView來作,component設置爲3便可
  • 隨機數用arc4random()來算出來,以後使用UIPickerView的selectRow方法進行設置值便可達到老虎機的效果
  • 爲了仿真,不能讓pickerView轉到第一個或者最後一個,否則就會碰到邊界了,所以在算隨機Row時,使用Int(arc4random())%(emojiArray.count - 2) + 1的方法來實現
  • 三個同時一致的狀況實在太少了,所以爲了方便模擬,我加了個雙擊操做,雙擊強制出666。。。
  • 這個case還挺有意思的,哈哈

Project 11 - Gradient in TableView

GradientInTableView.gif

我學到了

  • 這個比較簡單,注意將CAGradientLayer應用在UITableViewCell上便可
  • 建議將CAGradientLayer做爲cell的backgroundView,而不是直接在cell.layer上進行添加
  • 美觀起見,隱藏掉Cell的Select效果以及separatorStyle: table.separatorStyle = .none cell.selectionStyle = .none

Project 10 - Stretchy Header

Stretchy Header.gif

我學到了

  • 經過監聽ScrollView(及其子類)的scrollViewDidScroll代理能夠知道scrollView被拉動的位移(offset)
  • 經過位移以及限定的縮放值能夠得出圖片須要放大的倍率
  • 經過設置ImageView的transform來完成修改便可,核心代碼爲
bannerImgView.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
複製代碼

Project 9 - Swipeable Cell

Swipeable Cell.gif

我學到了

簡單起見,我用Project 13的代碼基礎上進行修改,換了個清爽的綠色:)

  • 實現editActionsForRowAt這個delegate方法,返回值是Array,新建幾個你須要的功能返回便可
  • 每個Action直接經過UITableViewRowAction的init方法新建便可。在新建方法裏有block,直接將點擊邏輯寫進去就好了。
  • 這種交互適用於Accessory比較簡單的狀況,例如對交互按鈕大小和內容無要求的狀況;若是有特殊要求,須要自定義UITableViewCell,手動控制Cell與捕捉UIPanGesture來進行實現。注意,這種方式要排除上下滑動Cell的狀況,不要錯誤觸發。

Project 8 - Color Gradient

ColorGradient.gif

我學到了

  • 顏色漸變效果採用的是類CAGradientLayer
  • 色彩空間的概念能夠藉助於Color數組來實現,注意,成員變量是CGColor類型,而後經過設置CAGradientLayer的colors屬性來實現
  • 上下滑動時改變顏色是經過加PanGestureRecognizer來實現。具體效果參考了應用Solar
    Solar

Project 7 - Simple Photo Browser

SimplePhotoBrowser.gif

我學到了

  • 縮放圖片的方式:將imageView添加到ScrollView上
  • 設置好scrollView的max/minZoomScale
  • 設置好delegate對象,至少實現viewForZooming的代理方法

Project 6 - Video Player

Video Player.gif

我學到了

  • AVPlayer:視頻播放器實體
  • AVPlayerViewController:簡單封裝了的視頻播放器,有簡單的控制功能
  • AVPlayerLayer:視頻的Layer層,全部功能須要寫控件進行控制,適合對播放器進行深度開發
  • 後臺播放的plist設置方式
  • do...catch...語法的使用
  • background modes的設置。
  • 如何作到app在後臺長期運行:參考簡書的文章
  • 如何顯示鎖屏信息,以及如何響應鎖屏設置(實現remoteControlReceived的代理方法)

Project 5 - Pull To Refresh

PullToRefresh.gif

我學到了

  • 下拉刷新組件: UIRefreshControl 設置好提示文字attributedTitle,添加好target事件(UIControlEvents.valueChanged事件)後,添加到tableView中,便可

Project 4 - Limited Input Text Field

Limit Input Text Field.gif

我學到了

  • 經過新建UIBarButtonItem來建立navigationBarItem的左右Item
  • 經過TextView的textViewDidChange事件捕捉當前輸入內容,從而進行限制輸入字數
  • 經過監聽NSNotification.Name.UIKeyboardWillChangeFrame事件來監視Keyboard的彈出和收起。在對應回調中,經過note.userInfo?[UIKeyboardFrameEndUserInfoKey]來拿到鍵盤的endFrame,從而拿到鍵盤的高度,對計數器進行frame操做
  • 同理,經過note.userInfo?[UIKeyboardAnimationDurationUserInfoKey]拿到鍵盤的動畫duration,進而能夠經過UIView的animation動畫作到同步變化計數器的frame

Project 3 - Find My Position

Find My Position.gif

我學到了

  • 定位點配置: 在plist中添加配置: NSLocationAlwaysUsageDescription
  • 用CLLocationManager來進行定位
  • 在逆地址解析的方法reverseGeocodeLocation調用時若是遇到了block中一直出現Domain=GEOErrorDomain Code=-8 "(null)"之類的錯誤,將 CLGeocoder改爲全局變量便可
  • 以後這種簡單功能能夠直接經過蘋果內置方法來完成,不須要再經過引入高德SDK(省去了高德SDK的大小)

Project 2: Watch Demo

Watch's Demo.gif

我學到了

  • Update cocoaPods to 1.2.0
  • Learn how to use SnapKit (Quite similar with Masonry)
  • Learn how to use Timer in Swift
  • 我學到了: guard語句,詳見 guard詳解

Project 1: Change Custom Font

Custom Font.gif

我學到了

  • 如何修改字體屬性,熟悉字體屬性

  • 字體名稱能夠去storyboard中查詢,或者經過以下代碼來進行查詢:

    func printAllSupportedFontNames() {
    let familyNames = UIFont.familyNames
    for familyName in familyNames {
        print("++++++ \(familyName)")
        let fontNames = UIFont.fontNames(forFamilyName: familyName)
        for fontName in fontNames {
            print("----- \(fontName)")
        }}}
    複製代碼

寫在最後:

能堅持看到這裏的,我給大家手動雙擊666!

image.png

實話實說,文章有點標題黨,實際開發時間是40天左右,由於開發時間在下班後到睡覺前,因此有時由於要出去聚餐,有時犯懶,還有時晚上要你懂得,因此完成這三十個項目的時間比計劃的時間要長。。。

image.png

寫完這些項目,感受上一方面是提升了使用Swift語言的熟練度,另外一方面更是複習了一遍iOS開發的知識點,由於寫到後來我已經基本感受不出來跟用OC開發有什麼思路上的差別。這也回答了別人問過個人問題,「若是我如今學iOS開發,是應該學OC仍是Swift」:

我以爲從iOS SDK的熟悉角度來講,沒有本質區別,若是熟悉OC下對應語法去使用Swift寫沒有太大區別。因此與其花時間糾結不如趕忙找兩個項目上手進行練習。

image.png

下一步,我打算再從新梳理下Swift語法,對這些項目進行小規模的重構,從結構上去看看可否挖掘到Swift的特性,從另外一個角度(目前是功能角度)來學習Swift。因此也許還會有下一篇。

image.png
相關文章
相關標籤/搜索