转自:http://www.111cn.net/sj/iOS/104115.htm
应网友要求,我这里总结了下 as、as!、as? 这三种类型转换操作符的异同,以及各自的使用场景。
1,as使用场合
(1)从派生类转换为基类,向上转型(upcasts)
class Animal {} class Cat: Animal {} let cat = Cat() let animal = cat as Animal
(2)消除二义性,数值类型转换
let num1 = 42 as CGFloat let num2 = 42 as Int let num3 = 42.5 as Int let num4 = (42 / 2) as Double
(3)switch 语句中进行模式匹配
如果不知道一个对象是什么类型,你可以通过switch语法检测它的类型,并且尝试在不同的情况下使用对应的类型进行相应的处理。
switch animal { case let cat as Cat: print("如果是Cat类型对象,则做相应处理") case let dog as Dog: print("如果是Dog类型对象,则做相应处理") default: break }
2,as!使用场合
向下转型(Downcasting)时使用。由于是强制类型转换,如果转换失败会报 runtime 运行错误。
class Animal {} class Cat: Animal {} let animal :Animal = Cat() let cat = animal as! Cat
3,as?使用场合
as? 和 as! 操作符的转换规则完全一样。但 as? 如果转换不成功的时候便会返回一个 nil 对象。成功的话返回可选类型值(optional),需要我们拆包使用。 由于 as? 在转换失败的时候也不会出现错误,所以对于如果能确保100%会成功的转换则可使用 as!,否则使用 as?
let animal:Animal = Cat() if let cat = animal as? Cat{ print("cat is not nil") } else { print("cat is nil") }
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1,支持排序的多列表格(multi-column sortable table control)效果图 2,功能说明: (1)表格列头文字增加下划线样式,表示可以点击。 (2)点击列头标题,内容条目便会根据该列数据进行排序显示(先升序、后降序,依次交替) (3)排序列背景色会变为蓝色,同时列头会显示上下箭头表示排列顺序。
(4)这里排序的上下箭头不是图片,而是使用 Font Awesome 图标字体库。优点是可以很轻松地设置颜色和大小,而不会失真
3,项目代码 (代码中高亮部分表示新增的排序相关的代码)
--- UICollectionGridViewController.swift(组件类) ---- UICollectionGridViewController.swift(组件类) ---
import Foundation import UIKit //表格排序协议 protocol UICollectionGridViewSortDelegate { func sort(colIndex: Int, asc: Bool, rows: [[AnyObject]]) -> [[AnyObject]] } //多列表格组件(通过CollectionView实现) class UICollectionGridViewController: UICollectionViewController { //表头数据 var cols: [String]! = [] //行数据 var rows: [[AnyObject]]! = [] //排序代理 var sortDelegate: UICollectionGridViewSortDelegate! //选中的表格列(-1表示没有选中的) private var selectedColIdx = -1 //列排序顺序 private var asc = true //单元格内容居左时的左侧内边距 private var cellPaddingLeft:CGFloat = 5 init() { //初始化表格布局 let layout = UICollectionGridViewLayout() super.init(collectionViewLayout: layout) layout.viewController = self collectionView!.backgroundColor = UIColor.whiteColor() collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell") collectionView!.delegate = self collectionView!.dataSource = self collectionView!.directionalLockEnabled = true collectionView!.contentInset = UIEdgeInsetsMake(0, 10, 0, 10) collectionView!.bounces = false } required init?(coder aDecoder: NSCoder) { fatalError("UICollectionGridViewController.init(coder:) has not been implemented") } //设置列头数据 func setColumns(columns: [String]) { cols = columns } //添加行数据 func addRow(row: [AnyObject]) { rows.append(row) collectionView!.collectionViewLayout.invalidateLayout() collectionView!.reloadData() } override func viewDidLoad() { super.viewDidLoad() } override func viewDidLayoutSubviews() { collectionView!.frame = CGRectMake(0, 0, view.frame.width, view.frame.height) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } //返回表格总行数 override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { if cols.isEmpty { return 0 } //总行数是:记录数+1个表头 return rows.count + 1 } //返回表格的列数 override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return cols.count } //单元格内容创建 override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell //单元格边框 cell.layer.borderWidth = 1 cell.backgroundColor = UIColor.whiteColor() cell.clipsToBounds = true //先清空内部原有的元素 for subview in cell.subviews { subview.removeFromSuperview() } //添加内容标签 let label = UILabel(frame: CGRectMake(0, 0, cell.frame.width, cell.frame.height)) //第一列的内容左对齐,其它列内容居中 if indexPath.row != 0 { label.textAlignment = NSTextAlignment.Center }else { label.textAlignment = NSTextAlignment.Left label.frame.origin.x = cellPaddingLeft } //设置列头单元格,内容单元格的数据 if indexPath.section == 0 { let text = NSAttributedString(string: cols[indexPath.row], attributes: [ NSUnderlineStyleAttributeName:NSUnderlineStyle.StyleSingle.rawValue, NSFontAttributeName:UIFont.boldSystemFontOfSize(15) ]) label.attributedText = text } else { label.font = UIFont.systemFontOfSize(15) label.text = "\(rows[indexPath.section-1][indexPath.row])" } cell.addSubview(label) //列排序 if indexPath.row == selectedColIdx { //排序列的单元格背景会变色 cell.backgroundColor = UIColor(red: 122/255, green: 186/255, blue: 255/255, alpha: 1) //排序列列头显示升序降序图标,并调整列头标签相关位置 if indexPath.section == 0 { let imageWidth: CGFloat = 14 let imageHeight: CGFloat = 14 let labelHeight = label.frame.height label.sizeToFit() label.frame = CGRectMake(cellPaddingLeft, 0, min(label.frame.width, cell.frame.width - imageWidth), labelHeight) let iconType = asc ? FAType.FALongArrowUp : FAType.FALongArrowDown let imageView = UIImageView() imageView.frame = CGRectMake(label.frame.width+3, cell.frame.height/2 - imageHeight/2, imageWidth, imageHeight) imageView.setFAIconWithName(iconType, textColor: UIColor.blueColor()) cell.addSubview(imageView) } } return cell } //单元格选中事件 override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { //打印出点击单元格的[行,列]坐标 print("点击单元格的[行,列]坐标: [\(indexPath.section),\(indexPath.row)]") if indexPath.section == 0 && sortDelegate != nil { //如果点击的是表头单元格,则默认该列升序排列,再次点击则变降序排列,以此交替 asc = (selectedColIdx != indexPath.row) ? true : !asc selectedColIdx = indexPath.row rows = sortDelegate.sort(indexPath.row, asc: asc, rows: rows) collectionView.reloadData() } } }
--- UICollectionGridViewLayout.swift(布局类) ---
import Foundation import UIKit //多列表格组件布局类 class UICollectionGridViewLayout: UICollectionViewLayout { private var itemAttributes: [[UICollectionViewLayoutAttributes]] = [] private var itemsSize: [NSValue] = [] private var contentSize: CGSize = CGSizeZero //表格组件视图控制器 var viewController: UICollectionGridViewController! //准备所有view的layoutAttribute信息 override func prepareLayout() { if collectionView!.numberOfSections() == 0 { return } var column = 0 var xOffset: CGFloat = 0 var yOffset: CGFloat = 0 var contentWidth: CGFloat = 0 var contentHeight: CGFloat = 0 if itemAttributes.count > 0 { for var section = 0; section < collectionView?.numberOfSections(); section++ { let numberOfItems = collectionView?.numberOfItemsInSection(section) for var index = 0; index < numberOfItems; index++ { if section != 0 && index != 0 { continue } let attributes = layoutAttributesForItemAtIndexPath( NSIndexPath(forItem: index, inSection: section))! if section == 0 { var frame = attributes.frame frame.origin.y = collectionView!.contentOffset.y attributes.frame = frame } } } return } itemAttributes = [] itemsSize = [] if itemsSize.count != viewController.cols.count { calculateItemsSize() } for var section = 0; section < collectionView?.numberOfSections(); section++ { var sectionAttributes: [UICollectionViewLayoutAttributes] = [] for var index = 0; index < viewController.cols.count; index++ { let itemSize = itemsSize[index].CGSizeValue() let indexPath = NSIndexPath(forItem: index, inSection: section) let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath) //除第一列,其它列位置都左移一个像素,防止左右单元格间显示两条边框线 if index == 0{ attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height)) }else { attributes.frame = CGRectIntegral(CGRectMake(xOffset-1, yOffset, itemSize.width+1, itemSize.height)) } if section == 0 && index == 0 { attributes.zIndex = 1024 } else if section == 0 || index == 0 { attributes.zIndex = 1023 } if section == 0 { var frame = attributes.frame frame.origin.y = collectionView!.contentOffset.y attributes.frame = frame } sectionAttributes.append(attributes) xOffset = xOffset+itemSize.width column++ if column == viewController.cols.count { if xOffset > contentWidth { contentWidth = xOffset } column = 0 xOffset = 0 yOffset += itemSize.height } } itemAttributes.append(sectionAttributes) } let attributes = itemAttributes.last!.last! as UICollectionViewLayoutAttributes contentHeight = attributes.frame.origin.y + attributes.frame.size.height contentSize = CGSizeMake(contentWidth, contentHeight) } //需要更新layout时调用 override func invalidateLayout() { itemAttributes = [] itemsSize = [] contentSize = CGSizeZero super.invalidateLayout() } // 返回内容区域总大小,不是可见区域 override func collectionViewContentSize() -> CGSize { return contentSize } // 这个方法返回每个单元格的位置和大小 override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { return itemAttributes[indexPath.section][indexPath.row] } // 返回所有单元格位置属性 override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var attributes: [UICollectionViewLayoutAttributes] = [] for section in itemAttributes { attributes.appendContentsOf(section.filter( {(includeElement: UICollectionViewLayoutAttributes) -> Bool in return CGRectIntersectsRect(rect, includeElement.frame) })) } return attributes } //当边界发生改变时,是否应该刷新布局。 //本例在宽度变化时,将重新计算需要的布局信息。 override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool { let oldBounds = self.collectionView?.bounds if CGRectGetWidth(oldBounds!) != CGRectGetWidth(newBounds) { return true }else { return false } } //计算所有单元格的尺寸(每一列各一个单元格) func calculateItemsSize() { var remainingWidth = collectionView!.frame.width - collectionView!.contentInset.left - collectionView!.contentInset.right for var index = viewController.cols.count-1; index >= 0; index-- { let newItemSize = sizeForItemWithColumnIndex(index, remainingWidth: remainingWidth) remainingWidth -= newItemSize.width let newItemSizeValue = NSValue(CGSize: newItemSize) //由于遍历列的时候是从尾部开始遍历了,因此将结果插入数组的时候都是放人第一个位置 itemsSize.insert(newItemSizeValue, atIndex: 0) } } //计算某一列的单元格尺寸 func sizeForItemWithColumnIndex(columnIndex: Int, remainingWidth: CGFloat) -> CGSize { let columnString = viewController.cols[columnIndex] //根据列头标题文件,估算各列的宽度 let size = NSString(string: columnString).sizeWithAttributes([ NSFontAttributeName:UIFont.systemFontOfSize(15), NSUnderlineStyleAttributeName:NSUnderlineStyle.StyleSingle.rawValue ]) //如果有剩余的空间则都给第一列 if columnIndex == 0 { return CGSizeMake(max(remainingWidth, size.width + 17), size.height + 10) } //行高增加10像素,列宽增加17像素(为了容纳下排序图表) return CGSizeMake(size.width + 17, size.height + 10) } }
--- ViewController.swift(测试类) ---
import UIKit class ViewController: UIViewController, UICollectionGridViewSortDelegate { var gridViewController: UICollectionGridViewController! override func viewDidLoad() { super.viewDidLoad() gridViewController = UICollectionGridViewController() gridViewController.setColumns(["客户", "消费金额", "消费次数", "满意度"]) gridViewController.addRow(["hangge", "100", "8", "60%"]) gridViewController.addRow(["张三", "223", "16", "81%"]) gridViewController.addRow(["李四", "143", "25", "93%"]) gridViewController.addRow(["王五", "75", "2", "53%"]) gridViewController.addRow(["韩梅梅", "43", "12", "33%"]) gridViewController.addRow(["李雷", "33", "27", "45%"]) gridViewController.addRow(["王大力", "33", "22", "15%"]) gridViewController.sortDelegate = self view.addSubview(gridViewController.view) } override func viewDidLayoutSubviews() { gridViewController.view.frame = CGRectMake(0, 50, view.frame.width, view.frame.height-60) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } //表格排序函数 func sort(colIndex: Int, asc: Bool, rows: [[AnyObject]]) -> [[AnyObject]] { let sortedRows = rows.sort { (firstRow: [AnyObject], secondRow: [AnyObject]) -> Bool in let firstRowValue = firstRow[colIndex] as! String let secondRowValue = secondRow[colIndex] as! String if colIndex == 0 { //首例姓名使用字典排序法 if asc { return firstRowValue < secondRowValue } return firstRowValue > secondRowValue } else if colIndex == 1 || colIndex == 2 { //中间两列使用数字排序 if asc { return Int(firstRowValue)! < Int(secondRowValue)! } return Int(firstRowValue)! > Int(secondRowValue)! } //最后一列数据先去掉百分号,再转成数字比较 let firstRowValuePercent = Int(firstRowValue.substringToIndex( firstRowValue.endIndex.advancedBy(-1))) let secondRowValuePercent = Int(secondRowValue.substringToIndex( secondRowValue.endIndex.advancedBy(-1))) if asc { return firstRowValuePercent < secondRowValuePercent } return firstRowValuePercent > secondRowValuePercent } return sortedRows } }
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
UITextField、UITextView组件系统原生就支持文字的复制,但有时我们需要让其他的一些组件也能实现复制功能,比如点击复制UILabel上的文字、UIImageView中的图片、UITableView里单元格的内容、或者点击按钮把文字或图片自动复制到粘贴板中等等。 这些我们借助 UIPasteboard 就可以实现。
一,将内容写入到剪贴板中
1,复制字符串
UIPasteboard.generalPasteboard().string = "欢迎访问 hangge.com"
2,复制字符串数组
UIPasteboard.generalPasteboard().strings = ["hellow", "hangge.com"]
3,复制图片
let image = UIImage(named: "logo.png") UIPasteboard.generalPasteboard().image = image
4,复制二进制数据(NSData)
let path = NSBundle.mainBundle().pathForResource("logo", ofType: "png")! let fileData = NSData(contentsOfFile: path)! UIPasteboard.generalPasteboard().setData(fileData, forPasteboardType: "public.png")
注:从剪贴板获取二进制数据(NSData)
let myData = UIPasteboard.generalPasteboard().dataForPasteboardType("public.png")
二,常见组件增加复制功能
1,让文本标签(UILabel)支持复制功能
我们自定义一个可复制的标签类 UICopyLabel(继承UILabel),其内部能响应 Touch 事件并显示复制菜单
import UIKit class UICopyLabel: UILabel { override init(frame: CGRect) { super.init(frame: frame) sharedInit() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) sharedInit() } func sharedInit() { userInteractionEnabled = true addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: "showMenu:")) } func showMenu(sender: AnyObject?) { becomeFirstResponder() let menu = UIMenuController.sharedMenuController() if !menu.menuVisible { menu.setTargetRect(bounds, inView: self) menu.setMenuVisible(true, animated: true) } } //复制 override func copy(sender: AnyObject?) { let board = UIPasteboard.generalPasteboard() board.string = text let menu = UIMenuController.sharedMenuController() menu.setMenuVisible(false, animated: true) } override func canBecomeFirstResponder() -> Bool { return true } override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { if action == "copy:" { return true } return false } }
在这个文本标签上长按后便可以复制其内容:
2,让图片控件(UIImageView)支持复制、粘贴功能
我们自定义一个图片控件类 UICPImageView(继承UIImageView),内部同样添加Touch事件响应。该控件不仅支持复制,还支持粘贴。
import UIKit class UICPImageView: UIImageView { override init(frame: CGRect) { super.init(frame: frame) sharedInit() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) sharedInit() } func sharedInit() { userInteractionEnabled = true addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: "showMenu:")) } func showMenu(sender: AnyObject?) { becomeFirstResponder() let menu = UIMenuController.sharedMenuController() if !menu.menuVisible { menu.setTargetRect(bounds, inView: self) menu.setMenuVisible(true, animated: true) } } //复制 override func copy(sender: AnyObject?) { let board = UIPasteboard.generalPasteboard() board.image = self.image let menu = UIMenuController.sharedMenuController() menu.setMenuVisible(false, animated: true) } //粘贴 override func paste(sender: AnyObject?) { let board = UIPasteboard.generalPasteboard() self.image = board.image let menu = UIMenuController.sharedMenuController() menu.setMenuVisible(false, animated: true) } override func canBecomeFirstResponder() -> Bool { return true } override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool { if action == "copy:" { return true }else if action == "paste:" { return true } return false } }
下面我们在界面上添加两个 UICPImageView,我们可以把左边控件里的图片复制到右边控件中来,效果图如下:
3,让表格(UITableView)支持复制功能
import UIKit class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { var tableView:UITableView? var tableData = ["条目1", "条目2", "条目3", "条目4", "条目5", "条目6", "条目7"] override func loadView() { super.loadView() } override func viewDidLoad() { super.viewDidLoad() //创建表视图 self.tableView = UITableView(frame: self.view.frame, style:.Plain) self.tableView!.delegate = self self.tableView!.dataSource = self //创建一个重用的单元格 self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "SwiftCell") self.view.addSubview(self.tableView!) } func tableView(tableView: UITableView, performAction action: Selector, forRowAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) { let board = UIPasteboard.generalPasteboard() board.string = tableData[indexPath.row] } func tableView(tableView: UITableView, canPerformAction action: Selector, forRowAtIndexPath indexPath: NSIndexPath, withSender sender: AnyObject?) -> Bool { if action == "copy:" { return true } return false } func tableView(tableView: UITableView, shouldShowMenuForRowAtIndexPath indexPath: NSIndexPath) -> Bool { return true } //在本例中,只有一个分区 func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1; } //返回表格行数(也就是返回控件数) func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData.count } //创建各单元显示内容(创建参数indexPath指定的单元) func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { //为了提供表格显示性能,已创建完成的单元需重复使用 let identify:String = "SwiftCell" //同一形式的单元格重复使用,在声明时已注册 let cell = tableView.dequeueReusableCellWithIdentifier(identify, forIndexPath: indexPath) as UITableViewCell cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator cell.textLabel?.text = tableData[indexPath.row] return cell } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
长按某个单元格即可复制这个单元格内容:
