1. 程式人生 > >swift 中 as、as!、as? 這三種類型轉換操作符的異同 及一些控制元件用法

swift 中 as、as!、as? 這三種類型轉換操作符的異同 及一些控制元件用法

轉自: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)效果圖     原文:Swift - 多列表格元件的實現(樣例2:帶排序功能)      原文:Swift - 多列表格元件的實現(樣例2:帶排序功能) 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
    }
}

在這個文字標籤上長按後便可以複製其內容:

原文:Swift - UIPasteboard剪貼簿的使用詳解(複製、貼上文字和圖片)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()
    }
}

長按某個單元格即可複製這個單元格內容: