10-下拉刷新 & 上拉加載

下拉刷新 & 上拉加載

課程目標(biāo)

  • KVO的使用
  • UIScrollView使用

接口準備

  • 新浪微博下拉刷新與上拉加載需要有兩個重要的參數(shù)
參數(shù)名 說明
since_id 返回ID比since_id大的微博(即比since_id時間晚的微博)
max_id 返回ID小于或等于max_id的微博

以上可知:

  1. 如果傳入since_id说墨,服務(wù)器會返回ID比since_id大的微博(即比since_id時間晚的微博)庸队,也就是最新的微博,所以這個參數(shù)可以用于下拉刷新.
  2. 傳入max_id茬缩,服務(wù)器會返回ID小于或等于max_id的微博,id 越小時間越早,所以可以用作上拉加載资锰。(特別注意:會返回ID小于或等于)
  • 更改微博數(shù)據(jù)加載的方法-> HMStatusListViewModelloadStatuses 方法添加參數(shù)
/// 加載微博數(shù)據(jù)的方法
func loadData(isPullUp isPullUp: Bool, completion: (isSuccessed: Bool)->()) {
    // 定義 url 與參數(shù)
    let urlString = "https://api.weibo.com/2/statuses/friends_timeline.json"

    let since_id = isPullUp ? 0 : (statuses?.first?.status?.id ?? 0)
    let max_id = isPullUp ? (statuses?.last?.status?.id ?? 0) : 0

    let params = [
        "access_token": HMUserAccountViewModel.sharedUserAccount.accessToken!,
        "since_id": since_id,
        "max_id": max_id
    ]
    ...
}

上拉加載

實現(xiàn)效果與思路

  • 當(dāng)用戶滾動到底部的時候首昔,自動去加載更多數(shù)據(jù)
  • 可以在加載當(dāng)前頁面最后一個 cell 的時候去執(zhí)行加載更多數(shù)據(jù)的方法
  • 給 tableView 添加一個 footerView(上拉顯示控件)橱健,用作拉到最底部的友好顯示

代碼實現(xiàn)

  • 懶加載底部上拉顯示控件
// 上拉加載控件
private lazy var pullupView: UIActivityIndicatorView = {
    let indicator = UIActivityIndicatorView()
    indicator.activityIndicatorViewStyle = .WhiteLarge
    indicator.color = UIColor.darkGrayColor()
    return indicator;
}()
  • 設(shè)置成 tableView 的footerView
// 設(shè)置上拉加載控件
tableView.tableFooterView = pullupView

運行測試,看不見任何東西沙廉【械矗看不見控件的原因就是 UIActivityIndicatorView 控件默認不執(zhí)行動畫是看不見的

  • 開啟執(zhí)行動畫
pullupView.startAnimating()

運行測試,已經(jīng)可以看到撬陵,但是位置沒有留出來珊皿,執(zhí)行 sizeToFit 方法

  • 在將要加載最后一個 cell 的時候去加載更多數(shù)據(jù)
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if indexPath.row == statusListViewModel.statuses!.count - 1 && pullupView.isAnimating() == false {
        // 加載更多
        pullupView.startAnimating()
        loadData()
    }
}

注意:需要在判斷里面多加一個條件,就是底部控件沒有執(zhí)行動畫的時候才去加載更多數(shù)據(jù)巨税,防止重復(fù)加載

  • 更改 loadData() 方法邏輯
// MARK: - 加載數(shù)據(jù)
private func loadData(){
    statusListViewModel.loadData(isPullUp: pullupView.isAnimating()) { (isSuccessed, count) -> () in
        if isSuccessed {
            self.tableView.reloadData()
        }
    }
}

  • 更改 HMStatusListViewModelloadData 方法 -> 上拉加載與下拉刷新數(shù)據(jù)添加的位置不一樣
if let array = res["statuses"] as? [[String: AnyObject]] {
    // 如果是字典
    // 判斷數(shù)組是否為 nil
    if self.statuses == nil {
        self.statuses = [HMStatusViewModel]()
    }

    // 定義一個臨時數(shù)組
    var tempStatuses = [HMStatusViewModel]()

    // 字典轉(zhuǎn)模型
    for dic in array {
        tempStatuses.append(HMStatusViewModel(status: HMStatus(dictionary: dic)))
    }

    if isPullUp {
        // 代表是上拉加載蟋定,拼裝數(shù)據(jù)到集合后面
        self.statuses! += tempArray
    }else{
        // 代表是下拉刷新,拼裝數(shù)據(jù)到前面
        self.statuses! = tempArray + self.statuses!
    }
}

