抓住iOS的未來(lái) - 30天學(xué)習(xí)編寫(xiě)30個(gè)Swift小程序


更新:所有代碼已經(jīng)更新到Swift4.1,請(qǐng)移步github下載

=======================================================

iOS開(kāi)發(fā)已經(jīng)做了快4年了,聽(tīng)說(shuō)Swift也已經(jīng)有兩年多笋轨,但是一直都只是把學(xué)習(xí)停留在表面坊饶。無(wú)意中聽(tīng)說(shuō)了有一個(gè)叫Sam Lu在Twitter上發(fā)起了一個(gè)100天做40個(gè)Swift小程序的活動(dòng)旨涝,再加上國(guó)內(nèi)看到了Allen_朝輝寫(xiě)的Swift學(xué)習(xí)的文章套媚,心里暗自下了一個(gè)決定:30天寫(xiě)30個(gè)Swift小程序,希望能推動(dòng)自己學(xué)習(xí)Swift的計(jì)劃。這30個(gè)小程序難度不同扮碧,有的一個(gè)晚上就能寫(xiě)完趟章,有的要占用周末大部分時(shí)間來(lái)細(xì)研究。大部分不會(huì)的東西Google都能找到慎王,就算Swift版本沒(méi)有找到Objective-C版本然后用Swift重寫(xiě)就好蚓土,好在他們對(duì)應(yīng)關(guān)系比較明確。

用例方面赖淤,既參考了Sam Lu的40個(gè)小項(xiàng)目蜀漆,也參考了Allen_朝輝的項(xiàng)目,還有的是我自己仿寫(xiě)的知名App咱旱。

其實(shí)我并不是唯一在國(guó)內(nèi)發(fā)起這個(gè)30天30個(gè)Swift小程序并且將其開(kāi)源的作者确丢,但是我可能是唯一一個(gè)從頭到尾用XCode 8 + Swift3環(huán)境編寫(xiě)的作者。而且吐限,為了讓代碼更加可讀鲜侥,所有代碼完全手寫(xiě),而非用Storyboard(除了只能用Storyboard的毯盈,例如apple watch app)剃毒。實(shí)際上多人協(xié)作的項(xiàng)目中我們盡可能少用Storyboard,因?yàn)楹苋菀壮霈F(xiàn)沖突問(wèn)題搂赋。況且從學(xué)習(xí)的角度赘阀,storyboard很難說(shuō)清楚操作步驟是什么。在這上面我其實(shí)花了不少時(shí)間脑奠,但是我認(rèn)為很值得基公。

希望能有更多對(duì)Swift感興趣的開(kāi)發(fā)者加入這項(xiàng)#30天30個(gè)Swift小程序 的活動(dòng)里面來(lái)。以下為Github鏈接:
https://github.com/nimomeng/30-swift-projects-in-30-days

Project 30 - Google Now App

GoogleNow.gif

我學(xué)到了

  • 這次Project演示了Present/Dismissd如何做Transition動(dòng)畫(huà)宋欺,這和做Push/Pop的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的基本原理都是一樣的
  • 這次的動(dòng)畫(huà)參考了BubbleTransition的動(dòng)畫(huà)效果轰豆,在它之上加了修改,支持傳入自定義的UI屬性齿诞,方便做組合型動(dòng)畫(huà)(例如本例中按鈕不僅放大而且上下移動(dòng))
  • 動(dòng)畫(huà)變化的原理是將相應(yīng)的ViewController進(jìn)行Scale變換酸休,再通過(guò)一個(gè)Bubble的蒙版看起來(lái)像是氣泡效果
  • 其它的細(xì)節(jié)知識(shí)如下:
    • 畫(huà)圓形按鈕的方法,必須要cornerRadius屬性為邊長(zhǎng)的1/2祷杈,具體代碼如下:
        triggerButton.layer.cornerRadius = triggerButton.frame.width / 2
        triggerButton.layer.masksToBounds = true

Project 29 - Beauty Contest

BeautyContest.gif

我學(xué)到了

  • 這個(gè)項(xiàng)目是基于Yalantis的Koloda來(lái)制作的。 Koloda是一個(gè)非常好用的UIImage選擇器
  • Swift中的懶加載的使用方法:
    • 兩種方式:
lazy var firstWay = "first"

以及

lazy var secondWay: String = {return "Second"}()

注意:第二種方式要注意定義好字段類(lèi)型但汞,以便于編譯時(shí)的類(lèi)型檢查宿刮;以及不要忘記最后的小括號(hào)

  • 為什么要用Lazy:因?yàn)檫@里面需要先知道KolodaView的尺寸,才能定Overlay的尺寸私蕾。因此這里有一個(gè)依賴(lài)關(guān)系僵缺,因此用懶加載最合適。
  • Swift中的unowned和weak的區(qū)別:
    • unowned更像OC里的unsafe_unretained; weak還是那個(gè)weak踩叭。
    • 如果確定使用時(shí)一定不會(huì)被釋放磕潮,可以用unowned;否則最好用weak

Project 28 - SnapChat Like App

Snap Chat Like App.gif

我學(xué)到了

  • UIScrollView的基本使用和細(xì)節(jié)小點(diǎn),例如禁止彈跳的bounces屬性容贝,整頁(yè)切換的isPagingEnabled屬性,起始位置contentOffset屬性等

  • 加載子Viewcontroller的addChildViewController方法

  • "xxx class has no initializers"問(wèn)題:

      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!
    
  • 權(quán)限問(wèn)題自脯,具體錯(cuò)誤描述為:
    "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之后的權(quán)限問(wèn)題,在info.plist里添加相應(yīng)的權(quán)限以及描述即可嗤疯。
    本例中權(quán)限為:
    <key>NSCameraUsageDescription</key>
    <string>PhotoMe needs the camera to take photos. </string>
    <key>NSMicrophoneUsageDescription</key>
    <string>PhotoMe needs the microphone to record audio with Live Photos.</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>PhotoMe will save photos in the Photo Library.</string>

  • AVCaptureSession 的使用方法:

    • AVCaptureSession是AVFoundation的核心類(lèi),用于捕捉視頻和音頻,協(xié)調(diào)視頻和音頻的輸入和輸出流.
    • 創(chuàng)建AVCaptureSession實(shí)例冤今,并設(shè)置其sessionPreset值,也就是設(shè)置畫(huà)面的質(zhì)量茂缚。
    • 給Session添加Input戏罢。一般是Video或者Audio數(shù)據(jù),也可以?xún)烧叨继砑?即AVCaptureSession的輸入源AVCaptureDeviceInput。具體步驟是先獲取對(duì)應(yīng)的device實(shí)例(此時(shí)決定是用Video還是Audio)脚囊,再由實(shí)例獲取其Input Source龟糕。最后將input source add到session中。
    • 給Session添加Output悔耘,即AVCaptureSession的輸出源讲岁。一般輸出源分成:音視頻源,圖片源,文件源等。這里以靜態(tài)圖片的輸出源為例,指的是AVCapturePhotoOutput缓艳。最后將其也add到session中校摩。
    • 設(shè)置預(yù)覽圖層,即AVCaptureVideoPreviewLayer阶淘。在input,output等重要信息都添加到session以后,可以用session創(chuàng)建AVCaptureVideoPreviewLayer,這是攝像頭的視頻預(yù)覽層衙吩。這里千萬(wàn)別忘了將Layer添加到View中。
    • 啟動(dòng)Session溪窒,即captureSesssion.startRunning()
  • Photo的捕獲方法

    • AVCaptureSession設(shè)置成功坤塞,并啟動(dòng)
    • 創(chuàng)建AVCapturePhotoSettings對(duì)象,并配置相應(yīng)的屬性澈蚌,例如是否打開(kāi)flash摹芙,是否開(kāi)啟防抖模式等等
    • 執(zhí)行輸出源的capture方法,并制定具體的AVCapturePhotoSettings對(duì)象以及delegate對(duì)象
    • 在capture的delegate方法:
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?)```
中執(zhí)行獲取圖像的具體邏輯宛瞄。本例中是先將buffer轉(zhuǎn)換為data浮禾,再轉(zhuǎn)換為UIImage,最終write到相冊(cè)文件夾中坛悉。
- Reference: [iOS AVCaptureSession學(xué)習(xí)筆記](méi)(http://www.reibang.com/p/b5618066dc2c)



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

![Carousel Effect.gif](http://upload-images.jianshu.io/upload_images/2340489-f1c54a0e91776c53.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- UICollectionView的使用
  - 與UItableView的不同在于伐厌,每一個(gè)對(duì)應(yīng)的Cell(不論是content的cell還是header,footer的),都需要預(yù)先執(zhí)行register方法
  - Cell間距太窄的問(wèn)題裸影,可以通過(guò)minimumLineSpacingForSection的DataSource代理方法來(lái)解決掉
  - 如果選擇的layout為UICollectionViewFlowLayout挣轨,可以通過(guò)修改scrollDirection屬性來(lái)修改滾動(dòng)方向
- 自定義Layout要在對(duì)應(yīng)的子類(lèi)里實(shí)現(xiàn)如下方法
        prepare()
        shouldInvalidateLayout(forBoundsChange newBounds: CGRect)
        targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) 
        layoutAttributesForElements(in rect: CGRect)
其中:
  - prepare可以定義初始化操作
  - shouldInvalidateLayout,判斷是否需要更新每個(gè)Cell的bounds。如果我們的layout是那種每個(gè)cell需要?jiǎng)討B(tài)變化的layout轩猩,則設(shè)置為true卷扮;否則為了性能考慮,請(qǐng)?jiān)O(shè)置為false均践。默認(rèn)為flase晤锹。
  - targetContentOffset,如果我們需要圖片在滾動(dòng)的過(guò)程中在特定位置可以停下來(lái)(類(lèi)似iphone上專(zhuān)輯圖片的選擇)彤委,請(qǐng)?jiān)诖撕瘮?shù)中國(guó)年給出停下來(lái)的具體規(guī)則
  - layoutAttributesForElements 返回所有元素此時(shí)的所有布局鞭铆。我們會(huì)在這里定義在滾動(dòng)過(guò)程中所有其他元素的attribute布局相關(guān)屬性。例如本例中焦影,離屏幕中間越近车遂,圖片被縮放的越大;離屏幕越小斯辰,圖片被縮放的越小舶担。
  - Reference:
    - [UICollectionView綜合視圖](http://www.reibang.com/p/c0f4d0833ff8)
    - [用UICollectionView實(shí)現(xiàn)相冊(cè)功能]
(http://www.reibang.com/p/4f691ec731e5)

- Visual Effect View的使用
  - 盡量在需要模糊化的圖層之后添加進(jìn)去,會(huì)自動(dòng)虛化所覆蓋的圖層
        let blurEffect: UIBlurEffect = UIBlurEffect(style: .light)
        let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
        blurView.frame = self.view.bounds
        bgView.addSubview(blurView)

- UISegmentedControl 的使用(略)

- 其它:#selector()中的func如果帶有參數(shù)彬呻,請(qǐng)將具體參數(shù)也一起寫(xiě)進(jìn)去衣陶,例如: ``` #selector(action_segmentValueChanged(sender:)```這個(gè)規(guī)則和OC不太一樣柄瑰,要注意。


#Project 26 - Twitter-like Splash

