2017年12月27日 星期三

swift4 自定義layer框線(border)

今天分享自定義layer匡線,有時候在設計上可能會有框架框線顏色兩種或三種,
但原生的通常就是讓你簡單方便,一個框架一個顏色。

下面範例就是原生整個框架顏色,我直接使用ViewController的View來給予範例如下:
self.view.layer.borderColor = UIColor.blue.cgColor;

self.view.layer.borderWidth = 10.0;

但是有時可能會需要多個label拼成一個大的並且有框架之類的時候,
又剛好只有最外圈框架顏色不同就很麻煩了,
有些人可能會選擇偷懶做一個View覆蓋在上面讓backgroundcolor是透明的,
然後給予框線並符合原本物件所需大小。

今天這邊要跟大家分享的就是寫一個CALayer的擴充來達到目的,
我建立了幾個物件,一個UILabel、一個UIButton、一個UITextField、一個UIView來呈現效果。
首先我們要先寫一個擴充如下:

extension CALayer{
    func addBorder(edge:UIRectEdge, color:UIColor, thickness:CGFloat){
        
        let borders = CALayer()
        
        switch edge {
        case .top:
            borders.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness);
            break
        case .bottom:
            borders.frame = CGRect(x: 0, y: frame.height - thickness, width: frame.width, height: thickness);
        case .left:
            borders.frame = CGRect(x: 0, y: 0 + thickness, width: thickness, height: frame.height - thickness * 2);
        case .right:
            borders.frame = CGRect(x: frame.width - thickness, y: 0 + thickness, width: thickness, height: frame.height - thickness * 2);
        default:
            break
        }
        
        borders.backgroundColor = color.cgColor;
        
        self.addSublayer(borders);
    }
}

這個擴充東西並不多,其實很簡短,
有人會好奇為什麼有的我會-thickness甚至做成-thickness * 2原因很簡單,
這可以依照個人喜好去做,我會做-thickness * 2 是因為那是左右兩邊,
而我不希望他有被覆蓋或是覆蓋到上或下的邊框,
所以我剪去thickness的兩倍讓他高度是去除上下邊框的高度。
但前提是你的框架寬(高)度必須是一致的。

寫好擴充後就剪得了,接下來你只要做以下範例動作就能夠產生邊框了喔!

 V1.layer.addBorder(edge: .top, color: .red, thickness: 5.0);
 V1.layer.addBorder(edge: .bottom, color: .blue, thickness: 5.0);
 V1.layer.addBorder(edge: .left, color: .green, thickness: 5.0);
 V1.layer.addBorder(edge: .right, color: .yellow, thickness: 5.0);

 btn.layer.addBorder(edge: .top, color: .brown, thickness: 2.0);
 btn.layer.addBorder(edge: .bottom, color: .gray, thickness: 2.0);
 btn.layer.addBorder(edge: .left, color: .red, thickness: 2.0);
 btn.layer.addBorder(edge: .right, color: .blue, thickness: 2.0);

 label.layer.addBorder(edge: .top, color: .black, thickness: 2.0);
 label.layer.addBorder(edge: .bottom, color: .gray, thickness: 2.0);
 label.layer.addBorder(edge: .left, color: .purple, thickness: 2.0);
 label.layer.addBorder(edge: .right, color: .orange, thickness: 2.0);

 text.layer.addBorder(edge: .top, color: .red, thickness: 2.0);
 text.layer.addBorder(edge: .bottom, color: .cyan, thickness: 2.0);
 text.layer.addBorder(edge: .left, color: .magenta, thickness: 2.0);
 text.layer.addBorder(edge: .right, color: .darkGray, thickness: 2.0);

看起來很多,是因為這種寫法就變成你每一個框都要去寫一次,
畢竟你可能四邊顏色都不同之類的,
最後附上運行出來的成果圖如下:

希望能夠幫助到有需要的朋友們!

2017年11月13日 星期一

Swift 4 將常用到的顏色製作成色塊

      我相信很多人在開發的時候都跟我一樣遇到過物件要改變顏色時美編所給的色碼都必須另外寫一段RBG或其他的方法。
      但在Swift 4(iOS11 & xCode9)出來一個新的小功能,就是自定義色塊。
      這邊就點單的說該如何製作,首先建立一個新的專案,然後選到Assets,在Assets左邊欄下方的+按下去後有一個New Color Set點選下去,如下圖:
 然後直接點選預設色塊後設定自己要的顏色,並在最上方欄位命名。

這樣完成後就能使用UIColor(named: String);
簡單一點的範例如下,我這邊為了讓大家看到改變所以做了兩個UIButton


因為這只是簡單的測試給大家看,所以程式碼也非常簡單,有些地方可以不用去執著他。只要記得當你要使用那個顏色時呼叫UIColor(named: String);即可
程式碼如下:
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var BG2: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        BG2.tintColor = UIColor.white
        touch(Btn: BG2)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func BG1(_ sender: UIButton) {
        let BG1_Color = UIColor(named: "新增的色塊");
        self.view.backgroundColor = BG1_Color
        BG2.tintColor = UIColor.white
    }
    
    @IBAction func BG2(_ sender: UIButton) {
        let BG2_Color = UIColor(named: "BG2_Color");
        self.view.backgroundColor = BG2_Color
        BG2.tintColor = UIColor.yellow
    }
    
    func touch(Btn: UIButton){
        Btn.tintColor = UIColor.blue
    }
}


執行後結果如下圖:

希望大家會喜歡,並在必要的時候善用此功能^^

2017年9月22日 星期五

關於Swfi4 使用到#selector的一些小修正

近期Apple已經釋放出正式版Xcode9雖然可以向下相容 Swift3.2
但我相信很多人會使用Swift4來進行編譯

跟以往有點不同的是這次因為有向下相容,所以原本專案是Swift3.2的話Xcode9並不會主動幫你升級成Swift4必須自己手動去調整。這邊就附上圖片給大家看一下

選擇你的prohect > targets > 專案名稱 > Build Settings > 收尋swift languae version去更改成swift4.0

好了以上是一點點的題外話,這邊主要是要說如果你在swift4中有使用#selector
在selector我們在swift3中會使用
#selector(funcName)這樣會無法像以往一樣做最簡單的方式 func xxxx(){ ... }
這樣系統會出現警告
這時不用擔心只要一個小動作,就是在func的前面加上@objc警告就會消失了唷!或是可以直接點選警告後按下Fix系統會自動幫你在該func前面帶入@objc

引述Swift3使用#selector指定的方法,只有當private時需要加上@objc,現在全部都要加上@objc


2017年9月17日 星期日

SWIFT3 簡易電子時鐘 Date & Timer 運用


基於好奇加上有點時間所以就做了很簡單的電子時鐘,功能沒什麼就無聊多做了時間擷取的Button記錄按下當下的時間。

然後這顆按鈕還可以發揮其他功能,譬如按下後將時間等資料回傳資料庫等等...這些就不多說了。

介面方面很簡當,我使用了4個UILabel跟1個UIButton,畫面請看最後的範例圖摟。

因為code其實不是很多加上小弟已經在code中註解了,這邊就直接附上code給大家參考

import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var UserTimeLabel: UILabel!
    @IBOutlet weak var timerLabel: UILabel!
    
    var dateFormatter = DateFormatter()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        dateFormatter.dateStyle = .medium
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"//格式可以自定義例如可改成"yyyy/MM/dd-HH:mm:ss"
        //將時間轉換成字串(刻意多寫出來讓大家能夠清楚了解)
        let timeString = dateFormatter.string(from: Date(timeIntervalSinceNow: 0.0))
        timerLabel.text = timeString
        
        //時間的監聽(更新時間)
        Timer.scheduledTimer(timeInterval: 0.0, target: self, selector: #selector(ViewController.updateClock), userInfo: nil, repeats: true)
        
    }
    //更新時間
    func updateClock() {
        let now = Date()//獲取當前時間
        timerLabel.text = dateFormatter.string(from: now)
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    @IBAction func userBtn(_ sender: UIButton) {
        
        dateFormatter.dateStyle = .medium
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        UserTimeLabel.text = dateFormatter.string(from: Date(timeIntervalSinceNow: 0.0))
        
    }
    

}

以上code直接以模擬其執行出來的效果如下



希望能夠幫助到剛好有需要的朋友們^^

2017年8月28日 星期一

SlideMenuControllerSwift 套用分享(左邊欄)_ Swift3

今天要分享第三方套件的簡單運用,首先使用套件做個簡單教學。先至https://cocoapods.org/ 安裝cocoapods,懶惰的話可以直接下載他們的APP官方有附上抄作教學唷!很方便。

套件連結SlideMenuControllerSwift ,小弟很早以前就找到這個套件了,只是一直沒有去使用。剛好有空閒時間玩了一下。順便跟大家分享^^

首先storyboard要建立5個ViewController,並在左邊欄中加入一個UITableVew
storyboard的前置作業大致上就這樣(記得UITableView要設定delegatedataSource),還有一點要注意的是每個ViewController要記得給予Identity(storyboard ID)