...

運行測試:發(fā)現(xiàn)只加載一次數(shù)據(jù)草添,下次再拖動就不去加載了驶兜,原因是加載完畢之后 pullupView 也一直在執(zhí)行動畫,下次就進入不到加載更多的判斷邏輯里面去了远寸,所以加載完畢需要將 pullupView 結(jié)束動畫

  • 結(jié)束動畫
/// 結(jié)束刷新
private func endRefresh(){
    pullupView.stopAnimating()
}

/// 在數(shù)據(jù)請求成功抄淑,或者數(shù)據(jù)請求失敗之后調(diào)用此方法
statusListViewModel.loadData(isPullUp: pullupView.isAnimating()) { (isSuccessed, count) -> () in
    if isSuccessed {
        self.tableView.reloadData()
    }
    self.endRefresh()
}

運行測試

下拉刷新

實現(xiàn)效果

  • 拖動 tableView,頂部顯示 下拉刷新驰后,箭頭朝下
  • 拖動到一定程度的時候肆资,頂部顯示 釋放更新,箭頭朝上
  • 松手:
    • 到達一定程度松手灶芝,頂部顯示 加載中…郑原,隱藏箭頭,顯示菊花轉(zhuǎn)
    • 未到達一定程度夜涕,直接回到最初狀態(tài)

頂部的整個 View 會隨著 tableView 的拖動而移動

示意圖

下拉刷新示意.png.jpeg

實現(xiàn)思路

  • 給 tableView 添加一個自定義刷新控件(HMRefreshControl)
  • 這個刷新控件的 y 值是 自己的高度犯犁,以讓其放在 tableView 的頂部以及可以跟隨 tableView 滑動
  • 在刷新控件內(nèi)部監(jiān)聽 tableView 的滑動
  • 當(dāng)滑動到某種程度去改變子控件要顯示的邏輯
  • 當(dāng)用戶松開手要刷新的時候,可以調(diào)整 tableView 的 contentInsettop 值以讓刷新控件顯示出來
  • 在刷新的時候調(diào)用外部提供的方法執(zhí)行刷新的邏輯

實現(xiàn)代碼

  • 自定義 HMRefreshControl
class HMRefreshControl: UIControl {

    override init(frame: CGRect) {
        super.init(frame: frame)

        setupUI()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupUI() {
        // 先設(shè)置默認寬度與高度
        self.frame.size.width = SCREENW
        self.frame.size.height = 44

        backgroundColor = RandomColor()
    }
}
  • 定義懶加載控件 & 添加到首頁的 tableView 中去
// 下拉刷新控件
private lazy var hmRefreshControl: HMRefreshControl = HMRefreshControl()

...

// 添加頭部視圖
tableView.addSubview(hmRefreshControl)

運行測試

  • 抽取控件高度常量
private let HMRefreshControlH: CGFloat = 44
  • 更改 Y 值
private func setupUI() {
    // 先設(shè)置默認寬度與高度
    self.frame.size.width = SCREENW
    self.frame.size.height = HMRefreshControlH
    self.frame.origin.y = -HMRefreshControlH

    backgroundColor = RandomColor()
}
  • 定義 scrollView 屬性
// 定義 scrollView女器,用于記錄當(dāng)前控件添加到哪一個 View 上的
var scrollView: UIScrollView?
  • HMRefreshView 中監(jiān)聽其添加到 tableView 的滾動
/// 當(dāng)前 view 的父視圖即將改變的時候會調(diào)用酸役,可以在這個方法里面拿到父控件
override func willMoveToSuperview(newSuperview: UIView?) {
    super.willMoveToSuperview(newSuperview)
    // 如果父控件不為空,并且父控件是UIScrollView
    if let scrollView = newSuperview where scrollView.isKindOfClass(NSClassFromString("UIScrollView")!) {
        scrollView.addObserver(self, forKeyPath: "contentOffset", options: NSKeyValueObservingOptions.New, context: nil)
        // 記錄當(dāng)前 scrollView,以便在 `deinit` 方法里面移除監(jiān)聽
        self.scrollView = scrollView as? UIScrollView
    }
}

/// 當(dāng)值改變之后回調(diào)的方法
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    printLog(change)
}