![TwitterLikeSplash.gif](http://upload-images.jianshu.io/upload_images/2340489-94851cd3a75aff9a.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- 這個(gè)效果嘗試了用簡(jiǎn)單的UIView的animation方法會(huì)比較吃力剪况,因此轉(zhuǎn)而使用CAAnimation來(lái)做教沾。
- 我們需要的效果,設(shè)置keyPath為"bounds"拯欧。吐槽一下详囤,蘋(píng)果為什么不做一個(gè)枚舉财骨。镐作。。完整的keyPath列表如下所示:
![KeyPath 對(duì)照表](http://upload-images.jianshu.io/upload_images/2340489-8fd2ad3a1592d039.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640)
- 由于logo的動(dòng)畫(huà)定制化要求比較高隆箩,所以關(guān)于這個(gè)變化的動(dòng)畫(huà)该贾,選擇CAAnimation里的CAKeyFrameAnimation來(lái)做。主要關(guān)注keyTimes捌臊,values屬性杨蛋,也就是keyFrame的屬性。timingFunctions屬性是keyframe的count - 1理澎, 也就是frame1到frame2逞力,frame2到frame3的動(dòng)畫(huà)過(guò)渡函數(shù)。這個(gè)不多說(shuō)了糠爬,之前的Project有提到過(guò)寇荧。
- 在logo變大的過(guò)程中,logo中間的alpha值也應(yīng)該有白色變?yōu)橥该髦此恚虼藨?yīng)該先添加一個(gè)maskView,藏在最上層揩抡,logo層之下,作為白色的底镀琉。動(dòng)畫(huà)trigger的時(shí)間和duration與logo的動(dòng)畫(huà)保持步調(diào)一致峦嗤,并且記得在動(dòng)畫(huà)complete的時(shí)候被移除掉。這里使用了CABasicAnimation的animationDidStop代理來(lái)完成屋摔。
- logo的透明度變化既可以使用簡(jiǎn)單的UIView的animation方法來(lái)做烁设,也可以采用layer級(jí)別的CABasicAnimation來(lái)完成。因?yàn)閷?duì)前者比較熟悉了钓试,所以我在這里使用后者装黑,注意keyPath是````opacity````。代碼比較簡(jiǎn)單亚侠,這里不贅述曹体。
- 整體效果還是很炫的:)
- CAKeyFrameAnimation參考[此篇文檔](http://blog.csdn.net/u013316626/article/details/54379540)

# Project 25 Custom Transition

![CustomTransition.gif](http://upload-images.jianshu.io/upload_images/2340489-8d998c7d397d2102.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- NavigationController的動(dòng)畫(huà)是可以自定義的,去實(shí)現(xiàn)UINavigationControllerDelegate里的方法就好
- 如果切換動(dòng)畫(huà)只需要關(guān)注之前的VC和之后的VC硝烂,不需要關(guān)注中間過(guò)程箕别,直接實(shí)現(xiàn)以下方法即可:

navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

- 上述方法的返回值````UIViewControllerAnimatedTransitioning````需要自定義動(dòng)畫(huà),需要實(shí)現(xiàn)````UIViewControllerAnimatedTransitioning````代理,實(shí)現(xiàn)具體的兩個(gè)方法:
  - 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)時(shí)間串稀,直接返回一個(gè)時(shí)間即可

transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval

 - 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)過(guò)程除抛,如下所示,這個(gè)比較復(fù)雜:

animateTransition(using transitionContext: UIViewControllerContextTransitioning)

    - 第一步母截,獲得轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的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

    - 之后是動(dòng)畫(huà)前的準(zhǔn)備工作清寇,例如image賦值喘漏,例如坐標(biāo)的計(jì)算:

let snapshotView = fromVC.selectedCell.imageView.snapshotView(afterScreenUpdates: false)
snapshotView?.frame = container.convert(fromVC.selectedCell.imageView.frame, from: fromVC.selectedCell)
....

    - 最后當(dāng)然是Animation動(dòng)畫(huà)的執(zhí)行邏輯了,可以通過(guò)UIView的animate方法去實(shí)現(xiàn)华烟。具體參數(shù)和方法可以參考之前的Project來(lái)進(jìn)靈活組合翩迈。
    - 進(jìn)入的動(dòng)畫(huà)最后一定不能忘記加上````            transitionContext.completeTransition(true)
````,說(shuō)明了讓navigationController來(lái)接管控制權(quán)利(在completion的block中)
    - 退出的動(dòng)畫(huà)記得帶上````            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
````說(shuō)明動(dòng)畫(huà)執(zhí)行完成
- 如果需要關(guān)注動(dòng)畫(huà)的執(zhí)行過(guò)程,則在上述的基礎(chǔ)之上還應(yīng)該實(shí)現(xiàn)下述方法:

navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

其中盔夜,````UIViewControllerInteractiveTransitioning````是動(dòng)畫(huà)過(guò)渡對(duì)象
- 獲取iOS中手從左往右沿屏幕滑動(dòng)的事件负饲,是通過(guò)````UIScreenEdgePanGestureRecognizer````方法并設(shè)置其edges為left實(shí)現(xiàn)的:

let edgePanGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(edgePanGestrueAction(_:)))
edgePanGesture.edges = UIRectEdge.left

- 這篇教程針對(duì)的是Push與Pop的自定義動(dòng)畫(huà)的制作
- 參考[文檔1](http://kittenyang.com/magicmove/),[文檔2](http://www.reibang.com/p/38cd35968864)喂链,并在他們的基礎(chǔ)之上做了改動(dòng)
- 這個(gè)例子我很喜歡返十,圖片是羅斯科。


# Project 24 - Vertical Menu Transition

![Vertical Menu Transition.gif](http://upload-images.jianshu.io/upload_images/2340489-d8ac5f10378c6199.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- 本文和Google Now App項(xiàng)目思路一致椭微,都是針對(duì)Present/Dismiss的操作進(jìn)行自定義Transition
- 由于動(dòng)畫(huà)需要局部截圖洞坑,因此建議將Present和Dismiss的Transition寫(xiě)到一起,通過(guò)一個(gè)變量來(lái)進(jìn)行不同動(dòng)畫(huà)的切換和控制赏表。變量可以通過(guò)````animationController(forDismissed dismissed: UIViewController)````與````animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController)````來(lái)進(jìn)行設(shè)置检诗。
- target-action方式,一般會(huì)將target設(shè)置為self瓢剿。如果設(shè)置為對(duì)應(yīng)的delegate逢慌,則action字段應(yīng)該填寫(xiě)````#selector(CustomTransitionDelegate.functionName)````
- 在Present/Dismiss的自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)中,記得在complete回調(diào)中加入動(dòng)畫(huà)結(jié)束語(yǔ)句塊:
transitionContext.completeTransition(true)
fromViewController?.endAppearanceTransition()
toViewController?.endAppearanceTransition()


#Project 23 - Side Navigation App

![SideNavigation.gif](http://upload-images.jianshu.io/upload_images/2340489-649e18fc36d15c9a.gif?imageMogr2/auto-orient/strip)

####我學(xué)到了
- Swift-OC混編方法
  - 新建一個(gè)頭文件间狂,例如名為Bridge.h
  - 單擊Project文件攻泼,選擇Build Setting,找到Objective-C Bridge Header,輸入Bridge.h的路徑
  - 之后所有需要在swift文件中引用的OC文件的頭文件放到Bridge.h中進(jìn)行import
  
![Swift-OC](http://upload-images.jianshu.io/upload_images/2340489-dcfebb7a5446ea03.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/500)

- 側(cè)滑效果借鑒了[SWRevealViewController](https://github.com/John-Lluch/SWRevealViewController)鉴象,使用步驟如下(原項(xiàng)目只提到了OC中的調(diào)用方法)
  - 項(xiàng)目中至少有以下幾類(lèi)viewController:第一頁(yè)展示的VC忙菠,比如FrontViewController;tabeView所在的MenuViewController
  - 在AppDelegate中根據(jù)規(guī)則創(chuàng)建自定義Window纺弊,具體步驟為:
    - 建立UIWindow
    - 新建兩個(gè)UINavigationController,分別以FrontViewController和MenuViewController為rootViewController
    - 實(shí)例化SWRevealViewController牛欢,并設(shè)置rearViewController的值和frontViewController的值。其中淆游,rearViewController是tableView所在的UINavigationController,frontViewController是FrontViewController所在的UINavigationController
    - 將實(shí)例化的SWRevealViewController設(shè)置為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()

  - 需要在每一個(gè)ViewController中加入左滑激活Menu的邏輯傍睹,一句話(huà):

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](http://upload-images.jianshu.io/upload_images/2340489-bf50d015a23d2062.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- 本次涉及到最基本的UIAnimation,很多復(fù)雜的Animation其實(shí)是各種簡(jiǎn)單的Animation的疊加拾稳,所以不能輕視
- Position的Animation既可以通過(guò)直接修改frame的origin屬性吮炕,也可以直接通過(guò)UIView的transform來(lái)進(jìn)行修改
- Opacity直接改Alpha值就可以了
- Scale是修改了UIView的transform,傳入要縮放的相對(duì)比例并創(chuàng)建對(duì)應(yīng)的CGAffineTransform對(duì)象访得。例如:

heartView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)

- Color是直接修改backgroundColor就可以了
- Rotation是通過(guò)修改UIView的transform龙亲,傳入要旋轉(zhuǎn)的值并創(chuàng)建對(duì)應(yīng)的CGAffineTransform對(duì)象。其中悍抑,值為0 - 2*Pi之間鳄炉,表示0到360°之間。注意传趾,正值為逆時(shí)針轉(zhuǎn)動(dòng)迎膜。例如:

self.rollingBallView.transform = CGAffineTransform(6.28)


# Project 21 CoreData App

![CoreDataAppDemo.gif](http://upload-images.jianshu.io/upload_images/2340489-7113254108dd8b22.gif?imageMogr2/auto-orient/strip)

#### 我學(xué)到了
- 一定要勾選 UseCoreData,這樣在Appdelegate里會(huì)自動(dòng)生成部分代碼
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/2340489-1f3d1001ef0571ec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 不一定非要通過(guò)Editor生成SubClass
- 本例中Entity如圖所示:

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/2340489-79ba92016a6aa5c7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800)

- 在需要調(diào)用CoreData的類(lèi)中浆兰,import CoreData
- 本例比較簡(jiǎn)單,只進(jìn)行了getResult和Add的操作珊豹,思路分別為:
  - getResult:
  let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: EntityName)
    do {
        let searchResults = try getContext().fetch(fetchRequest)
        dataSource = searchResults as! [TodoList]
    } catch  {
       // todo error handler
    }
注意簸呈,或取出來(lái)的searchResult可以直接實(shí)例化為T(mén)odoList(TodoList是我的Entity名字),這樣后續(xù)就可以直接使用TodoList的content方法了店茶。
  - saveContent:
    let context = getContext()
    // 定義一個(gè)entity蜕便,這個(gè)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{}
對(duì)應(yīng)getConent方法的代碼兩行:

let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext

如此操作后使用的時(shí)候直接通過(guò)獲取TodoList對(duì)象,然后調(diào)用其content方法即可完成贩幻。
````cell.textLabel?.text = (dataSource[indexPath.row]).content````
- UIAlertController添加輸入款的方法:

alertController.addTextField { (textField) in
textField.placeholder = "Please input the todo Item"}

- 該方法在XCode8.3 + Swift3.2測(cè)試通過(guò)轿腺,CoreData在iOS10的變化很大,之前的版本可能和上述操作方法有出入
- [參考文章](http://www.cnblogs.com/Free-Thinker/p/5944551.html)

#Project 20 - Apple Watch OS App - Guess Game


![WatchApp_Guess.gif](http://upload-images.jianshu.io/upload_images/2340489-7843cf75c364d69d.gif?imageMogr2/auto-orient/strip)


#### 我學(xué)到了
- Watch程序丛楚,需要在create project的先選擇Watch OS的Section族壳,之后選擇如下:
![watchOS.png](http://upload-images.jianshu.io/upload_images/2340489-c35efafcea74c758.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/600)

- watch中的UI只可以通過(guò)Storyboard來(lái)進(jìn)布局,布局文件在WatchKit App中的Interface.storyboard中

- 例子中涉及到了watch和主app的交互趣些,這里使用的是````WCSession````方法仿荆,使用步驟如下:
  - 確定app所在設(shè)備是否支持WCSession
  - 生成一個(gè)WCSession對(duì)象,并設(shè)置其delegate
  - 激活此WCSession對(duì)象
至此部分坏平,代碼為:
  let wcsession = WCSession.default()
    if WCSession.isSupported() {
        wcsession.delegate = self
        wcsession.activate()
    }
  - 發(fā)送通信(watch與主app之間)通過(guò)WCSession對(duì)象的updateApplicationContext方法來(lái)進(jìn)行拢操,例如
````try wcsession.updateApplicationContext(["numberToBeGuessed": number])````
  - 接收方通過(guò)代理方法來(lái)接收并解析發(fā)送的消息
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) 
- 示例參考了[WatchKit Introduction: Building a Simple Guess Game](http://www.appcoda.com/watchkit-introduction-tutorial/)

# Project 19 - TodayWidget


![TodayWidget.gif](http://upload-images.jianshu.io/upload_images/2340489-121ed2f83ae5409d.gif?imageMogr2/auto-orient/strip)


#### 我學(xué)到了
- 創(chuàng)建Today Widget: File > New > Target…,然后選擇 iOS 中的 Application Extension 的 Today Extension
- 為了方便Widget與App數(shù)據(jù)共享舶替,需要切換成App Group模式凄硼。步驟為打開(kāi)主target,選擇capability,找到App Group,打開(kāi):

![AppGroup](http://upload-images.jianshu.io/upload_images/2340489-f638e5eaf80c5bd7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/640)
- 在主Target下為這個(gè)app group添加一個(gè)名稱(chēng)八毯,然后去Extension的target下去采用相同操作,并勾選這個(gè)group
- 我們可以采用UserDefault作為主app與widget之間的共享存儲(chǔ)你踩。但是此處不能使用standardUserDefaults,只能通過(guò)suiteName的方式來(lái)進(jìn)行共享病苗,且名字是之前在app group中添加的名稱(chēng),代碼如下:

let userDefault = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")

- 在Widget的ViewController里寫(xiě)入相應(yīng)的讀取邏輯代碼:

let userDefaults = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")
var leftTimeWhenQuit = userDefaults?.double(forKey: "lefttime")

為了想讓widget里的數(shù)據(jù)也進(jìn)行同步更新,可以在extension的代碼里也加入一個(gè)timer來(lái)進(jìn)行同步操作篮绿。這樣widge和主程序的widge即可同步
- 如果想了解更多關(guān)于Widget的使用,請(qǐng)[參考文檔](https://onevcat.com/2014/08/notification-today-widget/)

# Project 18 - Spotlight Search

![SpotlightSearch.gif](http://upload-images.jianshu.io/upload_images/2340489-e7e702f5b8e98752.gif?imageMogr2/auto-orient/strip)

####我學(xué)到了
- Spotlight Search的使用:
  - 引入CoreSpotlight庫(kù)````import CoreSpotlight````
  - 這里用CSSearchableItem來(lái)進(jìn)行需要被索引的對(duì)象的添加吕漂。先創(chuàng)建一個(gè)CSSearchableItemAttributeSet亲配,也就是Item的屬性類(lèi),對(duì)具體屬性進(jìn)行添加惶凝,包括title,contentDescription吼虎,以及thumbData的設(shè)置。
  - 需要單獨(dú)說(shuō)明的是苍鲜,CSSearchableItemAttributeSet對(duì)象的thumbData可以通過(guò)獲取UIImage對(duì)象后對(duì)其UIImageJPEGRepresentation方法來(lái)獲取或者UIImagePNGRepresentation方法來(lái)獲人蓟摇(取決于image對(duì)應(yīng)的文件是什么類(lèi)型)
  - 創(chuàng)建CSSearchableItem對(duì)象,并進(jìn)一步通過(guò)indexSearchableItems方法將創(chuàng)建的CSSearchableItem添加到索引中:

let tmpItems = [searchItem]
CSSearchableIndex.default().indexSearchableItems(tmpItems) { (error) in
}

  - 如果調(diào)試過(guò)程中混滔,發(fā)現(xiàn)模擬器上重新了之前的spotlight緩存無(wú)法清除的情況洒疚,請(qǐng)更換新的模擬器,或者重置模擬器坯屿∮秃或者干脆切換成真機(jī)進(jìn)行調(diào)試,真機(jī)這種情況少一些:)


# Project 17 - 3D Touch Quick Action

![3DTouchQuickAction.gif](http://upload-images.jianshu.io/upload_images/2340489-66b03a3d2f4f541f.gif?imageMogr2/auto-orient/strip)

- 3D Touch的具體功能分成兩種:第一種是在SpringBoard里長(zhǎng)按圖標(biāo)進(jìn)行直接功能跳轉(zhuǎn)领跛,第二種是在APP內(nèi)部對(duì)特定的視圖元素長(zhǎng)按進(jìn)行Peek & Pop
- 在做任何3D Touch相關(guān)功能的引入之前乏德,務(wù)必確保用戶(hù)機(jī)型支持3D Touch。
````self.window?.traitCollection.forceTouchCapability == .available````
- 針對(duì)第一種功能吠昭,建立````UIApplicationShortcutItem````類(lèi)型的Item喊括,然后設(shè)置application的shortcutItems屬性即可。要注意矢棚,在設(shè)置icon時(shí)郑什,只可以設(shè)置系統(tǒng)內(nèi)置的集中icon,不支持自定義圖標(biāo)
- 針對(duì)第二種功能幻妓,需要在想加入支持3D Touch的VC中注冊(cè)并添加相應(yīng)事件
 - 添加````UIViewControllerPreviewingDelegate````
 - 在此VC種注冊(cè)3D Touch支持蹦误。````self.registerForPreviewing(with: self, sourceView: self.view)````
 - 實(shí)現(xiàn)兩種delegate:
````    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? 
    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) 
  • 如果想實(shí)現(xiàn)例子中的額外Action button,需要override對(duì)應(yīng)的previewActionItems屬性,并返回你需要的UIPreviewAction的Array

Project 16 - LoginAnimation

LoginAnimation.gif

我學(xué)到了

  • 開(kāi)始以為很簡(jiǎn)單肉津,普通UIView的Animation方法即可完成强胰。后來(lái)發(fā)現(xiàn)彈跳效果并不是我使用的常規(guī)方法可以完成的
  • 彈跳動(dòng)畫(huà)需要使用usingSpringWithDamping來(lái)完成,其中的屬性要注意:
    • usingSpringWithDamping:值越小動(dòng)畫(huà)越夸張妹沙,借用網(wǎng)上圖來(lái)說(shuō)明其區(qū)別:

      usingSpringWithDamping
    • initialSpringVelocity:值越大則起始速度越大偶洋,再借用網(wǎng)上圖片來(lái)說(shuō)明其區(qū)別:


      initialSpringVelocity
    • options的各個(gè)動(dòng)畫(huà)曲線(xiàn)有何區(qū)別:可以看圖來(lái)進(jìn)行區(qū)分:

options
  • UIView.animation的usingSpringWithDamping與不帶usingSpringWithDamping的參數(shù)動(dòng)畫(huà)有什么區(qū)別呢?可以看下圖:
Animation Comparation
  • 帶Spring屬性的動(dòng)畫(huà)太有意思了距糖!:)
  • 此部分參考文檔1,文檔2

Project 15 - Tumblr Menu

Tumblr Menu.gif

我學(xué)到了

  • 這個(gè)例子本質(zhì)上是對(duì)動(dòng)畫(huà)+BlurEffect
  • 三排的動(dòng)畫(huà)有一個(gè)先后順序玄窝,這個(gè)可以通過(guò)animation的delay參數(shù)進(jìn)行調(diào)節(jié)
  • button的上圖下文效果需要設(shè)置牵寺,這里自定義了一個(gè)CustomButton,對(duì)樣式進(jìn)行了封裝恩脂。參考了這篇文章

Project 14 - Video Splash

VideoSplash.gif

我學(xué)到了

  • 創(chuàng)建一個(gè)AVPlayerViewController帽氓,并將其view放到背景中
  • 之后結(jié)合AVPlayerViewController進(jìn)行視頻播放,并自動(dòng)循環(huán)
  • 視頻播放部分借鑒了此篇文章中的第十個(gè)用例俩块,據(jù)說(shuō)也是參考了一個(gè)叫VideoSplashViewController的庫(kù)

Project 13: Animation In TableViewCell

AnimationInTableViewCell.gif

我學(xué)到了

  • 開(kāi)始的思路是在willDisplay的delegate里進(jìn)行動(dòng)畫(huà)操作黎休,效果良好,但是發(fā)現(xiàn)在滾動(dòng)cell時(shí)發(fā)生cell錯(cuò)亂的現(xiàn)象玉凯,原因是在滾動(dòng)時(shí)cell重繪導(dǎo)致重新調(diào)用willDisplay進(jìn)而坐標(biāo)錯(cuò)誤势腮。粗看了下,解決起來(lái)有點(diǎn)兒麻煩漫仆,于是換思路捎拯。以此這種“進(jìn)場(chǎng)動(dòng)畫(huà)”不應(yīng)該在渲染過(guò)程中的delegate中執(zhí)行。
  • 將動(dòng)畫(huà)放到ViewWillAppear里來(lái)做盲厌∈鹫眨可以通過(guò)tableView的visibleCells獲取將要顯示的所有cell的Array,逐一遍歷來(lái)進(jìn)行動(dòng)畫(huà)操作狸眼。
  • 改變Cell的動(dòng)畫(huà)藤树,采用上一章所說(shuō)的usingSpringWithDamping的動(dòng)畫(huà),usingSpringWithDamping設(shè)置為0.8拓萌,initialSpringVelocity設(shè)置為0.(不然動(dòng)畫(huà)會(huì)彈跳過(guò)大,造成順次露出白色間隙升略,很不美觀)
  • 改變Cell的具體方式微王,既可以直接操作cell.frame.origin.y,也可以通過(guò)cell.transform = CGAffineTransform(translationX: 0, y: tableHeight)品嚣,效果是一樣的炕倘。不過(guò)如果要用到縮放或者旋轉(zhuǎn)的動(dòng)畫(huà),恐怕只能使用后者了翰撑。
  • 動(dòng)畫(huà)確實(shí)是很有意思的:)

Project 12 - Emoji Slot Machine

Emoji Slot Machine.gif

我學(xué)到了

  • 乍一看沒(méi)思路罩旋,本來(lái)打算用三個(gè)collectionView來(lái)做,但是發(fā)現(xiàn)有點(diǎn)兒復(fù)雜
  • 后來(lái)轉(zhuǎn)變思路眶诈,用UIPickerView來(lái)做涨醋,component設(shè)置為3即可
  • 隨機(jī)數(shù)用arc4random()來(lái)算出來(lái),之后使用UIPickerView的selectRow方法進(jìn)行設(shè)置值即可達(dá)到老虎機(jī)的效果
  • 為了仿真逝撬,不能讓pickerView轉(zhuǎn)到第一個(gè)或者最后一個(gè)浴骂,不然就會(huì)碰到邊界了,因此在算隨機(jī)Row時(shí)宪潮,使用Int(arc4random())%(emojiArray.count - 2) + 1的方法來(lái)實(shí)現(xiàn)
  • 三個(gè)同時(shí)一致的情況實(shí)在太少了溯警,因此為了方便模擬趣苏,我加了個(gè)雙擊操作,雙擊強(qiáng)制出666梯轻。食磕。。
  • 這個(gè)case還挺有意思的喳挑,哈哈

Project 11 - Gradient in TableView

GradientInTableView.gif

我學(xué)到了

  • 這個(gè)比較簡(jiǎn)單彬伦,注意將CAGradientLayer應(yīng)用在UITableViewCell上即可
  • 建議將CAGradientLayer作為cell的backgroundView,而不是直接在cell.layer上進(jìn)行添加
  • 美觀起見(jiàn)蟀悦,隱藏掉Cell的Select效果以及separatorStyle:
    table.separatorStyle = .none
    cell.selectionStyle = .none

Project 10 - Stretchy Header

Stretchy Header.gif

我學(xué)到了

  • 通過(guò)監(jiān)聽(tīng)ScrollView(及其子類(lèi))的scrollViewDidScroll代理可以知道scrollView被拉動(dòng)的位移(offset)
  • 通過(guò)位移以及限定的縮放值可以得出圖片需要放大的倍率
  • 通過(guò)設(shè)置ImageView的transform來(lái)完成修改即可媚朦,核心代碼為
bannerImgView.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)

Project 9 - Swipeable Cell

Swipeable Cell.gif

我學(xué)到了

簡(jiǎn)單起見(jiàn),我用Project 13的代碼基礎(chǔ)上進(jìn)行修改日戈,換了個(gè)清爽的綠色:)

  • 實(shí)現(xiàn)editActionsForRowAt這個(gè)delegate方法询张,返回值是Array<UITableViewRowAction>,新建幾個(gè)你需要的功能返回即可
  • 每一個(gè)Action直接通過(guò)UITableViewRowAction的init方法新建即可。在新建方法里有block浙炼,直接將點(diǎn)擊邏輯寫(xiě)進(jìn)去就行了份氧。
  • 這種交互適用于Accessory比較簡(jiǎn)單的情況,例如對(duì)交互按鈕大小和內(nèi)容無(wú)要求的情況弯屈;如果有特殊要求蜗帜,需要自定義UITableViewCell,手動(dòng)控制Cell與捕捉UIPanGesture來(lái)進(jìn)行實(shí)現(xiàn)资厉。注意厅缺,這種方式要排除上下滑動(dòng)Cell的情況,不要錯(cuò)誤觸發(fā)宴偿。

Project 8 - Color Gradient

ColorGradient.gif

我學(xué)到了

  • 顏色漸變效果采用的是類(lèi)CAGradientLayer
  • 色彩空間的概念可以借助于Color數(shù)組來(lái)實(shí)現(xiàn)湘捎,注意,成員變量是CGColor類(lèi)型窄刘,然后通過(guò)設(shè)置CAGradientLayer的colors屬性來(lái)實(shí)現(xiàn)
  • 上下滑動(dòng)時(shí)改變顏色是通過(guò)加PanGestureRecognizer來(lái)實(shí)現(xiàn)窥妇。具體效果參考了應(yīng)用Solar
    Solar

Project 7 - Simple Photo Browser

SimplePhotoBrowser.gif

我學(xué)到了

  • 縮放圖片的方式:將imageView添加到ScrollView上
  • 設(shè)置好scrollView的max/minZoomScale
  • 設(shè)置好delegate對(duì)象,至少實(shí)現(xiàn)viewForZooming的代理方法

Project 6 - Video Player

Video Player.gif

我學(xué)到了

  • AVPlayer:視頻播放器實(shí)體
  • AVPlayerViewController:簡(jiǎn)單封裝了的視頻播放器娩践,有簡(jiǎn)單的控制功能
  • AVPlayerLayer:視頻的Layer層活翩,所有功能需要寫(xiě)控件進(jìn)行控制,適合對(duì)播放器進(jìn)行深度開(kāi)發(fā)
  • 后臺(tái)播放的plist設(shè)置方式
  • do...catch...語(yǔ)法的使用
  • background modes的設(shè)置翻伺。
  • 如何做到app在后臺(tái)長(zhǎng)期運(yùn)行:參考簡(jiǎn)書(shū)的文章
  • 如何顯示鎖屏信息材泄,以及如何響應(yīng)鎖屏設(shè)置(實(shí)現(xiàn)remoteControlReceived的代理方法)

Project 5 - Pull To Refresh

PullToRefresh.gif

我學(xué)到了

  • 下拉刷新組件: UIRefreshControl
    設(shè)置好提示文字attributedTitle,添加好target事件(UIControlEvents.valueChanged事件)后,添加到tableView中穆趴,即可

Project 4 - Limited Input Text Field

Limit Input Text Field.gif

我學(xué)到了

  • 通過(guò)新建UIBarButtonItem來(lái)創(chuàng)建navigationBarItem的左右Item
  • 通過(guò)TextView的textViewDidChange事件捕捉當(dāng)前輸入內(nèi)容脸爱,從而進(jìn)行限制輸入字?jǐn)?shù)
  • 通過(guò)監(jiān)聽(tīng)NSNotification.Name.UIKeyboardWillChangeFrame事件來(lái)監(jiān)視Keyboard的彈出和收起。在對(duì)應(yīng)回調(diào)中未妹,通過(guò)note.userInfo?[UIKeyboardFrameEndUserInfoKey]來(lái)拿到鍵盤(pán)的endFrame簿废,從而拿到鍵盤(pán)的高度空入,對(duì)計(jì)數(shù)器進(jìn)行frame操作
  • 同理,通過(guò)note.userInfo?[UIKeyboardAnimationDurationUserInfoKey]拿到鍵盤(pán)的動(dòng)畫(huà)duration族檬,進(jìn)而可以通過(guò)UIView的animation動(dòng)畫(huà)做到同步變化計(jì)數(shù)器的frame

Project 3 - Find My Position

Find My Position.gif

我學(xué)到了

  • 定位點(diǎn)配置:
    在plist中添加配置:
    <key>NSLocationAlwaysUsageDescription</key>
    <true/>
  • 用CLLocationManager來(lái)進(jìn)行定位
  • 在逆地址解析的方法reverseGeocodeLocation調(diào)用時(shí)如果遇到了block中一直出現(xiàn)Domain=GEOErrorDomain Code=-8 "(null)"之類(lèi)的錯(cuò)誤歪赢,將 CLGeocoder改成全局變量即可
  • 之后這種簡(jiǎn)單功能可以直接通過(guò)蘋(píng)果內(nèi)置方法來(lái)完成,不需要再通過(guò)引入高德SDK(省去了高德SDK的大械チ稀)

Project 2: Watch Demo

Watch's Demo.gif

我學(xué)到了

  • Update cocoaPods to 1.2.0
  • Learn how to use SnapKit (Quite similar with Masonry)
  • Learn how to use Timer in Swift
  • 我學(xué)到了: guard語(yǔ)句埋凯,詳見(jiàn) guard詳解

Project 1: Change Custom Font

Custom Font.gif

我學(xué)到了

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

  • 字體名稱(chēng)可以去storyboard中查詢(xún)扫尖,或者通過(guò)如下代碼來(lái)進(jìn)行查詢(xún):

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

寫(xiě)在最后:

能堅(jiān)持看到這里的白对,我給你們手動(dòng)雙擊666!

image.png

實(shí)話(huà)實(shí)說(shuō)换怖,文章有點(diǎn)標(biāo)題黨甩恼,實(shí)際開(kāi)發(fā)時(shí)間是40天左右,因?yàn)殚_(kāi)發(fā)時(shí)間在下班后到睡覺(jué)前沉颂,所以有時(shí)因?yàn)橐鋈ゾ鄄吞趺袝r(shí)犯懶,還有時(shí)晚上要你懂得铸屉,所以完成這三十個(gè)項(xiàng)目的時(shí)間比計(jì)劃的時(shí)間要長(zhǎng)钉蒲。。彻坛。

