微博,微信圖片瀏覽過渡動畫的實現(xiàn)

原文刊載在:jmStack的個人主頁 轉載請說明出處

大家在平常用微信,微博的過程中肯定(對,就是肯定)都有查看過朋友圈和微博所發(fā)布的照片,當點擊九宮格的某一圖片時圖片會慢慢的放大并進入全屏,左右滑動查看另一張.輕點圖片又會以動畫的方式慢慢縮小回到滑動之后對應的圖片.說了這么多估計你還是不知道我在講什么鬼,一張動圖勝過千言萬語.畢竟語言這東西真不是碼農的特長...

微博九宮格
微信九宮格

上面兩張gif點開時的動畫不是很明顯,你可以在真機上查看更真實效果.接下來我會通過一個Demo來介紹實現(xiàn)這種效果的具體思路,如果你有更好的思路,請求賜教


Demo 預覽

在開始之前先看一看最終的效果

Demo 預覽

這個Demo抓取了美麗說的在線圖片,這里對毫不知情的美麗說表示感謝.

在看下面的部分之前假定你已經(jīng)撐握了Swift,網(wǎng)絡請求,會使用UICollectionView等基礎組件的技能.如若不能撐握建議先了解相關知識

DemoGitHub地址

Demo 結構分析

在Demo中主要包括兩個主要的視圖結構:一 縮略圖(主視圖)的瀏覽 二 大圖的瀏覽. 這兩個視圖中所要展示的內容都是有規(guī)律的矩形所以都可以用UICollectionView來實現(xiàn).

兩者的區(qū)別在于縮略圖是垂直方向的布局而大圖是水平方向上的布局方式.兩個UICollectionView的cell的內容只包含一個UIImageView.在大圖瀏覽視圖中有一個

需要注意的細節(jié):為了圖片瀏覽的效果每張圖片之間是有一定間隔的,如果讓每個cell都填充整個屏幕,圖片的寬度等于cell的寬度再去設置cell的間隔來達到間隔的效果會在停止滑動圖片時黑色的間隔會顯現(xiàn)在屏幕中(如下圖),這并不是我們想看到的結果.

錯誤的大圖cell寬設置

出現(xiàn)這個問題的原因是UICollectionView的分頁(pagingEnabled)效果是以UICollectionView的寬來滾動的,也就是說不管你的cell有多大每次滾動總是一個UICollectionView自身的寬.要實現(xiàn)這個效果有個小技巧,相關內容會在大圖瀏覽的實現(xiàn)一節(jié)中介紹.

主視圖圖片瀏覽的實現(xiàn)

根據(jù)上一節(jié)得出的結論,主視圖采用colletionview,這部分實現(xiàn)沒什么特別的技巧,但在添加collectionview之前需要添加幾個基礎組件.

因為我們所需的圖片是抓取美麗說的網(wǎng)絡圖片,所以我們需要一個網(wǎng)絡請求組件,另外為展示圖片還需要添加對應的數(shù)據(jù)模型.但這兩個組件的內容不是本篇博文主要討論的問題

另外這兩個組件相對較基礎,就不廢太多口水.具體實現(xiàn)可以參看GitHub源碼,每次網(wǎng)絡請求這里設置為30條數(shù)據(jù),這里提到也是為了讓你在下面的章節(jié)看到相關部分不至于感到疑惑,

添加完這兩個基礎組件之后,就可以實現(xiàn)縮略圖的瀏覽部分了.為方便起見縮略圖view的控制器采用UICollectionViewController,在viewDidLoad函數(shù)中設置流水布局樣式,實現(xiàn)collectionview的datasource,delegate.這部分都是一些常規(guī)的寫法,這里要關注的是datasource和delegate的下兩個函數(shù).


override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

// 從緩存池中取出重用cell

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as? CollectionViewCell

// 從模形數(shù)組中取出相應的模形

let item = shopitems[indexPath.item];

// 設置模形數(shù)據(jù)為顯示縮略圖模式

item.showBigImage = false

// 把模形數(shù)據(jù)賦值給cell,由cell去決定怎樣顯示,顯示什么內容

cell?.item = item

// 當滑動到到最后一個cell時請求加載30個數(shù)據(jù)

if indexPath.item == shopitems.count - 1 {

loadMoreHomePageData(shopitems.count)

}

return cell!

}

這里為使Demo不過于復雜,沒有用什么"上拉加載更多"控件,每次滑動到到最后一個cell時請求加載30個數(shù)據(jù)方式同樣能獲得良好的滑動體驗


override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

// 當點擊某個cell時, 創(chuàng)建大圖瀏覽控制器

let photoVC = PhotoBrowseCollectionVC()

// 當前點擊cell的indexPathw傳給控制器,以使大圖瀏覽器直接顯示對應圖片

photoVC.indexPath = indexPath

// 當前模型數(shù)組的內容傳給控制器,以使大圖瀏覽能左右滑動

photoVC.items = shopitems

// 先以正常形式modal出大圖瀏覽

presentViewController(photoVC, animated: true, completion: nil)

}

這里先以正常的樣式(從底部彈出)modal出大圖瀏覽視圖,當縮略圖和大圖的邏輯跳轉邏輯完成后再來完善畫動邏輯

大圖瀏覽的實現(xiàn)

與縮略圖一樣,大圖瀏覽也是一個collectionView.這里為大圖瀏覽控制器添加了一個便利構造器,以便在點擊縮略圖時快速創(chuàng)建固定流水布局的collectionView.


convenience init() {

let layout = UICollectionViewFlowLayout()

layout.itemSize = CGSize(width: UIScreen.mainScreen().bounds.width + cellMargin, height: UIScreen.mainScreen().bounds.height)

layout.minimumLineSpacing = 0

layout.minimumInteritemSpacing = 0

layout.scrollDirection = .Horizontal

self.init(collectionViewLayout: layout)

}

Demo 結構分析一節(jié)中遺留了一個問題,其實要實現(xiàn)全屏圖像間隔效果非常簡單,只要把collectionView和cell的寬設置為屏寬加固定的間距并且cell之間間距為0

而圖片只顯示在屏幕正中間(圖片與屏等寬),這樣在開啟pagingEnabled的情況下每次滑動都是滑動一個(圖片寬度+間距),相當于在cell中留了一個邊距來作間隔而不是在cell

外做間隔,可以參看下圖

上圖中有兩個cell,cell的間距是零.開啟pagingEnabled時,每次移動都是一個cell的寬,這樣停止滑動時間隔就不會出現(xiàn)在屏幕中了.

大圖瀏覽的collectionView的實現(xiàn)代碼幾乎與縮略圖一樣,需要注意的是當modal出大圖的時候collectionView是要直接顯示對應大圖的,這也是為什么在縮略視圖控制器的didSelectItemAtIndexPath函數(shù)中要傳遞indexPath的原因.


override func viewDidLoad() {

super.viewDidLoad()

// 大圖colletionview的frame

collectionView?.frame = UIScreen.mainScreen().bounds

collectionView?.frame.size.width = UIScreen.mainScreen().bounds.size.width + cellMargin

// 開啟分頁

collectionView?.pagingEnabled = true

// 注冊重用cell

collectionView?.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: cellID)

// collectionView顯示時跳轉到應的圖片

collectionView?.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .Left, animated: false)

}

上面代碼中scrollToItemAtIndexPath函數(shù)的atScrollPosition參數(shù)的意思是停止?jié)L動時對應的cell與collectionView的位置關系,Left是cell的左邊與colletionview的

左邊對齊.其它的對應關系可依此類推就不廢話了. collectionView的比較重要代理函數(shù)的實現(xiàn)如下


override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as! CollectionViewCell

let item = items![indexPath.item]

item.showBigImage = true

cell.item = item

return cell

}

override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

dismissViewControllerAnimated(true, completion: nil)

}

說重要是因為要與縮略圖控制器的代理函數(shù)對比看,cellForItemAtIndexPath只是常規(guī)的設置數(shù)據(jù),選中cell直接dismiss當前控制器.

至此縮略圖和大圖的跳轉邏輯你已經(jīng)清楚了,下面的部分才本博文要講的真正內容.其實上面分析那么多廢話也是因為present和dismiss的動畫與跳轉前后兩個控制器有密切關系

modal出一個View的原理

默認從底部彈出view的modal方式是將要顯式的view添加到一個容器view中,然后對容器view添加動畫效,動畫結束后把跳轉之前控制器的view從window中移除.在window中之前

的view完全被彈出的view替代最終看到如下圖的視圖結構

modal視圖結構

如你在上圖中看到的,黑色的是window,藍色的為彈出的View,而中間的就是容器View.容器view的類型是UITransitionView

