我不是八哥是乌鸫 發表於 2022-10-24 17:14:00

iOS开发之自定义日历控件

<h1 id="前言">前言</h1>
<p>日常开发中经常会遇到日期选择,为了方便使用,简单封装了一个日历控件,在此抛砖引玉供大家参考。</p>
<h1 id="效果">效果</h1>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0e00e2632d5540acaa892038ceadd6b9~tplv-k3u1fbpfcp-watermark.image?" alt="效果图" loading="lazy"></p>
<h1 id="功能">功能</h1>
<ul class="contains-task-list">
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"><label> 支持单选、区间</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"><label> 支持默认选中日期</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"><label> 支持限制月份</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"><label> 支持过去、当前、未来模式</label></li>
<li class="task-list-item"><input class="task-list-item-checkbox" checked="" disabled="" type="checkbox"><label> 支持<code>frame</code>、<code>AutoLayout</code></label></li>
</ul>
<h1 id="原理">原理</h1>
<p>层次结构使用<code>UIStackView</code>布局,<code>UICollectionView</code>复用,背景使用<code>DecorationView</code>。</p>
<h1 id="核心代码">核心代码</h1>
<p>日历核心代码在于计算每个月的日期,主要代码如下:</p>
<pre><code>func month() -&gt; {
    func day(with date: Date, section: Int) -&gt; {
      var newDate = date
      let tatalDay = newDate.daysInMonth
      let firstDay = max(0, newDate.weekday - 1)
      let columns = Int(ceil(CGFloat(tatalDay + firstDay) / CGFloat(weekArray.count)))
      var resultArray = ()
      for column in 0 ..&lt; columns {
            for weekDay in 0 ..&lt; weekArray.count {
                if column == 0,
                   weekDay &lt;= firstDay - 1
                {
                  resultArray.append(CLCalendarDayModel())
                } else {
                  let subtitle: String? = {
                        guard !newDate.isToday else { return "今天" }
                        guard config.isShowLunarCalendar else { return nil }
                        guard let index = chinese.dateComponents([.day], from: newDate).day else { return nil }
                        return chineseDayArray
                  }()
                  let type: CLCalendarDayModel.CLCalendarDayType = {
                        guard !newDate.isToday else { return .today }
                        guard newDate.compare(todayDate) == .orderedDescending else { return .future }
                        return .past
                  }()
                  
                  let dayModel = CLCalendarDayModel(title: "\(newDate.day)", date: newDate, subtitle: subtitle, type: type)
                  resultArray.append(dayModel)
                  newDate = newDate + 1.days
                  if beginDate?.year == dayModel.date?.year,
                     beginDate?.month == dayModel.date?.month,
                     beginDate?.day == dayModel.date?.day
                  {
                        startIndexPath = .init(row: 0, section: section)
                  }
                  guard (resultArray.count - firstDay) != tatalDay else { break }
                }
            }
      }
      return resultArray
    }
   
    var resultArray = ()
    let month: Int = {
      var value = 0
      if config.type == .past {
            value = config.limitMonth - 1
      } else if config.type == .today {
            value = config.limitMonth / 2
      }
      return value
    }()
   
    let start = todayDate - month.months
    for i in 0 ..&lt; config.limitMonth {
      let date = start + i.months
      let headerModel = CLCalendarMonthModel(headerText: date.format(with: "yyyy年MM月"),
                                             month: date.format(with: "MM"),
                                             daysArray: day(with: Date(year: date.year, month: date.month, day: 1), section: i))
      resultArray.append(headerModel)
    }
    return resultArray
}
</code></pre>
<h1 id="基础配置">基础配置</h1>
<pre><code class="language-swift">struct CLCalendarConfig {
    enum CLCalendarType {
      case past
      case today
      case future
    }

    enum CLSelectType {
      case single
      case area
    }

    struct CLTouchType: OptionSet {
      static let past = CLTouchType(rawValue: 1)
      static let today = CLTouchType(rawValue: 1 &lt;&lt; 1)
      static let future = CLTouchType(rawValue: 1 &lt;&lt; 2)
      let rawValue: Int64
      init(rawValue: Int64) {
            self.rawValue = rawValue
      }
    }

    struct CLColor {
      var background = "#ffffff".uiColor
      var topToolBackground = "#F4F4F4".uiColor
      var topToolText = "#444444".uiColor
      var topToolTextWeekend = "#3CCA79".uiColor
      var sectionBackgroundText = "f2f2f2".uiColor
      var selectStartBackground = "#4bce817f".uiColor
      var selectBackground = "#afe9c77f".uiColor
      var selectEndBackground = "#4bce817f".uiColor
      var todayText = "#32cd32".uiColor
      var titleText = "#555555".uiColor
      var subtitleText = "#555555".uiColor
      var selectTodayText = "#32cd32".uiColor
      var selectTitleText = "#ffffff".uiColor
      var selectSubtitleText = "#ffffff".uiColor
      var failureTitleText = "#a9a9a9".uiColor
      var failureSubtitleText = "#a9a9a9".uiColor
      var failureBackground = "#dcdcdc32".uiColor
    }

    var color = CLColor()
    var selectBegin: Date?
    var selectEnd: Date?
    var limitMonth = 12
    var type = CLCalendarType.today
    var selectType = CLSelectType.area
    var touchType: CLTouchType = [.today, .past]
    var isShowLunarCalendar = true
    var insetsLayoutMarginsFromSafeArea = true
    var headerHight = 50.0
}
</code></pre>
<h1 id="总结">总结</h1>
<p>定制化开发请自行参考CLDemo修改 , 如果喜欢,欢迎star。</p>
<h1 id="参考资料">参考资料</h1>
<ol>
<li>
<p>DateToolsSwift</p>
</li>
<li>
<p>UICollectionView:装饰视图 Decoration View</p>
</li>
<li>
<p>使用UIStackView来简化iOS的界面布局</p>
</li>
</ol>


</div>
<div id="MySignature" role="contentinfo">
    <div>作者:JmoVxia</div>
<div>出处:http://www.cnblogs.com/JmoVxia/
</div>
本文版权归作者所有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。<br><br>
来源:https://www.cnblogs.com/JmoVxia/p/16822128.html
頁: [1]
查看完整版本: iOS开发之自定义日历控件