我写一个IOS日历应用的过程-swift

需求

在项目中要用到一个日历控件,它需要支持单选、多选、节选(选择首尾)日期的功能,在网上找了一下,我不喜欢项目中出现OC,而swift代码里没找到太合适的控件,就琢磨着自己写一个。

地址

https://github.com/xiaohepan/Calendar.git

结构

我想使用collectionView或者tableView来实现我的日历控件,现在已经写好了使用collectionView实现的控件,就以collectionView来讲这个过程。

  1. 以一个月为一个section,一天为一个cell,而collectionView的sectionHeader正好可以作为一个月的标题。在设置了startDate和endDate之后,要计算出有几个月,每个月有多少天。如果能够计算出这两个值collectionView的dataSource 要实现的两个方法就完成了:
numberOfSectionsInCollectionView(collectionView:     UICollectionView) -> Int;
collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
  1. 接下来就要获得每天的信息,比如是否可以选择(selectable),是否已经选中(selected),是否是今天(isToday),是否在本月(isInMonth,因为要按星期排,所以在月初月末要用临近月份占坑)。所以我定义了一个结构体来表达这些信息。
public struct DayStateOptions:OptionSetType{
    
    public var rawValue:UInt
    
    public init(rawValue: UInt){
        self.rawValue = rawValue
    }
    public static var NotThisMonth:DayStateOptions{
        return DayStateOptions(rawValue: 1<<0)
    }
    public static var Today:DayStateOptions{
        return DayStateOptions(rawValue: 1<<1)
    }
    public static var UnSelectable:DayStateOptions{
        return DayStateOptions(rawValue: 1<<2)
    }
    public static var Selected:DayStateOptions{
        return DayStateOptions(rawValue: 1<<3)
    }
}
  1. 再下来,就要考虑不可选、单选、多选、节选(选择首尾)这四种选择模式。collectionView 有allowsSelection、allowsMultipleSelection两个属性,不可选的时候allowsSelection = false,单选的时候 allowsSelection = true ,allowsMultipleSelection = false,多选和节选的时候 allowsSelection = true,allowsMultipleSelection = true。节选的情况要我们自己做处理。我定义了一个枚举来表示这四种模式
public enum SelectionType:Int{
    case None = 0,Single,Mutable,Section
}

具体实现

我把这些计算放在了一个CalenadrDataSource类里,先看他的属性
//默认的开始日期

  1. public static let defaultFirstDate = NSDate(timeIntervalSince1970: -28800)
    //默认的结束日期
  2. public static let defaultLastDate = NSDate(timeIntervalSince1970:2145801599)
    //保存用户设置的开始日期
  3. private var firstDate:NSDate!
    //保存用户设置的结束日期
  4. private var lastDate:NSDate!
    //用户设置的开始日期的月初那一秒
  5. private var firstDateMonthBegainDate:NSDate!
    //用户设置的结束日期的月末那一秒
  6. private var lastDateMonthEndDate:NSDate!
    //日历类
  7. lazy var calendar:NSCalendar
    //当天所在的NSIndexPath
  8. private var todayIndex:NSIndexPath?
    //选中的日子所在的位置的数组,单选保持0-1个值,多选 0-N个值,节选 0-2个值
  9. private var selectedDates = NSIndexPath
    //格式化
  10. public let formatter:NSDateFormatter
    //选择模式
  11. public var selectionType:SelectionType = .None
    //日历开始的日期,计算属性
  12. public var startDate:NSDate
    //日历结束的日期,计算属性
  13. public var endDate:NSDate
    //有几个月
  14. public var monthCount:Int

只有calendar,formatter,selectionType,startDate,endDate是公开的,startDate,endDate是计算属性,根据这两个值,计算出firstDate,lastDate,firstDateMonthBegainDate,lastDateMonthEndDate,todayIndex,monthCount。

再看CalenadrDataSource的构造方法

//使用默认的开始和结束时间
    public convenience init(){
        self.init(startDate:CalenadrDataSource.defaultFirstDate,endDate:CalenadrDataSource.defaultLastDate)
    }
    //使用自定义的开始和结束时间
    public init(startDate:NSDate,endDate:NSDate){
        let result = startDate.compare(endDate)
        if result == .OrderedDescending {
            fatalError("startDate must smaller than endDate")
        }
        self.formatter = NSDateFormatter()
        self.startDate = startDate
        self.endDate = endDate
        formatter.dateFormat = "yyyy年MM月"
    }