deinit{
    // 移除監(jiān)聽
    if let scrollView = self.scrollView {
        scrollView.removeObserver(self, forKeyPath: "contentOffset")
    }
}

注意:監(jiān)聽之后需要做兩件事情:a.在合適的時候移除監(jiān)聽;b.一定要實現(xiàn)值改變之后的回調(diào)方法

  • 根據(jù)滾動簇捍,計算出 refreshView 完全展示出現(xiàn)的臨界點值
// 取到頂部增加的可滑動的距離
let contentInsetTop = self.scrollView!.contentInset.top
// 取到當(dāng)前 scrollView 的偏移 Y
let contentOffsetY = self.scrollView!.contentOffset.y

// printLog("contentInsetTop=\(contentInsetTop);contentOffsetY=\(contentOffsetY)")

// 通過分析可知:contentOffsetY 如果小于 (-contentInsetTop - 當(dāng)前 View 高度)只壳,就代表當(dāng)前 View 完全顯示出來
// 而 (-contentInsetTop - 當(dāng)前 View 高度) 這個值就代表臨界值

// 臨界值
let criticalValue = -contentInsetTop - self.height

// 在用戶拖動的時候去判斷臨界值
if scrollView!.dragging {
    if contentOffsetY < criticalValue {
        printLog("完全顯示出來啦")
    }else {
        printLog("沒有完全顯示出來/沒有顯示出來")
    }
}

  • 根據(jù)以上狀態(tài)添加 state 枚舉
enum HMRefreshControlStatus: Int {
    case Normal = 0 // 默認狀態(tài)
    case Pulling = 1 // 松手就可以刷新的狀態(tài)
    case Refreshing = 2 // 正在刷新的狀態(tài)
}

  • 定義 state 屬性

  • 根據(jù)滑動的位置設(shè)置當(dāng)前 View 的狀態(tài)
// 在用戶拖動的時候去判斷臨界值
if scrollView!.dragging {
    if contentOffsetY < criticalValue {
        printLog("完全顯示出來啦")
        self.status = .Pulling
    }else {
        printLog("沒有完全顯示出來/沒有顯示出來")
        self.status = .Normal
    }
}
  • 添加子控件 (箭頭,提示文字label)
// MARK: - 懶加載控件
// 箭頭圖標(biāo)
private lazy var arrowIcon: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
// 顯示文字的label
private lazy var messageLabel: UILabel = {
    let label = UILabel()
    label.text = "下拉刷新"
    label.textColor = UIColor.grayColor()
    label.font = UIFont.systemFontOfSize(12)
    return label
}()

...

// 添加子控件

private func setupUI(){
    ...

    // 添加控件
    addSubview(arrowIcon)
    addSubview(messageLabel)

    // 添加約束
    arrowIcon.snp_makeConstraints { (make) -> Void in
        make.centerX.equalTo(self.snp_centerX).offset(-30)
        make.centerY.equalTo(self.snp_centerY)
    }
    messageLabel.snp_makeConstraints { (make) -> Void in
        make.leading.equalTo(arrowIcon.snp_trailing)
        make.centerY.equalTo(arrowIcon.snp_centerY)
    }
}
  • 設(shè)置不同狀態(tài)下執(zhí)行不同的動畫
// 定義當(dāng)前控件的刷新狀態(tài)
var status: HMRefreshControlStatus = .Normal {
    didSet{
        switch status {
        case .Pulling:
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                self.arrowIcon.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
            })
            messageLabel.text = "釋放更新"
        case .Normal:
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                self.arrowIcon.transform = CGAffineTransformIdentity
            })
            messageLabel.text = "下拉刷新"
        default:
            break
        }
    }
}

運行測試

  • 監(jiān)聽用戶松手進入刷新狀態(tài)暑塑,滿足兩個條件
    • 用戶松手
    • 當(dāng)前狀態(tài)是 Pulling 狀態(tài) (可以進入刷新的狀態(tài))
