顯示具有 Swift 標籤的文章。 顯示所有文章
顯示具有 Swift 標籤的文章。 顯示所有文章

2020年7月2日

Swift 儲存文字檔的方法


 let filePath:String = NSHomeDirectory() + "/Documents/sample.txt"

 try! json.write(toFile: filePath, atomically: true, encoding: String.Encoding.utf8)

2020年2月4日

Swift UITextView 於 .xib 加入框線、圓角


User Defined Runtime AttributesKey Path
加入

layer.cornerRadius

layer. borderUIColor

layer.borderWidth

如下圖:







雖然,修改後,在 .xib 上無法馬上看到修改後的畫面,但經過 complier 到手機或模擬器上就可以清楚看到。

2020年1月8日

Swift google map InfoWindow Open


        let marker = GMSMarker()

        marker.position = CLLocationCoordinate2D(latitude: 23.963, longitude: 120.522)

        marker.title = "所在區域"

        marker.isDraggable = true

        marker.snippet = "X:23.963 \n Y:120.522"

        marker.tracksInfoWindowChanges = true

        marker.map = googleMapView

        googleMapView.selectedMarker = marker //InfoWindow Open

2019年11月28日

Swift 建立等待畫面


self.showSpinner(onView: self.view,title : "取得資料中,請稍候!")
        
        let seconds = 1.0
        DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
            // Put your code which should be executed with a delay here
            self.removeSpinner()

        }



///等待的畫面,參考資料 http://brainwashinc.com/2017/07/21/loading-activity-indicator-ios-swift/
var vSpinner : UIView?

extension UIViewController {
    func showSpinner(onView : UIView,title:String) {
        let spinnerView = UIView.init(frame: onView.bounds)
        spinnerView.backgroundColor = UIColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5)
        let ai = UIActivityIndicatorView.init(style: .whiteLarge)
        ai.startAnimating()
        ai.center = spinnerView.center
        
        
        DispatchQueue.main.async {
            spinnerView.addSubview(ai)
            onView.addSubview(spinnerView)
        }
        
        vSpinner = spinnerView
    }
    
    func removeSpinner() {
        DispatchQueue.main.async {
            vSpinner?.removeFromSuperview()
            vSpinner = nil
        }
    }
}

2019年11月13日

Swift String Splite

 

import Foundation

let fullName    = "First Last"
let fullNameArr = fullName.components(separatedBy: " ")

let name    = fullNameArr[0]
let surname = fullNameArr[1]

2019年11月11日

2019年11月8日

swift ios 13 full screen view

 

            if let controller = storyboard?.instantiateViewController(withIdentifier: "ARController") as? ARViewController {                
                if #available(iOS 13.0, *) {

                    controller.modalPresentationStyle = .fullScreen
                    
                } else {
                    // Fallback on earlier versions
                }
                present(controller, animated: true, completion: nil)
            }

2019年10月9日

Swift 當出現 libc++abi.dylib: terminating with uncaught exception of type NSException 時


當出現 libc++abi.dylib: terminating with uncaught exception of type NSException 時,常常是因為 .storyboard 的物件綁定有錯才會出現這個錯誤訊息,只需要「點選 .storyboard」 >> 「開啟 Triggered Segues」看是否有出現 「黃色的三角」訊息,正常綁定是不會有的。

2019年9月5日

SQLite資料庫,不覆蓋舊資料庫的流程,建議方式 for Swift

完整範例
compareDb.swift

呼叫方式:
compareDbVersion(iDbName: "db",iDbType: "sqlite")



如果想知道原理可以看以下說明:

在製作資料庫的過程,通常可能因為一段時間的使用,使得某些因素,讓我們不得不去建立新的資料表或者是欄位,但覆蓋資料庫不是一個好的作法,以下是說如何在不動到原本的資料情況下,建立新的資料表和欄位。

  以下是寫的流程:

1. 我們將利用比對的方式去查資料庫的資料是否有不一樣。

 if(getLocalDbVersion() != getNewDbVersion())
 {
       
    compareDB()
    //最後修改版本
    update(updateStatementString: "UPDATE dbVersion SET version='\(getNewDbVersion())'")

 }else{
       print("資料庫版本一樣:\(dbVersion)|\(getNewDbVersion())")

 }