再看其它业务方法:

    //多少年
    public func yearCount() -> Int
    {
    }
    //一星期多少天
    public func daysInWeek() -> Int{
        return calendar.maximumRangeOfUnit(.Weekday).length
    }
    //一个月多少天
    public func daysInMonth(monthIndex:Int) -> Int{
        return weeksInMonth(monthIndex) * daysInWeek()
    }
    //每天的信息
    public func dayState(indexPath:NSIndexPath) -> (NSDate,DayStateOptions){
    }
   //格式化日期
    public func StringDayFromDate(date:NSDate) -> String{
        return String(self.calendar.component(.Day, fromDate: date))
    }
   //格式化日期
    public func StringDateFromDate(date:NSDate) -> String{
        return formatter.stringFromDate(date)
    }
    //某个section的对应月份有几个星期
    public func weeksInMonth(monthIndex:Int) -> Int{
    }
    //把时时分分秒秒的信息改成0
    private func clampDate(date:NSDate, toComponents unitFlags:NSCalendarUnit) -> NSDate{
    }
    //某个section的对应月份开始那天
    private func firstOfMonthForSection(monthIndex:Int) -> NSDate
    {
    }
     //某个section的对应月份的信息 目前和firstOfMonthForSection差不多
    public func monthState(monthIndex:Int) -> NSDate
    {
    }
     //某日期所在的section
    func sectionForDate(date:NSDate) -> Int
    {
    }
     //某日期所在的NSIndexPath
    func indexPathForRowAtDate(date:NSDate) -> NSIndexPath
    {
    }
     //选中某天,返回true表示修改了selectedDates,collectionView需要刷新
    func didSelectItemAtIndexPath(indexPath:NSIndexPath) -> Bool{
    }
   //选中某天,返回true表示修改了selectedDates,collectionView需要刷新
    func didDeselectItemAtIndexPath(indexPath:NSIndexPath) -> Bool{
    }

有了这个类就能很方便的用collectionView写出日历控件了。但现在的实现有些差,还有很多要改进的地方。希望多指教。完整代码如下:

public class CalenadrDataSource
{
    
    public static let defaultFirstDate = NSDate(timeIntervalSince1970: -28800)
    public static let defaultLastDate = NSDate(timeIntervalSince1970:2145801599)
    //保存用户设置的开始日期
    private var firstDate:NSDate!
    //保存用户设置的结束日期
    private var lastDate:NSDate!
    //用户设置的开始日期的开始那一秒
    private var firstDateMonthBegainDate:NSDate!
    //用户设置的结束日期的结束那一秒
    private var lastDateMonthEndDate:NSDate!
    
    private lazy var calendar:NSCalendar = {
        //            let calendar =
        return NSCalendar.currentCalendar()
    }()
    
    private var todayIndex:NSIndexPath?
    
    var selectedDates = [NSIndexPath]()
    
    let formatter:NSDateFormatter
    
    var selectionType:SelectionType = .None{
        
        didSet{
            if selectionType == .None{
                selectedDates.removeAll()
                return
            }
            if selectedDates.count < 2 {
                return
            }
            switch (oldValue,selectionType) {
            case (.Mutable,.Single):
                selectedDates = Array(selectedDates.prefix(1))
            case (.Section,.Single):
                selectedDates = Array(selectedDates.prefix(1))
            case (.Mutable,.Section):
                selectedDates = [selectedDates.first!,selectedDates.last!]
            case (.Section,.Mutable):
               sectionToMutable()
            default:
                break
            }
        }
    }
    //日历开始的日期
    public var startDate:NSDate{
        set{
            if firstDate != nil && newValue.isEqualToDate(firstDate){
                return
            }
            if lastDate != nil{
                let result = newValue.compare(lastDate)
                if result == .OrderedDescending {
                    fatalError("startDate must smaller than endDate")
                }
                
            }
            
            firstDate =  clampDate(newValue, toComponents: [.Year,.Month,.Day])
            firstDateMonthBegainDate =  clampDate(newValue, toComponents: [.Month,.Year])
            if lastDate != nil{
                let today = NSDate()
                let result1 = today.compare(newValue)
                let result2 = today.compare(lastDate)
                if result1 != .OrderedAscending && result2 != .OrderedDescending{
                    todayIndex = indexPathForRowAtDate(today)
                }else{
                    todayIndex = nil
                }
            }
        }
        get{
            return firstDate
        }
    }
    
    //日历结束的日期
    public var endDate:NSDate{
        set{
            if lastDate != nil && newValue.isEqualToDate(lastDate){
                return
            }
            if firstDate != nil{
                let result = firstDate.compare(newValue)
                if result == .OrderedDescending {
                    fatalError("startDate must smaller than endDate")
                }
            }
            let components =  self.calendar.components([.Year,.Month,.Day],fromDate:newValue)
            components.hour = 23
            components.minute = 59
            components.second = 59
            lastDate = self.calendar.dateFromComponents(components)
            let firstOfMonth = self.clampDate(newValue, toComponents: [.Month,.Year])
            let offsetComponents = NSDateComponents()
            offsetComponents.month = 1
            let temp = self.calendar.dateByAddingComponents(offsetComponents, toDate: firstOfMonth, options: .WrapComponents)!
            lastDateMonthEndDate = NSDate(timeIntervalSince1970: temp.timeIntervalSince1970 - 1)
            if firstDate != nil{
                let today = NSDate()
                let result1 = today.compare(firstDate)
                let result2 = today.compare(newValue)
                if result1 != .OrderedAscending && result2 != .OrderedDescending{
                    todayIndex = indexPathForRowAtDate(today)
                    todayIndex?.section
                    todayIndex?.row
                }else{
                    todayIndex = nil
                }
            }
        }
        get{
            return lastDate
        }
    }
    
    
    
    
    func sectionToMutable(){
//        let length = self.calendar.components(.Day, fromDate: selectedDates[0], toDate: selectedDates[1], options: NSCalendarOptions(rawValue: 0)).day
//        selectedDates = Array<NSIndexPath>()
//        var date = selectedDates[0]
//        let offset = NSDateComponents()
//        offset.day = 1
//        for _ in 0 ..< length {
//            selectedDates.append(date)
//            date = self.calendar.dateByAddingComponents(offset, toDate: date, options: NSCalendarOptions(rawValue: 0))!
//        }
    }
    
    
    //使用默认的开始和结束时间
    public convenience init(){
        self.init(startDate:CalenadrDataSource.defaultFirstDate,endDate:CalenadrDataSource.defaultLastDate)
    }
    //使用自定义的开始和结束时间
    public init(startDate:NSDate,endDate:NSDate){
        let result = startDate.compare(endDate)
        if result == .OrderedDescending {
            fatalError("startDate must smaller than endDate")
        }
        self.formatter = NSDateFormatter()
        self.startDate = startDate
        self.endDate = endDate
        formatter.dateFormat = "yyyy年MM月"
    }
    
    //多少年
    public func yearCount() -> Int
    {
        let startYear = calendar.components(.Year,fromDate: CalenadrDataSource.defaultFirstDate, toDate: firstDateMonthBegainDate,options: .WrapComponents).year
        
        let endYear = calendar.components(.Year,fromDate: CalenadrDataSource.defaultFirstDate, toDate: lastDateMonthEndDate,options: .WrapComponents).year
        
        return endYear - startYear + 1
    }
    
    
    
    //一星期多少天
    public func daysInWeek() -> Int{
        return calendar.maximumRangeOfUnit(.Weekday).length
    }
    
    
    
    //多少月
    public var monthCount:Int{
        get{
            return calendar.components(.Month, fromDate: firstDateMonthBegainDate, toDate: lastDateMonthEndDate, options: .WrapComponents).month + 1
        }
    }
    
    //一个月多少天
    public func daysInMonth(monthIndex:Int) -> Int{
        return weeksInMonth(monthIndex) * daysInWeek()
    }
    //每天的信息
    public func dayState(indexPath:NSIndexPath) -> (NSDate,DayStateOptions){
        var options = DayStateOptions(rawValue:0)
        
        if todayIndex != nil{
            if indexPath.isEqual(todayIndex){
                options = [options,.Today]
            }
        }
        
        let firstOfMonth =  self.firstOfMonthForSection(indexPath.section)
        
        let ordinalityOfFirstDay =  1 - self.calendar.component(.Weekday, fromDate: firstOfMonth) + indexPath.row
        
        if ordinalityOfFirstDay < 0{
            options = [options,.NotThisMonth]
        } else{
            let maxRangeDay =  calendar.rangeOfUnit(.Day, inUnit: .Month, forDate: firstOfMonth).length
            if ordinalityOfFirstDay >= maxRangeDay{
                options = [options,.NotThisMonth]
            }else{
                let count = selectedDates.count
                switch count{
                case 0:
                    break
                case 2:
                    if selectionType == .Section{
                        let a = selectedDates[0].compare(indexPath)
                        NSLog("a = \(a)")
                        if a == .OrderedAscending {
                            let b = indexPath.compare(selectedDates[1])
                              NSLog("b = \(b)")
                            if b != .OrderedDescending{
                                options = [options,.Selected]
                            }
                        }else if a == .OrderedSame{
                             options = [options,.Selected]
                        }
                    }else{
                        fallthrough
                    }
                default:
                    if selectedDates.contains(indexPath){
                        options = [options,.Selected]
                    }
                }
            }
        }
        let  dateComponents = NSDateComponents()
        dateComponents.day = ordinalityOfFirstDay
        let date = self.calendar.dateByAddingComponents(dateComponents, toDate: firstOfMonth, options: NSCalendarOptions(rawValue: 0))!
        return (date,options)

    }

    public func StringDayFromDate(date:NSDate) -> String{
        return String(self.calendar.component(.Day, fromDate: date))
    }
    public func StringDateFromDate(date:NSDate) -> String{
        return formatter.stringFromDate(date)
    }
    
   
    
    //一个月多少星期
    public func weeksInMonth(monthIndex:Int) -> Int{
        let firstOfMonth = self.firstOfMonthForSection(monthIndex)
        let rangeOfWeeks = self.calendar.rangeOfUnit(.WeekOfMonth,inUnit: .Month,forDate: firstOfMonth).length
        return rangeOfWeeks
    }
    
    
    
    private func clampDate(date:NSDate, toComponents unitFlags:NSCalendarUnit) -> NSDate{
        let components = self.calendar.components(unitFlags,fromDate:date)
        return self.calendar.dateFromComponents(components)!
    }
    //一个月开始的时间
    private func firstOfMonthForSection(monthIndex:Int) -> NSDate
    {
        let offset = NSDateComponents()
        offset.month = monthIndex
        return self.calendar.dateByAddingComponents(offset, toDate: firstDateMonthBegainDate, options: NSCalendarOptions(rawValue: 0))!
    }
    
    
    public func monthState(monthIndex:Int) -> NSDate
    {
        let offset = NSDateComponents()
        offset.month = monthIndex
        return self.calendar.dateByAddingComponents(offset, toDate: firstDateMonthBegainDate, options: NSCalendarOptions(rawValue: 0))!
    }
    
    
    func sectionForDate(date:NSDate) -> Int
    {
        return self.calendar.components(.Month,fromDate:self.firstDateMonthBegainDate,toDate:date,options:.WrapComponents).month
    }
    
    func indexPathForRowAtDate(date:NSDate) -> NSIndexPath
    {
        let section = self.sectionForDate(date)
        let firstOfMonth = self.firstOfMonthForSection(section)
        
        let ordinalityOfFirstDay =  1 - self.calendar.component(.Weekday, fromDate: firstOfMonth)
        
        let  dateComponents = NSDateComponents()
        dateComponents.day = ordinalityOfFirstDay
        let startDateInSection = self.calendar.dateByAddingComponents(dateComponents, toDate: firstOfMonth, options: NSCalendarOptions(rawValue: 0))!
        
        let row = self.calendar.components(.Day, fromDate: startDateInSection, toDate: date, options: NSCalendarOptions(rawValue: 0)).day
        return NSIndexPath(forRow:row,inSection:section)
    }
    
    func didSelectItemAtIndexPath(indexPath:NSIndexPath) -> Bool{
        
        if selectedDates.contains(indexPath){
            return false
        }
        switch selectionType {
        case .None:
            return false
        case .Single:
            if selectedDates.count == 0{
                selectedDates.append(indexPath)
            }else{
                selectedDates[0] = indexPath
            }
        case .Mutable:
            selectedDates.append(indexPath)
        case .Section:
            if selectedDates.count == 0{
                selectedDates.append(indexPath)
            }else if selectedDates.count == 1{
                let result = selectedDates[0].compare(indexPath)
                switch result {
                case .OrderedSame:
                    return false
                case .OrderedAscending:
                    selectedDates.append(indexPath)
                case .OrderedDescending:
                   selectedDates.insert(indexPath, atIndex: 0)
                }
            }else{
                selectedDates.removeAll()
                selectedDates.append(indexPath)
            }
        }
        NSLog("didSelectItemAtIndexPath \(selectedDates)")
        return true
    }
    func didDeselectItemAtIndexPath(indexPath:NSIndexPath) -> Bool{
        if let index = selectedDates.indexOf(indexPath){
            selectedDates.removeAtIndex(index)
             NSLog("didDeselectItemAtIndexPath \(selectedDates)")
            return true
        }
        return false
    }
    
}

public struct DayStateOptions:OptionSetType{
    
    public var rawValue:UInt
    
    public init(rawValue: UInt){
        self.rawValue = rawValue
    }
    public static var NotThisMonth:DayStateOptions{
        return DayStateOptions(rawValue: 1<<0)
    }
    public static var Today:DayStateOptions{
        return DayStateOptions(rawValue: 1<<1)
    }
    public static var UnSelectable:DayStateOptions{
        return DayStateOptions(rawValue: 1<<2)
    }
    public static var Selected:DayStateOptions{
        return DayStateOptions(rawValue: 1<<3)
    }
}
public enum SelectionType:Int{
    case None = 0,Single,Mutable,Section
}
class TimeSelectorVC: UICollectionViewController
{
    
    private var  dataSourceManager:CalenadrDataSource
    
    var selectionType:SelectionType = .Section{
        didSet{
            guard oldValue  != selectionType else{
                return
            }
            initSelectionType()
        }
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        dataSourceManager = CalenadrDataSource()
        super.init(coder: aDecoder)
    }
    
    override init(collectionViewLayout layout: UICollectionViewLayout) {
        dataSourceManager = CalenadrDataSource()
        super.init(collectionViewLayout: layout)
    }
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        dataSourceManager = CalenadrDataSource()
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        initSelectionType()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
  
    }
    
    func initSelectionType(){
        switch selectionType {
        case .None:
            self.collectionView?.allowsSelection = false
            dataSourceManager.selectionType = .None
        case .Single:
            self.collectionView?.allowsSelection = true
            self.collectionView?.allowsMultipleSelection = false
            dataSourceManager.selectionType = .Single
        case .Mutable:
            self.collectionView?.allowsSelection = true
            self.collectionView?.allowsMultipleSelection = true
            dataSourceManager.selectionType = .Mutable
        case .Section:
            self.collectionView?.allowsSelection = true
            self.collectionView?.allowsMultipleSelection = true
            dataSourceManager.selectionType = .Section
        }

    }
    
    // MARK: UICollectionViewDataSource
    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return dataSourceManager.monthCount
    }


    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataSourceManager.daysInMonth(section)
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("DayCell", forIndexPath: indexPath)
        let (date,dayState) = dataSourceManager.dayState(indexPath)
        if let label = cell.viewWithTag(1) as? UILabel
        {
            if dayState.contains(.NotThisMonth)
            {
                label.hidden = true
                cell.backgroundColor = UIColor.whiteColor()
            }else{
                label.hidden = false
                label.text = dataSourceManager.StringDayFromDate(date)
                if dayState.contains(.Selected){
                
                    if dayState.contains(.Today){
                       cell.backgroundColor = UIColor.redColor()
                    }else{
                       cell.backgroundColor = UIColor.blueColor()
                    }
                    label.textColor = UIColor.whiteColor()
                }else{
                    cell.backgroundColor = UIColor.whiteColor()
                    if dayState.contains(.Today){
                        label.textColor = UIColor.redColor()
                    }else{
                        label.textColor = UIColor.blackColor()
                    }
                }
            }
        }
        return cell
    }

    override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView
    {
        let cell = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "SectionCell", forIndexPath: indexPath)
        if let label = cell.viewWithTag(1) as? UILabel
        {
            label.text = dataSourceManager.StringDateFromDate(dataSourceManager.monthState(indexPath.section))
        }
        
        return cell
    }
    
    // MARK: UICollectionViewDelegate
    override func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
        let (_,dayState) = dataSourceManager.dayState(indexPath)
        if dayState.contains(.NotThisMonth) || dayState.contains(.UnSelectable){
            return false
        }
        return true
    }

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

推荐阅读更多精彩内容