如何用 Swift 语言构建一个自定控件


- import UIKit
- class ViewController: UIViewController {
- let rangeSlider = RangeSlider(frame: CGRectZero)
- override func viewDidLoad() {
- super.viewDidLoad()
- rangeSlider.backgroundColor = UIColor.redColor()
- view.addSubview(rangeSlider)
- }
- override func viewDidLayoutSubviews() {
- let margin: CGFloat = 20.0
- let width = view.bounds.width - 2.0 * margin
- rangeSlider.frame = CGRect(x: margin, y: margin + topLayoutGuide.length,
- width: width, height: 31.0)
- }
- }

- import UIKit
- class RangeSlider: UIControl {
- var minimumValue = 0.0
- var maximumValue = 1.0
- var lowerValue = 0.2
- var upperValue = 0.8
- }
- import QuartzCore
- let trackLayer = CALayer()
- let lowerThumbLayer = CALayer()
- let upperThumbLayer = CALayer()
- var thumbWidth: CGFloat {
- return CGFloat(bounds.height)
- }
- override init(frame: CGRect) {
- super.init(frame: frame)
- trackLayer.backgroundColor = UIColor.blueColor().CGColor
- layer.addSublayer(trackLayer)
- lowerThumbLayer.backgroundColor = UIColor.greenColor().CGColor
- layer.addSublayer(lowerThumbLayer)
- upperThumbLayer.backgroundColor = UIColor.greenColor().CGColor
- layer.addSublayer(upperThumbLayer)
- updateLayerFrames()
- }
- required init(coder: NSCoder) {
- super.init(coder: coder)
- }
- func updateLayerFrames() {
- trackLayer.frame = bounds.rectByInsetting(dx: 0.0, dy: bounds.height / 3)
- trackLayer.setNeedsDisplay()
- let lowerThumbCenter = CGFloat(positionForValue(lowerValue))
- lowerThumbLayer.frame = CGRect(x: lowerThumbCenter - thumbWidth / 2.0, y: 0.0,
- width: thumbWidth, height: thumbWidth)
- lowerThumbLayer.setNeedsDisplay()
- let upperThumbCenter = CGFloat(positionForValue(upperValue))
- upperThumbLayer.frame = CGRect(x: upperThumbCenter - thumbWidth / 2.0, y: 0.0,
- width: thumbWidth, height: thumbWidth)
- upperThumbLayer.setNeedsDisplay()
- }
- func positionForValue(value: Double) -> Double {
- let widthDouble = Double(thumbWidth)
- return Double(bounds.width - thumbWidth) * (value - minimumValue) /
- (maximumValue - minimumValue) + Double(thumbWidth / 2.0)
- }
- override var frame: CGRect {
- didSet {
- updateLayerFrames()
- }
- }

- import UIKit
- import QuartzCore
- class RangeSliderThumbLayer: CALayer {
- var highlighted = false
- weak var rangeSlider: RangeSlider?
- }
- let lowerThumbLayer = RangeSliderThumbLayer()
- let upperThumbLayer = RangeSliderThumbLayer()
- lowerThumbLayer.rangeSlider = self
- upperThumbLayer.rangeSlider = self
- var previousLocation = CGPoint()
- override func beginTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) -> Bool {
- previousLocation = touch.locationInView(self)
- // Hit test the thumb layers
- if lowerThumbLayer.frame.contains(previousLocation) {
- lowerThumbLayer.highlighted = true
- } else if upperThumbLayer.frame.contains(previousLocation) {
- upperThumbLayer.highlighted = true
- }
- return lowerThumbLayer.highlighted || upperThumbLayer.highlighted
- }
- func boundValue(value: Double, toLowerValue lowerValue: Double, upperValue: Double) -> Double {
- return min(max(value, lowerValue), upperValue)
- }
- override func continueTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) -> Bool {
- let location = touch.locationInView(self)
- // 1. Determine by how much the user has dragged
- let deltaLocation = Double(location.x - previousLocation.x)
- let deltaValue = (maximumValue - minimumValue) * deltaLocation / Double(bounds.width - bounds.height)
- previousLocation = location
- // 2. Update the values
- if lowerThumbLayer.highlighted {
- lowerValue += deltaValue
- lowerValue = boundValue(lowerValue, toLowerValue: minimumValue, upperValue: upperValue)
- } else if upperThumbLayer.highlighted {
- upperValue += deltaValue
- upperValue = boundValue(upperValue, toLowerValue: lowerValue, upperValue: maximumValue)
- }
- // 3. Update the UI
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- updateLayerFrames()
- CATransaction.commit()
- return true
- }
- override func endTrackingWithTouch(touch: UITouch!, withEvent event: UIEvent!) {
- lowerThumbLayer.highlighted = false
- upperThumbLayer.highlighted = false
- }