2.可以先檢查兩個資料庫版本的不一樣「本機資料庫」、「新的資料庫」。

假設,你的資料庫是叫 db.sqlite

    2.1 在「本機資料庫」開啟的方式,這個只能用iTunes才看得到。

  //開啟本機資料庫
    func openLocalDatabase() -> OpaquePointer? {
        
        var db: OpaquePointer? = nil
        let dirPaths =  NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
        let docsDir:String! = dirPaths[0]
        let destPath = (docsDir as NSString).appendingPathComponent("/db.sqlite")
        if sqlite3_open(destPath, &db) == SQLITE_OK {
            //print("Successfully opened connection to database at \(destPath)")
            return db
        }
        return nil

    }

    2.2 「新的資料庫」開啟的方式,這個的意思是你放在app裡面的db.sqlite,如圖1:

  //開啟新的sqlite資料
    func openNewDatabase() -> OpaquePointer?{
        var db: OpaquePointer? = nil
        let path = Bundle.main.path(forResource: "db", ofType:"sqlite")
        if sqlite3_open(path, &db) == SQLITE_OK {
            //print("Successfully opened connection to database at \(path)")
            return db
        } else {
            
        }
        return nil

    }

圖1、App的db.sqlite


3.判斷這兩個版本是否一樣,不一樣的話,才進行「第4步」之後的比對動作。以下是取得「本機資料庫」和「新的資料庫」版本方式

      3.1 「本機資料庫」版本的程式

    //取得本機版本
    func getLocalDbVersion()->String{
        
        var Version = ""
        
        var db = openLocalDatabase()
        var queryStatementString = "SELECT version FROM dbVersion"
        var queryStatement: OpaquePointer? = nil
        if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
            if sqlite3_step(queryStatement) == SQLITE_ROW {
                
                let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
                Version = String(cString: queryResultCol1!)
                
            } else {
                print("Query returned no results")
            }
        } else {
            print("SELECT statement could not be prepared")
        }
        
        sqlite3_finalize(queryStatement)
        
        return Version

    }

  3.2 「新的資料庫」版本的程式

    //取得新的資料庫的資料
    func getNewDbVersion()->String
    {
        var Version = ""
        var db = openNewDatabase()
        
        var queryStatementString = "SELECT version FROM dbVersion"
        var queryStatement: OpaquePointer? = nil
        if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
    
            if sqlite3_step(queryStatement) == SQLITE_ROW {
                let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
                Version = String(cString: queryResultCol1!)
            }
            
        } else {
            print("SELECT statement could not be prepared")
        }
        
        sqlite3_finalize(queryStatement)
        return Version
        

    }


4.當版本的比對不一樣時,我們就開始以下的動作。

      4.1 利用 SELECT name, sql FROM sqlite_master WHERE type='table' 取得「新的資料庫」各「資料表的名稱」和「Create Table SQL語法」,圖2。

    4.2 利用迴圈查詢「本機資料庫」的資料表是否和「新的資料表」的資料表相符的資料,語法:SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'。如果符合,就繼續「檢查欄位」(4.5),如果沒有,就直接利用「Create Table SQL語法」建立新得資料表(4.3、4.4)