接下來進入到code方面了,首先第一個要做的地方是appdelegate
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)加入以下程式碼(有些函數是擴充去寫的文中會提到)

 let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let mainViewController: MainViewController = storyboard.instantiateViewController(withIdentifier: "main") as! MainViewController
        let leftViewController: LeftViewController = storyboard.instantiateViewController(withIdentifier: "left") as! LeftViewController
        let rightViewController: RightViewController = storyboard.instantiateViewController(withIdentifier: "right") as! RightViewController
        
        let navigationController: UINavigationController = UINavigationController(rootViewController: mainViewController)
        
        leftViewController.mainViewController = navigationController
        UINavigationBar.appearance().tintColor = UIColor(hex: "689F38")
        
        let slideMenuController = ExSlideMenuController(mainViewController: navigationController, leftMenuViewController: leftViewController, rightMenuViewController: rightViewController)
        slideMenuController.automaticallyAdjustsScrollViewInsets = true
        slideMenuController.delegate = mainViewController
        slideMenuController.closeLeft()
        slideMenuController.closeRight()
        
        self.window?.backgroundColor = UIColor(red: 236.0, green: 238.0, blue: 241.0, alpha: 1.0)
        self.window?.rootViewController = slideMenuController
        self.window?.makeKeyAndVisible()


ExSlideMenuControllerUIColor(hex: String)是另外寫擴充的方法,程式碼如下

ExSlideMenuController(請例外建立一個class)
import UIKit
import SlideMenuControllerSwift

class ExSlideMenuController : SlideMenuController {

    override func isTagetViewController() -> Bool {
        if let vc = UIApplication.topViewController() {
            if vc is MainViewController ||
            vc is Test1ViewController ||
            vc is Test2ViewController {
                return true
            }
        }
        return false
    }
    
    override func track(_ trackAction: TrackAction) {
        switch trackAction {
        case .leftTapOpen:
            print("TrackAction: left tap open.")
        case .leftTapClose:
            print("TrackAction: left tap close.")
        case .leftFlickOpen:
            print("TrackAction: left flick open.")
        case .leftFlickClose:
            print("TrackAction: left flick close.")
        case .rightTapOpen:
            print("TrackAction: right tap open.")
        case .rightTapClose:
            print("TrackAction: right tap close.")
        case .rightFlickOpen:
            print("TrackAction: right flick open.")
        case .rightFlickClose:
            print("TrackAction: right flick close.")
        }   
    }

}

UIColor(hex: String)(請另外建立class或直接在想寫的地方寫擴充)
extension UIColor {

    convenience init(hex: String) {
        self.init(hex: hex, alpha:1)
    }

    convenience init(hex: String, alpha: CGFloat) {
        var hexWithoutSymbol = hex
        if hexWithoutSymbol.hasPrefix("#") {
            hexWithoutSymbol = hex.substring(1)
        }
        
        let scanner = Scanner(string: hexWithoutSymbol)
        var hexInt:UInt32 = 0x0
        scanner.scanHexInt32(&hexInt)
        
        var r:UInt32!, g:UInt32!, b:UInt32!
        switch (hexWithoutSymbol.length) {
        case 3: // #RGB
            r = ((hexInt >> 4) & 0xf0 | (hexInt >> 8) & 0x0f)
            g = ((hexInt >> 0) & 0xf0 | (hexInt >> 4) & 0x0f)
            b = ((hexInt << 4) & 0xf0 | hexInt & 0x0f)
            break;
        case 6: // #RRGGBB
            r = (hexInt >> 16) & 0xff
            g = (hexInt >> 8) & 0xff
            b = hexInt & 0xff
            break;
        default:
            // TODO:ERROR
            break;
        }
        
        self.init(
            red: (CGFloat(r)/255),
            green: (CGFloat(g)/255),
            blue: (CGFloat(b)/255),
            alpha:alpha)
    }

}
appdelegate方面大致上就完成了,接下來就是個頁面,雖然是簡易套用介面簡單,但是程式碼也不算少了,接下來一個頁面一個頁面附上code給大家參考

首先是主頁面mainViewController(記得import SlideMenuControllerSwift)
import UIKit
import SlideMenuControllerSwift

class MainViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.setNavigationBarItem()
    }


}

extension MainViewController:SlideMenuControllerDelegate{
    
    func leftWillOpen() {
        print("SlideMenuControllerDelegate: leftWillOpen")
    }
    
    func leftDidOpen() {
        print("SlideMenuControllerDelegate: leftDidOpen")
    }
    
    func leftWillClose() {
        print("SlideMenuControllerDelegate: leftWillClose")
    }
    
    func leftDidClose() {
        print("SlideMenuControllerDelegate: leftDidClose")
    }
    
    func rightWillOpen() {
        print("SlideMenuControllerDelegate: rightWillOpen")
    }
    
    func rightDidOpen() {
        print("SlideMenuControllerDelegate: rightDidOpen")
    }
    
    func rightWillClose() {
        print("SlideMenuControllerDelegate: rightWillClose")
    }
    
    func rightDidClose() {
        print("SlideMenuControllerDelegate: rightDidClose")
    }
}
//MARK: UIViewController擴充(用來建立navigationBarItem&移除)
extension UIViewController {
    