- sendActionsForControlEvents(.ValueChanged)
- rangeSlider.addTarget(self, action: "rangeSliderValueChanged:", forControlEvents: .ValueChanged)
- func rangeSliderValueChanged(rangeSlider: RangeSlider) {
- println("Range slider value changed: (\(rangeSlider.lowerValue) \(rangeSlider.upperValue))")
- }
- Range slider value changed: (0.117670682730924 0.390361445783134)
- Range slider value changed: (0.117670682730924 0.38835341365462)
- Range slider value changed: (0.117670682730924 0.382329317269078)
- Range slider value changed: (0.117670682730924 0.380321285140564)
- Range slider value changed: (0.119678714859438 0.380321285140564)
- Range slider value changed: (0.121686746987952 0.380321285140564)
- import UIKit
- import QuartzCore
- class RangeSliderTrackLayer: CALayer {
- weak var rangeSlider: RangeSlider?
- }
- let trackLayer = RangeSliderTrackLayer()
- init(frame: CGRect) {
- super.init(frame: frame)
- trackLayer.rangeSlider = self
- trackLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(trackLayer)
- lowerThumbLayer.rangeSlider = self
- lowerThumbLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(lowerThumbLayer)
- upperThumbLayer.rangeSlider = self
- upperThumbLayer.contentsScale = UIScreen.mainScreen().scale
- layer.addSublayer(upperThumbLayer)
- }
- rangeSlider.backgroundColor = UIColor.redColor()

- var trackTintColor = UIColor(white: 0.9, alpha: 1.0)
- var trackHighlightTintColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0)
- var thumbTintColor = UIColor.whiteColor()
- var curvaceousness : CGFloat = 1.0
- override func drawInContext(ctx: CGContext!) {
- if let slider = rangeSlider {
- // Clip
- let cornerRadius = bounds.height * slider.curvaceousness / 2.0
- let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
- CGContextAddPath(ctx, path.CGPath)
- // Fill the track
- CGContextSetFillColorWithColor(ctx, slider.trackTintColor.CGColor)
- CGContextAddPath(ctx, path.CGPath)
- CGContextFillPath(ctx)
- // Fill the highlighted range
- CGContextSetFillColorWithColor(ctx, slider.trackHighlightTintColor.CGColor)
- let lowerValuePosition = CGFloat(slider.positionForValue(slider.lowerValue))
- let upperValuePosition = CGFloat(slider.positionForValue(slider.upperValue))
- let rect = CGRect(x: lowerValuePosition, y: 0.0, width: upperValuePosition - lowerValuePosition, height: bounds.height)
- CGContextFillRect(ctx, rect)
- }
- }

- override func drawInContext(ctx: CGContext!) {
- if let slider = rangeSlider {
- let thumbFrame = bounds.rectByInsetting(dx: 2.0, dy: 2.0)
- let cornerRadius = thumbFrame.height * slider.curvaceousness / 2.0
- let thumbPath = UIBezierPath(roundedRect: thumbFrame, cornerRadius: cornerRadius)
- // Fill - with a subtle shadow
- let shadowColor = UIColor.grayColor()
- CGContextSetShadowWithColor(ctx, CGSize(width: 0.0, height: 1.0), 1.0, shadowColor.CGColor)
- CGContextSetFillColorWithColor(ctx, slider.thumbTintColor.CGColor)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextFillPath(ctx)
- // Outline
- CGContextSetStrokeColorWithColor(ctx, shadowColor.CGColor)
- CGContextSetLineWidth(ctx, 0.5)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextStrokePath(ctx)
- if highlighted {
- CGContextSetFillColorWithColor(ctx, UIColor(white: 0.0, alpha: 0.1).CGColor)
- CGContextAddPath(ctx, thumbPath.CGPath)
- CGContextFillPath(ctx)
- }
- }
- }
- var highlighted: Bool = false {
- didSet {
- setNeedsDisplay()
- }
- }

- var minimumValue: Double = 0.0 {
- didSet {
- updateLayerFrames()
- }
- }
- var maximumValue: Double = 1.0 {
- didSet {
- updateLayerFrames()
- }
- }
- var lowerValue: Double = 0.2 {
- didSet {
- updateLayerFrames()
- }
- }
- var upperValue: Double = 0.8 {
- didSet {
- updateLayerFrames()
- }
- }
- var trackTintColor: UIColor = UIColor(white: 0.9, alpha: 1.0) {
- didSet {
- trackLayer.setNeedsDisplay()
- }
- }
- var trackHighlightTintColor: UIColor = UIColor(red: 0.0, green: 0.45, blue: 0.94, alpha: 1.0) {
- didSet {
- trackLayer.setNeedsDisplay()
- }
- }
- var thumbTintColor: UIColor = UIColor.whiteColor() {
- didSet {
- lowerThumbLayer.setNeedsDisplay()
- upperThumbLayer.setNeedsDisplay()
- }
- }
- var curvaceousness: CGFloat = 1.0 {
- didSet {
- trackLayer.setNeedsDisplay()
- lowerThumbLayer.setNeedsDisplay()
- upperThumbLayer.setNeedsDisplay()
- }
- }
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- CATransaction.commit()
- // 3. Update the UI
- CATransaction.begin()
- CATransaction.setDisableActions(true)
- updateLayerFrames()
- CATransaction.commit()
- let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC))
- dispatch_after(time, dispatch_get_main_queue()) {
- self.rangeSlider.trackHighlightTintColor = UIColor.redColor()
- self.rangeSlider.curvaceousness = 0.0
- }