圖2

      4.3 比對資料表與欄位的程式碼

  //當資料版本不一樣的時候,會進行,比對db
    func compareDB()
    {
        var dbLocal = openLocalDatabase()//手機本身的sqlite
        var dbNew = openNewDatabase()//新的資料庫
        
        var queryStatementString = "SELECT name, sql FROM sqlite_master WHERE type='table'"
        var queryStatementLocal: OpaquePointer? = nil
        var queryStatementNew: OpaquePointer? = nil
        
        //取得新資料庫的資料表名稱和Crate Table SQL語法
        if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
            // 
            while sqlite3_step(queryStatementNew) == SQLITE_ROW {
                
                //取得「新資料庫」資料表名稱
                let queryResultCol1 = sqlite3_column_text(queryStatementNew, 0)
                let name = String(cString: queryResultCol1!)
                
                //開始比對「本機資料庫」是否有相對應的資料表
                queryStatementString = "SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'"
                //
                if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {

                    if sqlite3_step(queryStatementLocal) == SQLITE_ROW {
              //如果「本機資料庫」有該比資料,那就再去比對「欄位」
                        compareColumn(tableName: name)
                    }else{
                        //如果「本機資料庫」沒有該資料表,那就取得Create Table SQL語法,建立新的
                        let queryResultCol1 = sqlite3_column_text(queryStatementLocal, 1)
                        let createTableString = String(cString: queryResultCol1!)
                        createTable(createTableString: createTableString)
                    }
                }
                
                
            }
        } else {
            print("SELECT statement could not be prepared")
        }
        
        // 6
        sqlite3_finalize(queryStatementNew)
        sqlite3_finalize(queryStatementLocal)
        

    }

      4.3 建立資料表的Swift寫法如下:

 func createTable(createTableString:String) {
        var db = openLocalDatabase()
        var createTableStatement: OpaquePointer? = nil
        if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
            if sqlite3_step(createTableStatement) == SQLITE_DONE {
                print("Contact table created.")
            } else {
                print("Contact table could not be created.")
            }
        } else {
            print("CREATE TABLE statement could not be prepared.")
        }
        sqlite3_finalize(createTableStatement)

    }

       4.4 比對欄位的寫法:

  //比較欄位
    func compareColumn(tableName:String)
    {
        var dbNew = openNewDatabase()
        
        var queryStatementString = "pragma table_info(\(tableName))"
        var queryStatementNew: OpaquePointer? = nil
        //取得新的資料表的資料欄位
        if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
            while sqlite3_step(queryStatementNew) == SQLITE_ROW {
                
                //取得所有欄位名稱
                let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
                let fieldName = String(cString: queryResultCol1!)
                
          //判斷本機資料庫欄位是否存在
                if(!isFieldExist(tableName: tableName, fieldName: fieldName))
                {
                     //如果不存在,就在該資料表建立新的欄位
                    let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
                    let col1 = String(cString: queryResultCol1!)
                    
                    let queryResultCol2 = sqlite3_column_text(queryStatementNew, 2)
                    let col2 = String(cString: queryResultCol2!)
                    
                    
                    let col3 = sqlite3_column_int(queryStatementNew, 3)
                    let col5 = sqlite3_column_int(queryStatementNew, 5)
                    
                    var sql = "ALTER TABLE \(tableName) ADD COLUMN \(col1) \(col2) ";
                    if col3 == 1 {
                        sql = sql + " not null"
                    }
                    
                    if col5 == 1 {
                        sql = sql + " PRIMARY KEY(\(tableName))";
                    }
                    
                    //建立新的欄位
                    createTable(createTableString: sql)
                    
                }else{
                    print("表格:\(tableName),欄位:\(fieldName) 存在")
                }
                
                
            }
        } else {
            print("SELECT statement could not be prepared")
        }
        
        sqlite3_finalize(queryStatementNew)

    }

       4.5 檢查「本機資料庫」是否有相對應的欄位:

    //檢查本機是否有這個欄位
    func isFieldExist(tableName:String,fieldName:String)->Bool{
        var isExist :Bool = false
        var dbLocal = openLocalDatabase()
        var queryStatementString = "pragma table_info(\(tableName))"
        
        var queryStatementLocal: OpaquePointer? = nil

        if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {
            while sqlite3_step(queryStatementLocal) == SQLITE_ROW {
                //檢查「本機資料庫」所有欄位名稱
                let queryResultCol1 = sqlite3_column_text(queryStatementLocal,1)
                let field = String(cString: queryResultCol1!)
               
                if fieldName == field
                {
            //如果一樣的欄位就直接離開。
                    isExist = true
                    break
                }
            }
            
            
        }
        sqlite3_finalize(queryStatementLocal)
        return isExist
        

    }

5.最後、最後,更新本機端的版本號 

檢查完畢後,取得「新的資料庫」版本號,進行「本機資料庫」的更新。
程式碼如下:

   //最後修改版本

    update(updateStatementString: "UPDATE dbVersion SET version='(getNewDbVersion())'")


    func update(updateStatementString:String) {
        var db = openLocalDatabase()
        var updateStatement: OpaquePointer? = nil
        if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
            if sqlite3_step(updateStatement) == SQLITE_DONE {
                print("Successfully updated row.")
            } else {
                print("Could not update row.")
            }
        } else {
            print("UPDATE statement could not be prepared")
        }
        sqlite3_finalize(updateStatement)

    }





 

2019年9月4日

2019年9月3日

Swift 如何 Copy 一份.sqlite 到Mobile Device

一、先把建好的 .sqlite 放到自己建立好的資料夾裡,資料夾什麼名稱都沒關係。


二、填入下列程式碼,就可以了。

       
        let dirPaths =  NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
        let docsDir:String! = dirPaths[0]
        let destPath = (docsDir as NSString).appendingPathComponent("/db.sqlite")//這裡填入你的.sqlite
        let fileMgr = FileManager.default
        if let path = Bundle.main.path(forResource: "db", ofType:"sqlite")
        {
            if !fileMgr.fileExists(atPath: destPath)//如果db不存在,那就直接複製一份
            {
                do {
                    try fileMgr.copyItem(atPath: path, toPath: destPath)
                    print("success")//
                }
                catch let error as NSError
                {
                    print(error.localizedDescription)
                }

            }else{
                print(".sqlite已存在")//

            }
        }

2019年5月3日

Swift 5 讀取 json 檔案及解析

    //CheckList.json
    {
      "CheckList": [
      {
        "CheckItem": "施工計畫書之安全衛生管理計畫",
        "Major": 1,
        "CheckContent": [
        {
            "Major": 1,
            "Minor": 1,
            "CheckItem": "安全衛生管理計畫各項安全檢查"
        },
        {
          "Major": 1,
          "Minor": 2,
          "CheckItem": "工地潛在危險狀況分析及採取相對應之防範措施"
        }
        ]
      }
      ]
    }
    
    //========================================================================================================
    //讀取 Json 資料
    func readJson()
    {
        if let path = Bundle.main.path(forResource: "JsonFile/CheckList", ofType: "json") {
            do {
                
                let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
                
                if let myJSON = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
                    
                    //檢查項目
                    if let data = myJSON["CheckList"] as? [[String: Any]] {
                        for jsonDict in data {
                            var CheckItem = jsonDict["CheckItem"] as? String
                            print("CheckItem:" + CheckItem!)
                            
                            var Major = jsonDict["Major"] as? Int
                            print("Major:" + String(Major!))
                           
                            //檢查內容
                            if let CheckContent = jsonDict["CheckContent"] as? [[String: Any]] {
                                for CheckContentDict in CheckContent {
                                    var CheckItem = CheckContentDict["CheckItem"] as? String
                                    print("CheckItem:" + CheckItem!)

                                    var Major = CheckContentDict["Major"] as? Int
                                    print("Major:" + String(Major!))

                                    var Minor = CheckContentDict["Minor"] as? Int
                                    print("Minor:" + String(Minor!))

                                }
                             }
                        }
                    }
                }
                
            } catch let error {
                print("parse error: \(error.localizedDescription)")
            }
        } else {
            print("Invalid filename/path.")
        }
    }

2019年1月10日

ARKit Geometry 旋轉


func addBox() {
        
        let box = SCNBox(width: 0.5, height: 0.5, length: 0.5, chamferRadius: 0)
        box.firstMaterial?.diffuse.contents="art.scnassets/10.jpg";
        
        let boxNode = SCNNode()
        boxNode.geometry = box
        boxNode.position = SCNVector3(0, 0, -2)
        
        addAnimation(node: boxNode)
        
        let scene = SCNScene()
        scene.rootNode.addChildNode(boxNode)
        sceneView.scene = scene
        
}
    
//旋轉 參考 https://medium.com/s23nyc-tech/arkit-scenekit-and-how-to-control-the-world-dc7377eb1dc6
func addAnimation(node: SCNNode) {
        let rotateOne = SCNAction.rotateBy(x: 0, y: CGFloat(Float.pi), z: 0, duration: 5.0)
        let repeatForever = SCNAction.repeatForever(rotateOne)
        node.runAction(repeatForever)

}

2018年11月15日

Swift ARKit SCNGeometry 呈現半透明在 SCNNode



let geometry:SCNGeometry

...................
                           
let material = SCNMaterial()
material.diffuse.contents = UIColor(red: 255.0 / 255.0, green: 0 / 255.0, blue: 0 / 255.0, alpha: 0.8) // 设置半透明颜色
                            
let boxNode = SCNNode()
boxNode.geometry = geometry
boxNode.geometry?.materials = [material]
boxNode.position = SCNVector3(0, -2, -2)



2018年10月9日

Swift 2D Array add 1D Array(使用 append)



var test2arr:[[Float]] = []
var test1arr:[Float] = [1.0,2.0,3.0]
test2arr.append(test1arr)
test1arr = [4.0,5.0,6.0]
test2arr.append(test1arr)
print(test2arr)



//[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]

Swift 如何回傳 兩個以上的 參數及如何接收。

//回傳方式
func parserE3D(str:String)->([Float],[Int])

{
    var Vertexs:[Float] = []
    var Faces:[Int] = []
    
    .... 一堆程式

    return (Vertexs,Faces)


//接收方式

    var Vertexs:[Float] = []
    var Faces:[Int] = []
    (Vertexs,Faces) = parserE3D(str: datas.hexEncodedString())

2018年10月8日

Swift 16進位(Hex) 轉 Float (依據 IEEE 754)



以 3e4ccccd 轉成 0.2 為範例


    Hex2Float(HexStr: "3e4ccccd")



    //依據 IEEE 754 https://www.h-schmidt.net/FloatConverter/IEEE754.html,16進位轉Float
    func Hex2Float(HexStr:String)
    {
        let binary = Array(get8Hex2Binary(HexStr:HexStr))
        
        let Sign = binary[0...0]
        let Exponent = binary[1...8]
        let Mantissa = binary[9...(binary.count-1)]
        print("Sign:" + Sign)                  //0
        print("Exponent:" + Exponent)          //01111100
        print("Mantissa:" + Mantissa)          //10011001100110011001101
        
        var s = 1.0
        if String(Sign) == "1"
        {
            s = -1.0
        }
        
        let e = Binary2Int(binary: String(Exponent)) - 127
        print("e:" + String(e))                 //-3

        let _2_e = powf(2,Float(e))
        print(_2_e)                             //0.125
        let CalcMant = CalculationMantissa(Mantissa: String(Mantissa))
        let toFloat = Float(s) * _2_e * CalcMant
        print("toFloat:" + String(toFloat))     //0.2
        
    }
    
    //計算Mantissa
    func CalculationMantissa(Mantissa:String) -> Float{
        var calu_m_sum = 0.0
        var e = 0
        for m in Mantissa {
            //println(char)
            e = e - 1
            if m == "1"
            {
                let d = powf(2.0,Float(e))
                calu_m_sum = calu_m_sum + Double(d)
            }
        }
        
        print("calu_m_sum:" + String(1.0 + calu_m_sum))    //1.600000023841858
        return Float(1.0 + calu_m_sum)
    }
    
    //8個位元字串轉成Binary
    func get8Hex2Binary(HexStr:String) -> String{
        var binary = String(Int(HexStr, radix: 16)!, radix: 2)
        print("binary:" +  binary)              //111110010011001100110011001101 未補滿
        if(binary.count != 32)//要補滿32個位元
        {
            for index in binary.count..<32 {
                binary = "0" + binary
            }
        }
        
        print("binary:" +  binary)          //00111110010011001100110011001101 補滿32位元
        return binary
    }
    
    
    //Binary轉Int
    func Binary2Int(binary:String)->Int
    {
        let number = Int(binary, radix: 2)
        return number!
    }

2018年10月5日

Swift Int 轉 16進位字串、16進位字串轉 Int

let strHex = self.Int2strHex(int: 12345)
print(strHex)                          //..... 3039 (16進位)

print(self.strHex2Int(strHex: strHex)) //..... 12345


//////////////////////////////////////////////////////////////

//16進位字串轉Int
func strHex2Int(strHex:String) -> Int
{
    let int = Int(strHex, radix: 16)
    return int!
}
    
//Int轉16進位字串
func Int2strHex(int:Int)-> String
{
    let str = String(int, radix: 16)
    return str
}