UICollectionViewLayout.h

import Foundation
import UIKit

//
//  UICollectionViewLayout.h
//  UIKit
//
//  Copyright (c) 2011-2015 Apple Inc. All rights reserved.
//

// The UICollectionViewLayout class is provided as an abstract class for subclassing to define custom collection layouts.
// 是一个抽象类,使用子类进行一些自定义的 collection 视图的布局。
// Defining a custom layout is an advanced operation intended for applications with complex needs.
// 定义一个自定义的布局是一种先进的复杂操作的应用程序的需求。



// 集合元素的枚举
public enum UICollectionElementCategory : UInt {
    
    case Cell                // cell
    case SupplementaryView   // 补充视图
    case DecorationView      // 装饰视图
}


@available(iOS 6.0, *)
// 布局属性
public class UICollectionViewLayoutAttributes : NSObject, NSCopying, UIDynamicItem {

    // 位置和尺寸
    public var frame: CGRect
    
    // 中心点
    public var center: CGPoint

    // 大小
    public var size: CGSize

    // 3d 变换
    public var transform3D: CATransform3D
    @available(iOS 7.0, *)

   // 大小
    public var bounds: CGRect

    @available(iOS 7.0, *)
    // 2d 变换
    public var transform: CGAffineTransform

    // 透明度
    public var alpha: CGFloat

    // z 轴的层叠关系
    public var zIndex: Int // default is 0

    // 是否隐藏
    public var hidden: Bool // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES

    // 索引
    public var indexPath: NSIndexPath
    
    // 显示的是那种类型的视图
    public var representedElementCategory: UICollectionElementCategory { get }

    // header 还是 footer
    public var representedElementKind: String? { get } // nil when representedElementCategory is UICollectionElementCategoryCell
    

    // 便利构造器
    public convenience init(forCellWithIndexPath indexPath: NSIndexPath)
    public convenience init(forSupplementaryViewOfKind elementKind: String, withIndexPath indexPath: NSIndexPath)
    public convenience init(forDecorationViewOfKind decorationViewKind: String, withIndexPath indexPath: NSIndexPath)
}



// 集合视图的更新动作
public enum UICollectionUpdateAction : Int {
    
    case Insert
    case Delete
    case Reload
    case Move
    case None
}



@available(iOS 6.0, *)
// 集合视图更新的模型
public class UICollectionViewUpdateItem : NSObject {
    
    // 更新之前的索引
    public var indexPathBeforeUpdate: NSIndexPath? { get } // nil for UICollectionUpdateActionInsert

    // 更新之后的索引
    public var indexPathAfterUpdate: NSIndexPath? { get } // nil for UICollectionUpdateActionDelete

    // 更新操作
    public var updateAction: UICollectionUpdateAction { get }
}



@available(iOS 7.0, *)
// 集合视图布局失效的上下文
public class UICollectionViewLayoutInvalidationContext : NSObject {
    
    // 失效任何
    public var invalidateEverything: Bool { get } // set to YES when invalidation occurs because the collection view is sent -reloadData

    // 无效的数据源个数
    public var invalidateDataSourceCounts: Bool { get } // if YES, the layout should requery section and item counts from the collection view - set to YES when the collection view is sent -reloadData and when items are inserted or deleted
    
    @available(iOS 8.0, *)
    public func invalidateItemsAtIndexPaths(indexPaths: [NSIndexPath])
    @available(iOS 8.0, *)
    public func invalidateSupplementaryElementsOfKind(elementKind: String, atIndexPaths indexPaths: [NSIndexPath])
    @available(iOS 8.0, *)
    public func invalidateDecorationElementsOfKind(elementKind: String, atIndexPaths indexPaths: [NSIndexPath])
    @available(iOS 8.0, *)
    public var invalidatedItemIndexPaths: [NSIndexPath]? { get }
    @available(iOS 8.0, *)
    public var invalidatedSupplementaryIndexPaths: [String : [NSIndexPath]]? { get } // keys are element kind strings - values are NSArrays of NSIndexPaths
    @available(iOS 8.0, *)
    public var invalidatedDecorationIndexPaths: [String : [NSIndexPath]]? { get } // keys are element kind strings - values are NSArrays of NSIndexPaths
    
    @available(iOS 8.0, *)
    public var contentOffsetAdjustment: CGPoint // delta to be applied to the collection view's current contentOffset - default is CGPointZero
    @available(iOS 8.0, *)
    public var contentSizeAdjustment: CGSize // delta to be applied to the current content size - default is CGSizeZero
    
    // Reordering support
    @available(iOS 9.0, *)
    public var previousIndexPathsForInteractivelyMovingItems: [NSIndexPath]? { get } // index paths of moving items prior to the invalidation
    @available(iOS 9.0, *)
    public var targetIndexPathsForInteractivelyMovingItems: [NSIndexPath]? { get } // index paths of moved items following the invalidation
    @available(iOS 9.0, *)
    public var interactiveMovementTarget: CGPoint { get }
}



@available(iOS 6.0, *)
// 集合视图布局
public class UICollectionViewLayout : NSObject, NSCoding {
    
    public init()
    public init?(coder aDecoder: NSCoder)
    
    // Methods in this class are meant to be overridden and will be called by its collection view to gather layout information.
    // To get the truth on the current state of the collection view, call methods on UICollectionView rather than these.
    
    // 正在使用这个布局的集合视图
    public var collectionView: UICollectionView? { get }

    // MARK: - iOS 10
    // 返回集合视图 内容的 宽度和高度 
    /*
     collectionViewContentSize() -> CGSize  方法的替代品。
     子类必须重写这个属性的 get 方法,返回 集合视图内容的宽高。
     这个值代表的是左右内容的宽和高,不仅仅是当前显示的内容的宽高。 
     集合视图使用这个信息去配置自己的 contentSize 从而导致可以滚动。
    */
    public var collectionViewContentSize: CGSize { get }

    // Call -invalidateLayout to indicate that the collection view needs to requery the layout information.
    // Subclasses must always call super if they override.
    // 调用  -invalidateLayout 表明, 集合视图需要重新查询布局信息
    // 子类必须总是调用父类
    public func invalidateLayout()


    @available(iOS 7.0, *)
    // 布局失效的上下文
    public func invalidateLayoutWithContext(context: UICollectionViewLayoutInvalidationContext)



    // 注册装饰视图    
    public func registerClass(viewClass: AnyClass?, forDecorationViewOfKind elementKind: String)
    public func registerNib(nib: UINib?, forDecorationViewOfKind elementKind: String)
}


extension UICollectionViewLayout {
    
    // 布局属性类,
    // 重载这个类,是为了提供一个自定义的 UICollectionViewLayoutAttributes 来使用
    public class func layoutAttributesClass() -> AnyClass // override this method to provide a custom class to be used when instantiating instances of UICollectionViewLayoutAttributes


    @available(iOS 7.0, *)
    // 失效的上下文类
    // 重载这个类,主要是提供一个自定义的失效上下文
    public class func invalidationContextClass() -> AnyClass 
    // override this method to provide a custom class to be used for invalidation contexts
    

    // MARK: - 准备布局
    /*
     The collection view calls -prepareLayout once at its first layout as the first message to the layout instance.
     布局对象进行第一次布局的时候 collection view 会调用这个方法

     The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information.
     collection view 会在布局失效后,和查询新的布局信息之前调用这个方法。

     Subclasses should always call super if they override.
     子类必须总是调用父类

     注意:  这个方法主要是告诉布局对象更新当前的布局信息

    布局更新发生:
           * 在 collection view 第一次呈现内容的时候。
           * 无论在什么时候由于视图的改变导致的 布局明确的失效和隐含的失效都会导致布局更新

            在每个布局更新期间, collection view 调用这个方法的第一次的时候会给你的布局对象一次准备即将到来的布局操作的机会。
            这个方法默认实现没有做任何事情。子类能够重载这个方法,使用这个方法设置一些数据结构和完成一些完成布局需要的初始化计算。
    */ 
    public func prepareLayout()
    


    // MARK: - 布局信息的确认
    // UICollectionView calls these four methods to determine the layout information.
    //  UICollectionView 调用下面 4 个方法来群的个布局信息。

    // Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
    // 实现  -layoutAttributesForElementsInRect:  去给 supplementary 或 decoration  返回一个布局属性,或者需要在屏幕上完成一个需要的布局

    // Additionally, all layout subclasses should implement -layoutAttributesForItemAtIndexPath: to return layout attributes instances on demand for specific index paths.
    // 此外,所有的布局子类,将实现  -layoutAttributesForItemAtIndexPath:  方法去返回一个布局属性,需要指定索引。

    // If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
    // 如果布局对象支持任何的 补充视图和 装饰视图类型, 还应该实现各自的 atIndexPath: 方法。