dismiss的過程是present的逆過程,除了從底部彈出的動畫UIKit還提供了多種動畫效果可以通過設置彈出控制器modalTransitionStyle屬性.

這里有個需要注意點,當設置modalPresentationStyle為Custom時原控制器的view并不會從window中移除.同時如果設置了transitioningDelegate

那么modalTransitionStyle設置的動畫效果將全部失效,此時動畫全權交給代理來完成. UIViewControllerTransitioningDelegate協(xié)議包含五個函數(shù)

這里只需要關注Getting the Transition Animator Objects的兩個函數(shù),這兩個函數(shù)都需要返回一個實現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議的實例對象,

具體的動畫邏輯將在這個實例對象的方法中完成.

添加點擊跳轉到大圖瀏覽動畫

按上一節(jié)的分析需要在點擊縮略圖時把大圖控制器的modalPresentationStyle設為.Custom,并且過渡動畫(transitioningDelegate)設置代理對象,具體代碼如下


override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

let photoVC = PhotoBrowseCollectionVC()

photoVC.indexPath = indexPath

photoVC.items = shopitems

photoVC.transitioningDelegate = modalDelegate

photoVC.modalPresentationStyle = .Custom

presentViewController(photoVC, animated: true, completion: nil)

}

modalDelegate是ModalAnimationDelegate的實例對象,其實現(xiàn)了UIViewControllerTransitioningDelegate協(xié)議方法,animationControllerForPresentedController

返回本身的實例對象,所以ModalAnimationDelegate也要實現(xiàn)UIViewControllerAnimatedTransitioning協(xié)議方法.


func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

return self

}

現(xiàn)在具體的動畫邏輯就轉到了UIViewControllerAnimatedTransitioning協(xié)議的animateTransition方法中.要實現(xiàn)從選中的圖片慢慢放大的效果分成如下幾步

  • 取出容器view,也就是上一節(jié)提到的UITransitionView實例對象

  • 取出要彈出的目標view,在這里就是展示大圖的colletionview,并添加到容器view

  • 新建UIImageView對象,得到選中的UIImage對像,及其在window上的frame

  • 把新建的UIImageView對象添加到容器view

  • 設置新建UIImageView的放大動畫,動畫結果束后從容器view中移除

  • 通知系統(tǒng)動畫完成(主動調用completeTransition)

把動畫的實現(xiàn)分解開來是不是清晰很多了,具體實現(xiàn)還是得參看代碼


func presentViewAnimation(transitionContext: UIViewControllerContextTransitioning) {

// 目標view

let destinationView = transitionContext.viewForKey(UITransitionContextToViewKey)

// 容器view

let containerView = transitionContext.containerView()

guard let _ = destinationView else {

return

}

// 目標view添加到容器view上

containerView?.addSubview(destinationView!)

// 獲取目標控制器

let destinationController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as? PhotoBrowseCollectionVC

let indexPath = destinationController?.indexPath

// 跳轉前的控制器

let collectionViewController = ((transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)) as! UINavigationController).topViewController as! UICollectionViewController

let currentCollectionView = collectionViewController.collectionView

// 當前選中的cell

let selectctedCell = currentCollectionView?.cellForItemAtIndexPath(indexPath!) as? CollectionViewCell

// 新建一個imageview添加到目標view之上,做為動畫view

let annimateViwe = UIImageView()

annimateViwe.image = selectctedCell?.imageView.image

annimateViwe.contentMode = .ScaleAspectFill

annimateViwe.clipsToBounds = true

// 被選中的cell到目標view上的座標轉換

let originFrame = currentCollectionView!.convertRect(selectctedCell!.frame, toView: UIApplication.sharedApplication().keyWindow)

annimateViwe.frame = originFrame

containerView?.addSubview(annimateViwe)

let endFrame = coverImageFrameToFullScreenFrame(selectctedCell?.imageView.image)

destinationView?.alpha = 0

// 過渡動畫執(zhí)行

UIView.animateWithDuration(1, animations: {

annimateViwe.frame = endFrame

}) { (finished) in

transitionContext.completeTransition(true)

UIView.animateWithDuration(0.5, animations: {

destinationView?.alpha = 1

}) { (_) in

annimateViwe.removeFromSuperview()

}

}

}

這里的關鍵是怎樣通過transitionContext拿到兩個控制器.通過UITransitionContextFromViewControllerKey拿到的是轉跳前控制器的父控制器,由于Demo中縮略圖控制器內嵌了導航控制器所以在Demo中拿到就是導航控制器,經(jīng)過一系列的轉換才能拿到選中的圖片.拿到選中的圖片后需要計算動畫開始和結束的frame,開始的frame是將選中的cell座標直接轉換到window上

結束的frame是UIImageView放大到屏寬并居中的frame,具體計算方法參看Demo的coverImageFrameToFullScreenFrame全局函數(shù).

另外UIViewControllerAnimatedTransitioning協(xié)議另一個必須要實現(xiàn)的函數(shù)是transitionDuration,這個函數(shù)決定了動畫執(zhí)行的時長.


func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {

return 1.0

}

添加輕擊回到小圖瀏覽動畫

輕擊dismiss的過程與上一節(jié)彈出正好相反,但仍有所區(qū)別.過程如下:

  • 取出彈出的大圖colletionview,得到當前輕擊的圖片

  • 新建UIImageView作為動畫view,并把上一步得到的image給新建UIImageView

  • 得到選中圖片在window上的frame,并設置為新建UIImageView動畫的開始frame

  • 得到當前輕擊的大圖對應的縮略圖的frame,并將其做為動畫結束frame

  • 執(zhí)行動畫,動畫結束后移除UIImageView

  • 通知系統(tǒng)動畫完成(主動調用completeTransition)

與present過程不同的是UITransitionContextFromViewControllerKey和UITransitionContextToViewControllerKey兩個key正好相反,present過程的FromVC是縮略圖的父控制器,toTV是大圖瀏覽控制器.而dismiss與present是相反的.


func dismissViewAnimation(transitionContext: UIViewControllerContextTransitioning) {

let transitionView = transitionContext.viewForKey(UITransitionContextFromViewKey)

let contentView = transitionContext.containerView()

// 取出modal出的來控制器

let destinationController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! UICollectionViewController

// 取出當前顯示的collectionview

let presentView = destinationController.collectionView

// 取出控制器當前顯示的cell

let dismissCell = presentView?.visibleCells().first as? CollectionViewCell

// 新建過渡動畫imageview

let animateImageView = UIImageView()

animateImageView.contentMode = .ScaleAspectFill

animateImageView.clipsToBounds = true

// 獲取當前顯示的cell的image

animateImageView.image = dismissCell?.imageView.image

// 獲取當前顯示cell在window中的frame

animateImageView.frame = (dismissCell?.imageView.frame)!

contentView?.addSubview(animateImageView)

// 縮略圖對應的indexPath

let indexPath = presentView?.indexPathForCell(dismissCell!)

// 取出要返回的控制器view

let originView = ((transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as! UINavigationController).topViewController as! UICollectionViewController).collectionView

var originCell = originView!.cellForItemAtIndexPath(indexPath!)

// 得到返回后對應cell在window上的frame

let originFrame = originView?.convertRect(originCell!.frame, toView: UIApplication.sharedApplication().keyWindow)

UIView.animateWithDuration(1, animations: {

animateImageView.frame = originFrame!

transitionView?.alpha = 0

}) { (_) in

animateImageView.removeFromSuperview()

transitionContext.completeTransition(true)

}

}

present和dismiss時都會調用到UIViewControllerAnimatedTransitioning協(xié)議的animateTransition方法,為區(qū)分dismiss和present的動畫,定義一個屬性isPresentAnimationing表明當前要執(zhí)行的是dismiss還是present,而當前執(zhí)行的動畫是由UIViewControllerTransitioningDelegate協(xié)議的animationControllerForPresentedController和animationControllerForDismissedController兩個函數(shù)決定的.


func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

isPresentAnimationing = true

return self

}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

isPresentAnimationing = false

return self

}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

isPresentAnimationing ? presentViewAnimation(transitionContext) : dismissViewAnimation(transitionContext)

}

要注意的問題

其實上在dismiss動畫邏輯留下了一個坑,dismiss時需要獲取對應縮略圖的cell進而得到動畫結束的frame,而獲取這個cell用了cellForItemAtIndexPath方法


...

let originView = ((transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as! UINavigationController).topViewController as! UICollectionViewController).collectionView

var originCell = originView!.cellForItemAtIndexPath(indexPath!)

// 得到返回后對應cell在window上的frame

let originFrame = originView?.convertRect(originCell!.frame, toView: UIApplication.sharedApplication().keyWindow)

...

而cellForItemAtIndexPath只能返回正在顯示的cell,沒有被顯示的cell將返回nil.所以當大圖對應的縮略圖沒有被顯示在colletionview中時強制解包就會拋出異常.也就是說當選擇查看當前顯示縮略圖的最后一張對應的大圖時就會閃退.解決的辦是若用cellForItemAtIndexPath取不到cell則將應的cell滾動到可視范圍內,由于cellForItemAtIndexPath需要下一個顯示周期才能顯示所以要主動調用layoutIfNeeded,實現(xiàn)如下


var originCell = originView!.cellForItemAtIndexPath(indexPath!)

if originCell == nil {

originView?.scrollToItemAtIndexPath(indexPath!, atScrollPosition: .CenteredVertically, animated: false)

originView?.layoutIfNeeded()

}

originCell = originView!.cellForItemAtIndexPath(indexPath!)

let originFrame = originView?.convertRect(originCell!.frame, toView: UIApplication.sharedApplication().keyWindow)

...

總結

上面啰啰嗦嗦寫了很多我認為是廢話的話,其實實現(xiàn)類似微信微博的圖片瀏覽動畫的核心在于dismissViewAnimation和presentViewAnimation函數(shù).本文只是通過一個簡單的demo實現(xiàn)了相同的效果,為大家在自己項目中實現(xiàn)類似效果提供一個可參考的思路.當然本人水平有限,或許你知道更簡單有效的方法希望也告知我.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末恭应,一起剝皮案震驚了整個濱河市桌粉,隨后出現(xiàn)的幾起案子突委,更是在濱河造成了極大的恐慌拇勃,老刑警劉巖倦青,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卷员,死亡現(xiàn)場離奇詭異刨疼,居然都是意外死亡,警方通過查閱死者的電腦和手機雷酪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涝婉,“玉大人哥力,你說我怎么就攤上這事《胀洌” “怎么了吩跋?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渔工。 經(jīng)常有香客問我锌钮,道長,這世上最難降的妖魔是什么引矩? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任梁丘,我火速辦了婚禮,結果婚禮上旺韭,老公的妹妹穿的比我還像新娘氛谜。我一直安慰自己,他們只是感情好茂翔,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布混蔼。 她就那樣靜靜地躺著履腋,像睡著了一般珊燎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遵湖,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天悔政,我揣著相機與錄音,去河邊找鬼延旧。 笑死谋国,一個胖子當著我的面吹牛,可吹牛的內容都是我干的迁沫。 我是一名探鬼主播芦瘾,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼集畅!你這毒婦竟也來了近弟?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挺智,失蹤者是張志新(化名)和其女友劉穎祷愉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡二鳄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年赴涵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片订讼。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡髓窜,死狀恐怖,靈堂內的尸體忽然破棺而出躯嫉,到底是詐尸還是另有隱情纱烘,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布祈餐,位于F島的核電站擂啥,受9級特大地震影響,放射性物質發(fā)生泄漏帆阳。R本人自食惡果不足惜哺壶,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜒谤。 院中可真熱鬧山宾,春花似錦、人聲如沸鳍徽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阶祭。三九已至绷杜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間濒募,已是汗流浹背鞭盟。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瑰剃,地道東北人齿诉。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像晌姚,于是被迫代替她去往敵國和親粤剧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內容

  • 緣起 那時挥唠,我想要一個這樣的圖片瀏覽器: 從小圖進入大圖瀏覽時抵恋,使用轉場動畫 可加載網(wǎng)絡圖片,且過渡自然猛遍,不阻塞操...
    囧書閱讀 7,024評論 35 107
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫馋记、插件号坡、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,124評論 4 61
  • 好久不見的一個同校朋友宽堆,那天晚上過來我宿舍拿東西。坐在床邊我們就聊了起來茸习,說著說著就說起了她對象的話題了畜隶,她瞬間就...
    李籽兒閱讀 185評論 0 0
  • 又是一年春節(jié),今年回家的任然是獨身一人号胚,父母多 次催我該找女朋友了籽慢,我總是無言以對,其實不是自己不想找猫胁,只是沒有很...
    兔毛閱讀 369評論 1 1
  • 定義 歸并排序(英語:Merge sort)箱亿,是創(chuàng)建在歸并操作上的一種有效的排序算法,效率為O(n log n)弃秆。...
    craneyuan閱讀 524評論 0 9