如何用 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( ...
随机推荐
- C++时间获取
http://net.pku.edu.cn/~yhf/linux_c/function/04.html asctime(将时间和日期以字符串格式表示) 相关函数 time,ctime,gmtime ...
- Centos rpm缺少依赖无法安装mysql5.5
rpm -ivh mysql-5.5.22-2.1.i386.rpm --nodeps --force 缺少依赖导致rpm -ivh mysql-5.5.22-2.1.i386.rpm命令无法安装!
- 利用fitnesse实现api接口自动化测试
上午在园子里乱逛,看了不少小伙伴们分享的接口测试方面的知识,仔细想想,我做接口测试也有几个年头了,大家所叙述到的一些经验或多或少,我也曾遇到过,突然意识到知识的点滴积累是多么的重要,我记得我最早接触接 ...
- [LeetCode]题解(python):109-Convert Sorted List to Binary Search Tree
题目来源: https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/ 题意分析: 给定一个排好序的链表,将这个链 ...
- [LeetCode]题解(python):070-Climbing Stairs
题目来源: https://leetcode.com/problems/climbing-stairs/ 题意分析: 爬楼梯,一次可以爬一步或者两步.如果要爬n层,问一共有多少种爬法.比如说,如果是3 ...
- IOS 学习笔记(5) 控件 文本视图(UITextView)的使用方法
相对于UILabell所支持的较短文本内容,UITextView对于长文本的支持更好.UITextView能够以滚动的方式全部浏览到长文本,并且就像UILabel那样,从ISO6,他也提供了对NSAt ...
- Delphi中的“委托”
.NET中有委托(Delegate)的概念,其声明形式如下所示: public delegate void MyDelegate(int aIntParam, string aStringPa ...
- 没有开发者账号,如何解锁wp8设备
原文 http://www.cnblogs.com/vsdot/p/3263454.html 问题的引入 好了,问题的由来是这样的,我想把我开发的wp8手机部署到手机上,可是竟然出现了下面的问题: [ ...
- viewpager的layout_width="wrap_content"无效问题
在viewpager当中直接使用layout_width="wrap_content"是无效的,扩展了一下.解决这个问题. package com.soulagou.ui; imp ...
- 基于Visual C++2013拆解世界五百强面试题--题17-程序结果分析1
分析程序结果,分析过程我们就写在程序注释里面. 写出下列代码的输出内容 #include <stdio.h> int inc(int a) { return (++a); } int mu ...