    // return an array layout attributes instances for all the views in the given rect
    // 返回一个 布局属性数组,在给定 rect 里面的所有视图。
    /**
      方法默认返回时 nil ,返回值是代表所有 cel 和 view 的布局信息的 布局属性对象数组。
      创建布局属性的时候,总是创建代表正确元素类型的布局属性。集合视图会根据属性的类型来区分不同的属性。使用这些信息来决怎么创建哪些视图和如何管理视图。
      */
    public func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? 
  
    // 获取指定 indexPath 的 cell 的布局属性
    // 布局属性包含提供给 item 的所有信息, 使用这个方法得到的布局信息只对某个 item 有效
    public func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?


    // 获取 指定index  和 kind 的补充视图布局属性
   //  如果有补充视图,就必须重载这个方法,并使用这个方法获取布局属性。
    public func layoutAttributesForSupplementaryViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?

    // 获取装饰视图的布局属性
    public func layoutAttributesForDecorationViewOfKind(elementKind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    

    // return YES to cause the collection view to requery the layout for geometry information
    // 返回 yes 去引起 集合视图询问布局的几何信息
    // 是否由于 bounds 的改变来触发布局失效
    public func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool 
    @available(iOS 7.0, *)

    // 由于 bounds 改变而导致的布局失效上下文
    public func invalidationContextForBoundsChange(newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext
    
    @available(iOS 8.0, *)
    // 布局失效 偏好布局属性
    public func shouldInvalidateLayoutForPreferredLayoutAttributes(preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> Bool
    @available(iOS 8.0, *)
    public func invalidationContextForPreferredLayoutAttributes(preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext
    

    // 目标内容的偏移量
    // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
    
    // 返回一个停止滚动的点。 
    // collection view 停止滚动会调用这个方法。
    /*
      proposedContentOffset :  滚动停止后,左上角可见视图的偏移量
      velocity: x  轴和  y 轴的滚动速度
      
      你可以使用一个你想要的值进行替换。 默认实现就是 proposedContentOffset 参数

     方法重载:
        如果你想要快速滚动到一个指定的边界, 你可以重载这个方法,更改这个停止的点。 例如,你可以使用这个方法总是停止在边界内某个items 之间,或者是 item 的中间。
    */
    public func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint 


    @available(iOS 7.0, *)
     // a layout can return the content offset to be applied during transition or update animations
    /*
        在使用一个动画布局进行更新或改变后,返回一个 内容的偏移量。
        proposedContentOffset: 推荐的一个偏移点。是可视内容左上角的一个点。
       这代表点,集合视图计算动画结束,最可能使用的值。 这个方法的默认实现值直接返回  proposedContentOffset 的值。
       你可以使用一个你想要替代的 偏移量返回。

       在布局更新期间,或者布局过度期间。这个 collection view 会调用这个方法,去给你一个机会去改变 推荐的偏移量,使用在结束动画上。

       方法的重载:
            如果动画或者 过度效果 可能引起 items 的 positioned 和你的设计不吻合的时候,可以重载这个方法。
           collection view 回调用 这个方法之后,调用   prepare() 和  collectionViewContentSize 方法。
    */  
    public func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint 
    


    /*
    Subclasses must override this method and use it to return the width and height of the collection view’s content.
    子类必须重载这个方法,去返回 collection view’s content 的宽度和高度。
    These values represent the width and height of all the content, not just the content that is currently visible. 
    这个值代表着所有内容的宽度和高度,不单单是可见内容的宽度和高度。
    The collection view uses this information to configure its own content size to facilitate scrolling. 
    集合视图会使用这个信息配置自己内容的尺寸来促使可以滚动
    */
    public func collectionViewContentSize() -> CGSize 
   
}


extension UICollectionViewLayout {
    
    // It will be called prior to calling the initial/final layout attribute methods below to give the layout an opportunity to do batch computations for the insertion and deletion layout attributes.
    // The updateItems parameter is an array of UICollectionViewUpdateItem instances for each element that is moving to a new index path.
    // 通知布局对象, collection view 的 content 发生了改变
    /*
      通知布局对象,collection view 的 content 即将改变。

      This method is called when there is an update with deletes/inserts to the collection view.
      当  collection view 的 items 进行 deletes 和 inserts 会通知布局对象调用这个方法。以至于 布局对象可以根据需求对布局进行调整。
      第一个过程是,调用这个方法让布局对象知道预期的变化,之后再收集布局的 插入,删除,移动 信息在 collection view 进行动画。
    */
    public func prepareForCollectionViewUpdates(updateItems: [UICollectionViewUpdateItem])


    /*
      执行所需的任何额外的动画或清理集合视图中更新。
      集合视图调用这个方法作为动画的任何改变之前最后一步。这个方法会在 插入,添加,删除的 block 动画执行完毕后调用,以至于你可以添加一些附加的动画。
      因此,您可以使用它来执行任何最后一分钟的任务与管理你的布局对象的状态信息。
    */
    public func finalizeCollectionViewUpdates() // called inside an animation block after the update
    


    // MARK: 协调动画变化
    public func prepareForAnimatedBoundsChange(oldBounds: CGRect) // UICollectionView calls this when its bounds have changed inside an animation block before displaying cells in its new bounds
    public func finalizeAnimatedBoundsChange() // also called inside the animation block
    


    // MARK: layout 之间的过度
    // UICollectionView calls this when prior the layout transition animation on the incoming and outgoing layout
    @available(iOS 7.0, *)
    public func prepareForTransitionToLayout(newLayout: UICollectionViewLayout)
    @available(iOS 7.0, *)
    public func prepareForTransitionFromLayout(oldLayout: UICollectionViewLayout)
    @available(iOS 7.0, *)
    public func finalizeLayoutTransition() // called inside an animation block after the transition
    





    // This set of methods is called when the collection view undergoes an animated transition such as a batch update block or an animated bounds change.
    // For each element on screen before the invalidation, finalLayoutAttributesForDisappearingXXX will be called and an animation setup from what is on screen to those final attributes.
    // For each element on screen after the invalidation, initialLayoutAttributesForAppearingXXX will be called and an animation setup from those initial attributes to what ends up on screen.
    public func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    public func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    public func initialLayoutAttributesForAppearingSupplementaryElementOfKind(elementKind: String, atIndexPath elementIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    public func finalLayoutAttributesForDisappearingSupplementaryElementOfKind(elementKind: String, atIndexPath elementIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    public func initialLayoutAttributesForAppearingDecorationElementOfKind(elementKind: String, atIndexPath decorationIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    public func finalLayoutAttributesForDisappearingDecorationElementOfKind(elementKind: String, atIndexPath decorationIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
    
    // These methods are called by collection view during an update block.
    // Return an array of index paths to indicate views that the layout is deleting or inserting in response to the update.
    @available(iOS 7.0, *)
    public func indexPathsToDeleteForSupplementaryViewOfKind(elementKind: String) -> [NSIndexPath]
    @available(iOS 7.0, *)
    public func indexPathsToDeleteForDecorationViewOfKind(elementKind: String) -> [NSIndexPath]
    @available(iOS 7.0, *)
    public func indexPathsToInsertForSupplementaryViewOfKind(elementKind: String) -> [NSIndexPath]
    @available(iOS 7.0, *)
    public func indexPathsToInsertForDecorationViewOfKind(elementKind: String) -> [NSIndexPath]
}

// MARK: - 9.0 新加的
extension UICollectionViewLayout {
    
    @available(iOS 9.0, *)
    public func targetIndexPathForInteractivelyMovingItem(previousIndexPath: NSIndexPath, withPosition position: CGPoint) -> NSIndexPath
    
    @available(iOS 9.0, *)
    // 当用户交互式的移动 item 的时候 返回 item 的布局属性
    /*
       indexPath: item  item的目标索引, 这个 indexPath 是 targetIndexPathForInteractivelyMovingItem 方法返回值
       position: item 在 collection view 坐标系中当前的位置
       返回值是在指定位置的 item 的布局属性。

       当 item 由于用户交互的原因进行了移动,布局对象会调用这个方法去接收一个布局属性,并将这个 布局属性用在指定位置的 item 上。
       这个方法的默认实现是返回 item 已经存在的布局属性的拷贝, 并对拷贝的 center 进行修改,center = position , zIndex 也进行了修改,zIndex = NSIntegerMax。 以至于 item 可以浮动在 collection view  items 的上面。

    方法的重载:
          子类可以重载这个方法,去修改一些需要的附加布局属性。
          如果重载这个方法,调用父类方法去接收一个 item 已经存在的属性。做一些你自己的改变,让后返回这个数据结构。
    */
    public func layoutAttributesForInteractivelyMovingItemAtIndexPath(indexPath: NSIndexPath, withTargetPosition position: CGPoint) -> UICollectionViewLayoutAttributes
    
    @available(iOS 9.0, *)
    public func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [NSIndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext
    @available(iOS 9.0, *)
    public func invalidationContextForEndingInteractiveMovementOfItemsToFinalIndexPaths(indexPaths: [NSIndexPath], previousIndexPaths: [NSIndexPath], movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext
}

UICollectionViewLayout 对象的使用

UICollectionViewLayout 是一个基础的抽象类,必须使用子类化,使用子类来产生 collection view 需要的布局信息。布局对象的工作是去决定在 collection view 范围内部 cell, supplementary, decoration 视图的位置,在需要的时候向 collection view 报告这些信息。collection view 根据响应的布局相应的视图以至于视图能够在屏幕上显示。

你必须子类化 UICollectionViewLayout 去使用。在你考虑子类化之前, 查看 UICollectionViewFlowLayout 类是否能够满足你的布局需求。

子类化注意事项
布局对象的主要工作是提供在 collection view 内的 item 的 位置和 视觉状态信息。布局对象不会根据提供的布局来创建视图。视图是被 collection view 的数据源创建的。相反,布局对象定义了根据布局设计的视觉元素的位置和尺寸。

Collection view 有三个类型的视觉元素需要去布局:

  • cell:
  • Supplementary views
  • Decoration Views

方法重载
每一个布局对象将要实现下面这些方法:

 collectionViewContentSize
 // 确定集合视图内容的尺寸(这个 contentSize 决定了 collection view 的滚动范围)
   
layoutAttributesForElements(in:)
// 返回素有元素(cell, Supplementary views, Decoration Views) 的布局属性 
 
layoutAttributesForItem(at:)
// 返回 item 的布局属性

   *下面两个方法只在需要的时候重载 
layoutAttributesForSupplementaryView(ofKind:at:) (if your layout supports supplementary views)
 // 返回  Supplementary views 的布局属性

layoutAttributesForDecorationView(ofKind:at:) (if your layout supports decoration views)
// 返回 Decoration Views 的布局属性

shouldInvalidateLayout(forBoundsChange:)
// 是否由于  collection view bounds 的改变而实现布局(进行重新布局)

这些方法提供了 collection view 需要去设置屏幕上内容位置的基本的布局信息。当然,如果你的布局不需要支持 supplementary 或 decoration views ,你就不需要实现相应的方法。

当 collection view 的 data 发生改变(item 进行了 删除,插入)的时候, collection view 要求布局对象去更新布局信息。
尤其是,任何 item 被 moved, added ,deleted 必须对 item 的布局信息进行更新,去映射到新的位置。

  • 对于 item 的移动, collection view 使用标准的方法去获取 item 更新的布局属性。
  • 对于 item 的插入和删除, collection view 会调用一些不同的方法,你需要重载这些方法,提供恰当的布局信息

插入和删除需要重载的方法

initialLayoutAttributesForAppearingItem(at:)

initialLayoutAttributesForAppearingSupplementaryElement(ofKind:at:)

initialLayoutAttributesForAppearingDecorationElement(ofKind:at:)

finalLayoutAttributesForDisappearingItem(at:)

finalLayoutAttributesForDisappearingSupplementaryElement(ofKind:at:)

finalLayoutAttributesForDisappearingDecorationElement(ofKind:at:)

除了上面的这些方法,你还可以重载

// 处理任何布局相关的操作
prepare(forCollectionViewUpdates:)

// 使用这个方法,添加动画到整体的动画块中
// 或者实现一些最终的布局相关的任务。
finalizeCollectionViewUpdates()

使用失效上下文来优化布局性能
当设计你的自定义布局的时候, 只有你布局的那一部分真实的发生改变的时候使用 invalidating 可以提高性能。 当你改变 items 的时候 , 调用 invalidateLayout() 方法,触发 collection view 重新计算 collection view 申请的所有的布局信息。更好的解决方案是只去重新计算发生改变的布局信息,这恰好是 invalidation contexts 允许你这么去做的。 一个 invalidation context 告诉你 那一部分的布局发生了改变。布局对象能够使用这些信息去减少大量的重复计算。

子类化 UICollectionViewLayoutInvalidationContext 类, 给你的布局对象自定义一个失效上下文。在你的子类里面,定义自定义的属性,来代表你布局的那一部分数据,致使这一部分数据可以被独立重新计算。当你需要在运行时失效你的布局,创建一个你的失效上下文实例对象的子类,配置一些基于布局信息改变的自定义属性, 并将该对象传递给你的布局invalidateLayout(:)方法。在这些方法的自定义实现里面你可以使用 invalidation context 里面的信息去重新计算你布局发生改变的那一部分。

如果你给你的布局对象自定义了 invalidation context 上下文类,你将要重载 布局对象的 invalidationContextClass() 方法,去返回你自定义的类。 collection view 当需要一个失效上下文的时候总是创建一个你指定类的实例对象。 从这个方法返回您的自定义子类可以确保您的布局对象总是预计的失效上下文。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容