如何用 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( ...
随机推荐
- ZOJ3689 Digging(01背包)
#include <iostream> #include <cstdio> #include<cmath> #include<algorithm> #i ...
- CentOS下成功挂载xxxxxDVDx.iso并使用yum安装软件
CentOS下成功挂载xxxxxDVDx.iso并使用yum安装软件 **不断尝试,终能到达彼岸** 测试环境为Win7 32位,VirtualBOx4.2.16+CentOS6.5,可分别到virt ...
- JavaScript的深度克隆
1.JavaScript的五种基本数据类型: Number.String.Boolean.null.undefined. 2.typeof返回的六种数据类型: Number.String.Boolea ...
- 一个页面从输入URL到页面加载显示完成的详细过程
下面以访问baidu页面来做一个过程分析 输入 URL:http://www.baidu.com DNS 域名解析 计算机无法识别域名,计算机与计算机之间要想进行通信,必须通过ip地址用来定位该计算机 ...
- [Swust OJ 409]--小鼠迷宫问题(BFS+记忆化搜索)
题目链接:http://acm.swust.edu.cn/problem/409/ Time limit(ms): 1000 Memory limit(kb): 65535 Description ...
- html的显示消息和留言板
<div class="inner_content"> <c:forEach items="${notices}" var="n&q ...
- Android 开发笔记“关闭默认键盘”
1.打开AndroidManifest.xml文件 2.在对应的activity中增加配置信息 android:windowSoftInputMode="stateHidden"
- python函数abs()
详解: 返回绝对值 参数可以是:负数.正数.浮点数或者长整形 实例: abs(-1.2) #返回 1.2 abs(1.2) #返回 1.2 abs(-11216.5) #返回 11216.5 abs( ...
- grunt切换下载源
nrm 是一个 NPM 源管理器,允许你快速地在NPM 源间切换: 安装:npm install -g nrm 列出可选源:nrm ls 切换:nrm use taobao 测试所有源连接时间:nrm ...
- Qt的Model/View Framework解析(数据是从真正的“肉(raw)”里取得,Model提供肉,所以读写文件、操作数据库、网络通讯等一系列与数据打交道的工作就在model中做了)
最近在看Qt的Model/View Framework,在网上搜了搜,好像中文的除了几篇翻译没有什么有价值的文章.E文的除了Qt的官方介绍,其它文章也很少.看到一个老外在blog中写道Model/Vi ...