// 在用戶拖動的時候去判斷臨界值
    if scrollView!.dragging {
        if contentOffsetY < criticalValue {
            printLog("完全顯示出來啦")
            self.state = .Pulling
        }else {
            printLog("沒有完全顯示出來/沒有顯示出來")
            self.state = .Normal
        }
    }else{
        // 判斷如果用戶已經(jīng)松手吼句,并且當(dāng)前狀態(tài)是.Pulling,那么進入到 .Refreshing 狀態(tài)
        if self.status == .Pulling {
            print("進入刷新狀態(tài)")
            self.status = .Refreshing
        }
    }
  • 顯示刷新狀態(tài)的效果
// 1.懶加載控件
// 菊花轉(zhuǎn)
private lazy var indecator: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)

// 2.添加控件 & 設(shè)置約束

addSubview(indecator)
indecator.snp_makeConstraints { (make) -> Void in
    make.center.equalTo(arrowIcon.snp_center)
}


// 3.在 state 為 Refreshing 狀態(tài)時顯示效果
case .Refreshing:   // 顯示刷新的效果
    // 添加頂部可以多滑動的距離
    UIView.animateWithDuration(0.25, animations: { () -> Void in
        var contentInset = self.scrollView!.contentInset
        contentInset.top += self.frame.height
        self.scrollView?.contentInset = contentInset
    })

    // 隱藏箭頭
    arrowIcon.hidden = true
    // 開始菊花轉(zhuǎn)
    indecator.startAnimating()
    // 顯示 `加載中…`
    messageLabel.text = "加載中…"
  • 默認狀態(tài)下顯示箭頭事格,隱藏菊花轉(zhuǎn)
case .Normal:       // 置為默認的狀態(tài)的效果
    UIView.animateWithDuration(0.25, animations: { () -> Void in
        self.arrowIcon.transform = CGAffineTransformIdentity
    })
    messageLabel.text = "下拉刷新"
    arrowIcon.hidden = false
    indecator.stopAnimating()

運行:測試發(fā)現(xiàn)當(dāng)松手刷新的時候惕艳,顯示的效果能出來,但是當(dāng)一滑動的時候狀態(tài)就發(fā)會了改變驹愚,而 Refreshing 的狀態(tài)改變是由數(shù)據(jù)刷新完成之后去重置远搪,所以更改滑動時候的判斷邏輯

  • 更改滑動時的判斷邏輯,以防止正在刷新中的時候的狀態(tài)異常改變
// 在用戶拖動的時候去判斷臨界值
if scrollView!.dragging {
    if state == .Normal && contentOffsetY < criticalValue {
        printLog("完全顯示出來啦")
        self.status = .Pulling
    }else if status == .Pulling && contentOffsetY >= criticalValue {
        printLog("沒有完全顯示出來/沒有顯示出來")
        self.status = .Normal
    }
}else{
    // 判斷如果用戶已經(jīng)松手逢捺,并且當(dāng)前狀態(tài)是.Pulling谁鳍,那么進入到 .Refreshing 狀態(tài)
    if self.status == .Pulling {
        self.status = .Refreshing
    }
}
  • 模擬 5 秒后結(jié)束刷新
UIView.animateWithDuration(0.25, animations: { () -> Void in
        var contentInset = self.scrollView!.contentInset
        contentInset.top += self.height
        self.scrollView?.contentInset = contentInset
    }, completion: { (finish) -> Void in

        // 模似 5 秒之后約束刷新
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
            // 設(shè)置狀態(tài)為 默認狀態(tài)
            self.status = .Normal
            // 重置contentInsetTop
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                var contentInset = self.scrollView!.contentInset
                contentInset.top -= self.frame.height
                self.scrollView?.contentInset = contentInset
            })
        }
    })

運行測試

  • 添加要執(zhí)行刷新的方法
hmRefreshControl.addTarget(self, action: "loadData", forControlEvents: UIControlEvents.ValueChanged)
  • 在刷新的時候執(zhí)行方法
case .Refreshing:   // 顯示刷新的效果
    ...
    // 調(diào)用刷新的方法
    sendActionsForControlEvents(.ValueChanged)
  • 去掉上面模擬5秒結(jié)束刷新的邏輯,添加結(jié)束刷新方法
func endRefreshing(){
    // 重置contentInsetTop
    UIView.animateWithDuration(0.25, animations: { () -> Void in
        var contentInset = self.scrollView!.contentInset
        contentInset.top -= self.frame.height
        self.scrollView?.contentInset = contentInset
    })
    // 設(shè)置狀態(tài)為 默認狀態(tài)
    self.status = .Normal
}
  • 在刷新完畢之后調(diào)用 HMRefreshControlendRefreshing() 方法