image.png

寫(xiě)完這些項(xiàng)目顷啼,感覺(jué)上一方面是提高了使用Swift語(yǔ)言的熟練度,另一方面更是復(fù)習(xí)了一遍iOS開(kāi)發(fā)的知識(shí)點(diǎn)昌屉,因?yàn)閷?xiě)到后來(lái)我已經(jīng)基本感覺(jué)不出來(lái)跟用OC開(kāi)發(fā)有什么思路上的差異线梗。這也回答了別人問(wèn)過(guò)我的問(wèn)題,“如果我現(xiàn)在學(xué)iOS開(kāi)發(fā)怠益,是應(yīng)該學(xué)OC還是Swift”:

我覺(jué)得從iOS SDK的熟悉角度來(lái)說(shuō),沒(méi)有本質(zhì)區(qū)別瘾婿,如果熟悉OC下對(duì)應(yīng)語(yǔ)法去使用Swift寫(xiě)沒(méi)有太大區(qū)別蜻牢。所以與其花時(shí)間糾結(jié)不如趕緊找兩個(gè)項(xiàng)目上手進(jìn)行練習(xí)。

image.png

下一步偏陪,我打算再重新梳理下Swift語(yǔ)法抢呆,對(duì)這些項(xiàng)目進(jìn)行小規(guī)模的重構(gòu),從結(jié)構(gòu)上去看看能否挖掘到Swift的特性笛谦,從另一個(gè)角度(目前是功能角度)來(lái)學(xué)習(xí)Swift抱虐。所以也許還會(huì)有下一篇。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饥脑,一起剝皮案震驚了整個(gè)濱河市恳邀,隨后出現(xiàn)的幾起案子懦冰,更是在濱河造成了極大的恐慌,老刑警劉巖谣沸,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刷钢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乳附,警方通過(guò)查閱死者的電腦和手機(jī)内地,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赋除,“玉大人阱缓,你說(shuō)我怎么就攤上這事【倥” “怎么了荆针?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)并蝗。 經(jīng)常有香客問(wèn)我祭犯,道長(zhǎng),這世上最難降的妖魔是什么滚停? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任沃粗,我火速辦了婚禮,結(jié)果婚禮上键畴,老公的妹妹穿的比我還像新娘最盅。我一直安慰自己,他們只是感情好起惕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布涡贱。 她就那樣靜靜地躺著,像睡著了一般惹想。 火紅的嫁衣襯著肌膚如雪问词。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天嘀粱,我揣著相機(jī)與錄音激挪,去河邊找鬼。 笑死锋叨,一個(gè)胖子當(dāng)著我的面吹牛垄分,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娃磺,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼薄湿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起豺瘤,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吆倦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后炉奴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逼庞,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年瞻赶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赛糟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡砸逊,死狀恐怖璧南,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情师逸,我是刑警寧澤司倚,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站篓像,受9級(jí)特大地震影響动知,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜员辩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一盒粮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奠滑,春花似錦丹皱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至杰赛,卻和暖如春呢簸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乏屯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工阔墩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓶珊。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像耸彪,于是被迫代替她去往敵國(guó)和親伞芹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容