如何用 Swift 语言构建一个自定控件的更多相关文章
- 前端每日实战:158# 视频演示如何用纯 CSS 创作一个雨伞 toggle 控件
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/pxLbjv 可交互视频 此视频是可 ...
- 前端每日实战:145# 视频演示如何用纯 CSS 创作一个电源开关控件
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/PdMyJd 可交互视频 此视频是可 ...
- Pro Android 4 第六章 构建用户界面以及使用控件(一)
目前为止,我们已经介绍了android的基础内容,但是还没开始接触用户界面(UI).本章我们将开始探讨用户界面和控件.我们先讨论一下android中UI设计的一般原理,然后我们在介绍一下an ...
- WPF系列 自定控件
引言 WPF中微软提供了一些基本的控件,但是工作中这些基础的控件往往不能满足我们的需求,这个时候我们就需要根据实际的需求去开发自己的控件,但要注意不是所有功能不满足的情况都需要通过自定义控件来实现.实 ...
- Qt 创建一个QtDesinger第三方控件
1.需要创建一个合适的.pro文件 2.创建一个继承QDesignerCustomWidgetInterface的类,描述控件的一些属性. 函数 描述和返回值 name() 指定控件的名称 group ...
- 【React】开发一个城市选择控件
想到做这个,是因为无意中在github上看到了这一个仓库https://github.com/lunlunshiwo/ChooseCity,做的就是一个城市选择控件,是用vue写的,说的是阿里的一道题 ...
- android中一个评分的控件
RatingBar android中一个评分的控件 如何使用 Android Studio下: dependencies { compile 'com.hedgehog.ratingbar:app:1 ...
- 【WPF学习】第六十四章 构建基本的用户控件
创建一个简单用户控件是开始自定义控件的好方法.本章主要介绍创建一个基本的颜色拾取器.接下来分析如何将这个控件分解成功能更强大的基于模板的控件. 创建基本的颜色拾取器很容易.然而,创建自定义颜色拾取器仍 ...
- C++ 一个程序获取另一个程序Edit控件的内容
//一个程序获取另一个程序Edit控件的内容 //根据指定程序的标题名获取改程序窗口的句柄 HWND hWnd=::FindWindow(NULL,"zhang001"); if( ...
随机推荐
- SSIS 实例 从Ftp获取多个文件并对数据库进行增量更新。
整个流程 Step 1 放置一个FTP Task 将远程文件复制到本地 建立FTP链接管理器后 Is LocalPatchVariable 设置为Ture 并创建一个变量设置本地路径 Operatio ...
- 日志记录Filter
Filter也可以日志记录,在request 之前后, 该filter 使用Apache 日只记录工具,记录客户IP ,访问URI 以及消耗时间. LogFilter.java package com ...
- Lucence.Net学习+盘古分词
创建索引库 //读取文件,存储到索引库 public string CreateDatebase() { //获取索引库的路径 ...
- HDU2023-求平均成绩
描述: 假设一个班有n(n<=50)个学生,每人考m(m<=5)门课,求每个学生的平均成绩和每门课的平均成绩,并输出各科成绩均大于等于平均成绩的学生数量. 输入数据有多个测试实例,每个测试 ...
- ThinkPHP第十六天(redirect、join、视图模型)
1.redirect /** * Action跳转(URL重定向) 支持指定模块和延时跳转 * access protected * @param string $url 跳转的URL表达式 * @p ...
- jQuery事件函数bind,live,delegate的区别
DOM树 首先,可视化一个HMTL文档的DOM树是很有帮助的.一个简单的HTML页面看起来就像是这个样子: 事件冒泡(又称事件传播) 当我们点击一个链接时,其触发了链接元素的单击事件,该事件则引发任何 ...
- JAVA GUI学习 - JMenuBar菜单条、JMenu菜单、JMenuItem菜单项组件学习
public class MenuBarKnow extends JFrame { JMenuBar jMenuBar; JMenu jMenuFile,jMenuEditor,jMenuAbout; ...
- PHP自学之路-----javascript基础入门
Javascript概述: Javascript是基于对象和事件的脚本语言.特点; 1.安全性(不允许直接访问本地硬盘),它可以做的就是信息的动态交互. 2.跨平台性. JavaScript与HTML ...
- Android JNI入门第三篇——jni头文件分析
一. 首先写了java文件: public class HeaderFile { private native void doVoid(); native int doShort(); native ...
- Python 参数传递
python中的变量: 一个变量是局部还是全局,在编译函数的时候就已经决定,因此读变量值的时候也不会逐层向外查找.变量是全局还是局域,根据如下3条: 1. 如果函数内部有global语句,那么它声明的 ...