/// 結(jié)束刷新
private func endRefresh(){
    pullupView.stopAnimating()
    hmRefreshControl.endRefreshing()
}

運行測試:第一次啟動的時候劫瞳,刷新完畢倘潜,出現(xiàn)contentInset.top值遞減問題,所以要判斷如果之前狀態(tài)是刷新狀態(tài)志于,結(jié)束刷新才去更改contentInset.top

  • 增加保存上一次狀態(tài)的邏輯

// 定義舊狀態(tài)屬性涮因,保存上一次狀態(tài)
var oldStatus: HMRefreshControlStatus?

// 在 `state` 的 `didSet` 方法末尾記錄狀態(tài)
// 定義當(dāng)前控件的刷新狀態(tài)
var status: HMRefreshState = .Normal {
    didSet{
        switch status {
        case .Pulling:      // 松手就可以刷新的狀態(tài)
            ...
        case .Normal:       // 置為默認的狀態(tài)的效果
            ...
        case .Refreshing:   // 顯示刷新的效果
            ...
        }
        // 記錄本次狀態(tài)
        oldStatus = status
    }
}
  • 在結(jié)束刷新的時候判斷如果是從刷新狀態(tài)進入到默認狀態(tài)就遞減contentInset.top
/// 結(jié)束刷新
func endRefreshing(){
    if oldStatus == .Refreshing {
        // 重置contentInsetTop
        UIView.animateWithDuration(0.25, animations: { () -> Void in
            var contentInset = self.scrollView!.contentInset
            contentInset.top -= self.frame.height
            self.scrollView?.contentInset = contentInset
        })
    }
    // 設(shè)置狀態(tài)為 默認狀態(tài)
    self.state = .Normal
}
  • 部分代碼抽取
// 把結(jié)束刷新的邏輯,移動到 state 的 didSet 的 case .Normal 中
switch state {
    case .Pulling:      // 松手就可以刷新的狀態(tài)
        ...
    case .Normal:       // 置為默認的狀態(tài)的效果
        ...
        // 如果之前狀態(tài)是刷新狀態(tài)伺绽,需要遞減 contentInset.top
        if oldStatus == .Refreshing {
            // 重置contentInsetTop
            UIView.animateWithDuration(0.25, animations: { () -> Void in
                var contentInset = self.scrollView!.contentInset
                contentInset.top -= self.frame.height
                self.scrollView?.contentInset = contentInset
            })
        }
    case .Refreshing:   // 顯示刷新的效果
        ...
    }
...

// 抽取之后的方法
func endRefreshing(){
    // 設(shè)置狀態(tài)為 默認狀態(tài)
    self.state = .Normal
}

運行測試

下拉刷新提示

  • 修改 loadStatuses 养泡,回調(diào)加載成功數(shù)據(jù)條數(shù)
/// 加載微博數(shù)據(jù)的方法
    func loadData(isPullUp isPullUp: Bool, completion: (isSuccessed: Bool, count: Int)->()) {
}
  • 懶加載提示控件
/// 下拉刷新提示的label
// 提示控件
private lazy var pullDownTipLabel: UILabel = {
    let label = UILabel(textColor: UIColor.whiteColor(), fontSize: 12)
    // 設(shè)置文字居中、背景顏色
    label.textAlignment = NSTextAlignment.Center
    label.backgroundColor = UIColor.orangeColor()

    // 設(shè)置大小
    label.frame.size = CGSizeMake(SCREENW, 35)
    return label
}()
  • 增加 showPullDownTips 方法奈应,測試添加位置
/// 顯示下拉刷新提示
private func showPullDownTips(count: Int){
    pullDownTipLabel.y = 35
    navigationController?.view.insertSubview(pullDownTipLabel, belowSubview: navigationController!.navigationBar)
}
  • 在下拉刷新完成之后調(diào)用此方法
@objc private func loadData(){
    statusListViewModel.loadData(isPullUp: pullupView.isAnimating()) { (isSuccessed, count) -> () in
        if isSuccessed {
            self.tableView.reloadData()
        }
        if self.pullupView.isAnimating() == false {
            self.showPullDownTips(count)
        }
        self.endRefresh()
    }
}
  • 更改懶加載代碼
/// 下拉刷新提示的label
private lazy var pullDownTipLabel: UILabel = {
    let label = UILabel()

    // 設(shè)置文字顏色澜掩、文字大小、居中钥组、背景顏色
    label.textColor = UIColor.whiteColor()
    label.font = UIFont.systemFontOfSize(12)
    label.textAlignment = NSTextAlignment.Center
    label.backgroundColor = UIColor.orangeColor()

    // 設(shè)置大小
    label.size = CGSizeMake(SCREENW, 35)

    // 默認是隱藏狀態(tài)
    label.hidden = true
    // 添加控件
    if let navigationController = self.navigationController {
        navigationController.view.insertSubview(label, belowSubview: navigationController.navigationBar)
    }
    return label
}()
  • 完成顯示邏輯
/// 顯示下拉刷新提示
private func showPullDownTips(count: Int){

    // 如果當(dāng)前控件處于顯示狀態(tài)输硝,直接返回
    if !pullDownTipLabel.hidden {
        return
    }
    /// 提示文字信息
    let tipStr = count==0 ? "沒有微博數(shù)據(jù)": "\(count)條新微博"
    let height = pullDownTipLabel.frame.height
    pullDownTipLabel.frame.origin.y = CGRectGetMaxY(self.navigationController!.navigationBar.frame) - height
    // 設(shè)置文字并將其顯示
    pullDownTipLabel.text = tipStr
    pullDownTipLabel.hidden = false
    //執(zhí)行動畫
    UIView.animateWithDuration(1, animations: { () -> Void in
        self.pullDownTipLabel.transform = CGAffineTransformMakeTranslation(0, height)
        }) { (finish) -> Void in
            UIView.animateWithDuration(1, delay: 1, options: [], animations: { () -> Void in
                self.pullDownTipLabel.transform = CGAffineTransformIdentity

                }, completion: { (finish) -> Void in
                    //動畫執(zhí)行完畢,隱藏
                    self.pullDownTipLabel.hidden = true
            })
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末程梦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子橘荠,更是在濱河造成了極大的恐慌屿附,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哥童,死亡現(xiàn)場離奇詭異挺份,居然都是意外死亡,警方通過查閱死者的電腦和手機贮懈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門匀泊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來优训,“玉大人,你說我怎么就攤上這事各聘〈Х牵” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵躲因,是天一觀的道長早敬。 經(jīng)常有香客問我,道長大脉,這世上最難降的妖魔是什么搞监? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮镰矿,結(jié)果婚禮上琐驴,老公的妹妹穿的比我還像新娘。我一直安慰自己秤标,他們只是感情好绝淡,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抛杨,像睡著了一般够委。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怖现,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天茁帽,我揣著相機與錄音,去河邊找鬼屈嗤。 笑死潘拨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶号。 我是一名探鬼主播铁追,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫船!你這毒婦竟也來了琅束?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤算谈,失蹤者是張志新(化名)和其女友劉穎涩禀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體然眼,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡艾船,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屿岂。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡践宴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出爷怀,到底是詐尸還是另有隱情阻肩,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布霉撵,位于F島的核電站磺浙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏徒坡。R本人自食惡果不足惜撕氧,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喇完。 院中可真熱鬧伦泥,春花似錦、人聲如沸锦溪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刻诊。三九已至防楷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間则涯,已是汗流浹背复局。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工我磁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留病游,地道東北人谎懦。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓榴嗅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親和蚪。 傳聞我的和親對象是個殘疾皇子迁央,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫爬坑、插件呻澜、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,029評論 4 62
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,518評論 25 707
  • 20170720第10拆 片段10 《拆出你的溝通力》 R: 片段10《拆出你的溝通力》 與有情緒的人溝通時...
    LiLi1113閱讀 311評論 0 2
  • 去年在參加QCon時递礼,就明顯感受到各個創(chuàng)業(yè)者都是『通吃』型,身懷百技羹幸。同時宰衙,在招聘或者尋找合作伙伴時,也個個都是能...
    悶騷喬巴閱讀 4,976評論 0 13
  • 富記菜館020-29897628 失眠的人睹欲,睡不著覺是個好麻煩的事情。失眠往往是有幾種因素:一種因素是因為心火很亢...
    琥珀工坊閱讀 190評論 0 1