    func setNavigationBarItem() {
        self.addLeftBarButtonWithImage(UIImage(named: "ic_menu_black_24dp")!)
        self.addRightBarButtonWithImage(UIImage(named: "ic_notifications_black_24dp")!)
        self.slideMenuController()?.removeLeftGestures()
        self.slideMenuController()?.removeRightGestures()
        self.slideMenuController()?.addLeftGestures()
        self.slideMenuController()?.addRightGestures()
    }
    
    func removeNavigationBarItem() {
        self.navigationItem.leftBarButtonItem = nil
        self.navigationItem.rightBarButtonItem = nil
        self.slideMenuController()?.removeLeftGestures()
        self.slideMenuController()?.removeRightGestures()
    }

}
再來是左邊欄leftViewController
import UIKit

enum LeftMenu: Int {
    case main
    case test1
    case test2
}

protocol LeftMenuProtocol : class {
    func changeViewController(_ menu: LeftMenu)
}

class LeftViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource{

    @IBOutlet weak var leftTableView: UITableView!
   
    
    var menus = ["首頁", "test1", "test2"]
    var mainViewController: UIViewController!
    var test1ViewController: UIViewController!
    var test2ViewController: UIViewController!
    
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        let mainViewController = storyboard.instantiateViewController(withIdentifier: "main") as! MainViewController
        self.mainViewController = UINavigationController(rootViewController: mainViewController)

        let test1ViewController = storyboard.instantiateViewController(withIdentifier: "test1") as! Test1ViewController
        self.test1ViewController = UINavigationController(rootViewController: test1ViewController)
        
        let test2ViewController = storyboard.instantiateViewController(withIdentifier: "test2") as! Test2ViewController
        self.test2ViewController = UINavigationController(rootViewController: test2ViewController)
        
        
        leftTableView.backgroundColor = UIColor(hex: "66ff66", alpha: 0.2)
        //隱藏多餘的cell分隔線
        leftTableView.tableFooterView = UIView()
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        //        self.view.layoutIfNeeded()
    }
    
    
    func changeViewController(_ menu: LeftMenu) {
        switch menu {
        case .main:
            self.slideMenuController()?.changeMainViewController(self.mainViewController, close: true)
        case .test1:
            self.slideMenuController()?.changeMainViewController(self.test1ViewController, close: true)
        case .test2:
            self.slideMenuController()?.changeMainViewController(self.test2ViewController, close: true)
        }
    }
    
    
    // MARK: - DataSource
    // ---------------------------------------------------------------------
    // 設定表格section的列數
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return menus.count
    }
    
    // 表格的儲存格設定
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
                if cell == nil {
                    cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell")
                }
        cell?.backgroundColor = UIColor.lightGray
        //        cell?.imageView?.image = UIImage(named: "star")
        cell?.textLabel?.text = self.menus[indexPath.row]
        cell?.backgroundColor = UIColor.clear
        return cell!
    }
    
    // MARK: - Delegate
    // ---------------------------------------------------------------------
    // 設定cell的高度
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return self.view.frame.size.height / 10
    }
    
    // 點選儲存格事件
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let menu = LeftMenu(rawValue: indexPath.row) {
            self.changeViewController(menu)
        }
    }
    
}
public extension UITableView {
    
    func registerCellClass(_ cellClass: AnyClass) {
        let identifier = String.className(cellClass)
        self.register(cellClass, forCellReuseIdentifier: identifier)
    }
    
    func registerCellNib(_ cellClass: AnyClass) {
        let identifier = String.className(cellClass)
        let nib = UINib(nibName: identifier, bundle: nil)
        self.register(nib, forCellReuseIdentifier: identifier)
    }
    
    func registerHeaderFooterViewClass(_ viewClass: AnyClass) {
        let identifier = String.className(viewClass)
        self.register(viewClass, forHeaderFooterViewReuseIdentifier: identifier)
    }
    
    func registerHeaderFooterViewNib(_ viewClass: AnyClass) {
        let identifier = String.className(viewClass)
        let nib = UINib(nibName: identifier, bundle: nil)
        self.register(nib, forHeaderFooterViewReuseIdentifier: identifier)
    }

}
然後右邊欄rightViewController(我沒刻意寫什麼功能,如沒使用到就不要加入按鈕跟頁面即可),右邊藍什麼都不用寫...因為我沒做任何事@@

兩個子頁面是一樣的沒做任何特殊動作(Test1ViewController,Test2ViewController)
只要在ViewWillAppear加入 self.setNavigationBarItem()即可


以上希望能夠幫助到大家能夠快點上手使用SlideMenuControllerSwift的套件唷^^
在此也感謝套件作者及其他有分享教學的朋友們!

成果如下列圖片顯示: