2019年11月14日 星期四

Windows:關於 Teamviewer重新安裝的方法

關於 Teamviewer重新安裝的方法

註:  MacOS 的方法還再找....

 - 單位時間內, 連線太多台遠端 Teamviewer就會被定義為商業使用
 - 連續連線到某一台被定義為商業使用的, 也會被定義為商業使用
   (這也會感染, 真的醉了...)

 使用 Teamview AlterID 最快速,最簡單

 但下面這個解決方案最完整
 重點在那個 SID Change Utility
  Windows 7 SID 更換後, 重新開機就可以
  但Windows 10 SID更換後, 所有的 User Account Register Data都會被刪除
  一堆軟體的設定,都要重新設定,麻煩.

 SIDCHG 使用方法
 D:>  sidchg64.exe /f /r /key:3YPCr-vF9fi-R2VB7-ef

 那個 3YPCr-vF9fi-R2VB7-ef 就是免費的測試Key
 SIDCHG Key 的 Trial key在網頁的中間段, 捲頁就找的到
    SIDCHG SID Change Utility Trial key


解決方案:
 TeamViewer ‘Commercial Use Suspected’ fix

2019年11月5日 星期二

swift 5: 關於iOS13禁用beginIgnoringInteractionEvents後的取代方法

 UIApplication.shared.beginIgnoringInteractionEvents()
 因為 beginIgnoringInteractionEvents 在 iOS 13已被作廢


 解決方案來源:
  這網址內容是日文, 看網頁下方的程式就可以
  如何鎖定屏幕以防止在iOS 13上操作

swift 5: 關於 iPad OS 13.2 Can't end BackgroundTask錯誤訊息


APP 在執行時, 按了一下 Home/Touch ID Sensor 就會發生

Can't end BackgroundTask: no background task exists with identifier 3 (0x3), or it may have already been ended. Break in UIApplicationEndBackgroundTaskError() to debug.

在網路查詢了一堆資料
得到結論: 別理會這個錯誤

註:
 這在錯誤訊息 在 Xcode 12.4 就突然消失了...
 反而跑出另一段錯誤訊息 

 [Snapshotting] 
Snapshotting a view (0x102810000, UIKeyboardImpl) that has not been
 rendered at least once requires afterScreenUpdates:YES.

2019年10月30日 星期三

swift 5: 關於 iPad 10.2" iPad OS 13.2 Xcode Debug

關於 iPad 10.2" iPad OS 13.2 Xcode Debug
 這台iPad (7th)10.2"台灣於11月初才開始銷售
 如果直接更新到 13.2 會造成 Xcode 11.1以前的版本偵測錯誤

UIScreen.main.bounds
 單位: point
 Width 1024
 High 768

 iPad (7th)
 UIScreen.main.bounds
  單位: point
  Width 1080
  High 810

 UIScreen.main.nativeBounds
  單位: pixel
  Width 1620
  High 2160

 方案一. 別更新到 iPad OS 13.2

 方案二. 請下載 Xcode 11.2
   如果你把 macOS 升級到  Catalina 10.15.1
   有可能每次開啟 Xcode時發生 "正在驗證Xcode 11.2"
   那就把Xcode 11.2移除, 重新安裝Xcode 11.2
 
   先安裝Xcode 11.2再升級到 Catalina 10.15.1, 整個macOS 變的超級慢,
   請看這邊  macOS: 關於macOS 10.15 Catalina 安裝


 方案三. 降級回到 iOS 13.1.2
   但iPad每天會提醒要更新到 13.2 超級煩的

    [降級教學] 如何替新版 iOS 13 降回舊版 iOS 13.1.3 攻略技巧

   如果您有 Apple Developer Account 可以直接到 Apple 官方網站各種韌體iPSW下載
      iOS 各系列 Restore Images
      iPad OS 各系列 Restore Images
 
   也可以到這邊點選下載
   iOS 13 / iPadOS 13 各版本認證伺服器狀態 
   iPadOS 13正式版各種韌體iPSW下載清單含認證狀態
   iPadOS 13.1.3 正式版(17A878) iPSW官方下載點 (2019.10.16)

  使用 iTunes回復iPad, 要先關閉 iPad的 Find My功能
  iPad -> Setting -> Password & Account -> Find My-> Find My iPad -> Off

  How to Use IPSW File to Restore iPhone iPad with iTunes(Dass Loss)
   Hold Alt key (on Mac) or Shift key (on Windows) and click on the “Restore” button from your iTunes.




2019年10月25日 星期五

swift 5: 關於移除 Xcode simulator images file

關於移除 Xcode simulator images file
 這些檔案都滿大的, 如果不須要, 可以手動移出
  目錄所在: /Library/Developer/CoreSimulator/Profiles/Runtimes

 解決方案來源:
  Can I delete unnecessary device simulators of Xcode?


 Xcode 11開發工具新的功能解說
    註: 可以打開右下角的三個點, 可以顯示字幕(有簡體中文)
 Apple WWDC 2019 What's New in Xcode 11

Xcode 11開發工具模擬器功能解說
  Apple WWDC 2019 Getting the Most Out of Simulator


 

2019年10月24日 星期四

swift 5: 關於iOS 13.x 深色模式及Label文字顏色差異

深色模式會對APP 產生一響
 請看這一篇
  Apple WWDC 2019 SwiftUI Essentials video
    註: 可以打開右下角的三個點, 可以顯示字幕(有簡體中文)
  天生支援 dark mode 的 SwiftUI Color

 也就是說
  原本 light 模式中, Label Font Color Deuault是黑色
  APP畫面就會呈現為白底黑字

 但轉換為 dark模式
  Label Font Color Deuault是白色
  如依原本設計, 就會變成白底白字畫面, 白色字體就看不到了
  因此就要轉換畫面底色為黑色
  但原來的 Button 圖片是黑色外框, 這時就發生鋸齒,
  Button 圖片也要一起更換重畫.

解決方案來源:
 How to check if Dark Appearance is enabled tvOS
 Apple Developer UI Element Colors
 Apple Developer UIColor


 請在 info.plist增加一個Key
 <key>UIUserInterfaceStyle</key>
   <string>Automatic</string>


判斷是否為 iOS13
    if #available(iOS 13.0, *)
    {
     // Fallback on 13.0 versions
      checkInterfaceStyle()
    } 
    else 
    {
     // Fallback on earlier versions
    }

取得 brightness mode

 func checkInterfaceStyle()
 {
   guard(traitCollection.responds(to: #selector(getter: UITraitCollection.userInterfaceStyle)))
      else { return }

   let style = traitCollection.userInterfaceStyle

    switch style
    {
     case .light:
       print("light")
     case .dark:
       print("dark")
     case .unspecified:
       print("unspecified")
     @unknown default:
       print("unknown default")
       fatalError()
    }
 }


這是一個 Label Text 在新舊版本之間的程式差異

Main Storyboard中畫面的設定如下
 Main View 的 Background 設定為 System Background Color
  System Background Color light Mode 其實是白色
  System Background Color dark Mode 其實是黑色
 Label Color 設定為 Defalut( Label Color)
  Defalut Label Color light Mode 其實是黑色
  Defalut Label Color dark Mode 其實是白色


   if #available(iOS 13.0, *)
   {
    if( MyLabel.textColor != UIColor.label)
    {// 黑底白字, 只適用於 iOS 13.0以上版本
     MyLabel.textColor = UIColor.label
    }
   }
   else
   {  // Fallback on earlier versions
    if( MyLabel.textColor != UIColor.darkText)
    { // 白底黑字
     MyLabel.textColor = UIColor.darkText
    }
   }



2019年10月23日 星期三

swift 5: 關於iOS偵測網路連線方法

 在 iOS 12以上, 已新增 Network framework - NWPathMonitor 偵測網路連線
 如果是 iOS 12之前可以使用 Reachability.swift

 解決方案來源:

 Class NWPathMonitor
 
 利用 NWPathMonitor 偵測網路連線
 
 Detecting Internet Access on iOS 12+
 
 Reachability.swift
 
 橋接第三方 Objc Library - Reachability and Bridge.h
 

2019年10月21日 星期一

macOS: 關於macOS 10.15 Catalina 安裝後變慢的原因

/*======================================
  終於查出Catalina 變慢的原因
  因為我的macOS 電腦中有很多個版本的 Xcode  (放在'下載'的目錄中)
  Catalina會把硬碟中所有的檔案都掃描並驗證
  驗證完成後, 會把APP加入到啟動台中

  方案一:
  把 '下載'的目錄中, 所有的Xcode  及其他APP都刪除(移放到另一個Windows 10的分享目錄中)
  速度就變快了....

  方案二:
   等待 Catalina 把硬碟中所有APP 都驗證完成....

  如果有安裝防毒軟體, 請更新為最新版.

/*======================================
系統已更新到 macOS Catalina 是無法用APP Store 下載舊版Mojave轉為Bootable USB Disk
請務必先用舊版的做好Mojave Bootable USB Disk 後, 再更新到 macOS Catalina.

/*======================================
   我現在的狀況是先安裝Xcode 11.2再升級到 Catalina 10.15.1
   整個macOS 變的超級慢, 最後只能重裝macOS試看看
   心態崩潰中....

   上網一查 還真的有人和我一樣
     MAC OS Catalina 10.15.1更新完後整體速度超慢


/*======================================
 如何在 VM Ware中安裝  macOS Catalina
 參考這網站的教學 (必須有mac電腦自行製作 DMG Image File)
 這方法也可以備份現在正在使用的 mac電腦使用的 macOS
  How to Install macOS 10.15 Catalina on VMware on Windows PC


 直接下載 macOS Catalina 實體 DMG Image File
  Download macOS Catalina Final Version  (Final Version (19A602) 15 Oct 2019)

如果想重新安裝 macOS
 參考這網站的教學
  如何製作 macOS 專屬 USB 系統安裝、重灌隨身碟

更新 macOS Catalina後, 開機速度變慢
可參考這網站的教學
  macOS Catalina Slow? How am I able to fix it

VM Ware中的macOS Catalina如何和Windows分享檔案
 參考這網站的教學
  How to Transfer Files Between Mac and Windows PC

Windows:關於 XMind 這一套軟體

關於 XMind 這一套軟體
 由其是在撰寫 IEC-62304 時還滿有用的
 不想用 Power point或 Visio畫一些圖
 可以試試這個, 簡單又乾淨
 XMind 和 Visio 可以互補

 XMind 官網

 安裝好後, 第一次執行時, 要求註策的畫面
 選擇"Skip"



關於 Understand這一套軟體


 軟體工程師應該測試一下
 滿有趣的一套軟體
 尤其是上層要求一些流程圖
 但又不想花太多的時間在這些無意義的畫圖上
 可以試試這個
 滑鼠點一點就可以印出一大堆"流程圖"

 由其是在撰寫 IEC-62304 時還滿有用的
 把專案中一些不需要的檔案和函式移除後
 直接產生一些圖, 可以充充版面....

 Scientific Toolworks Understand
 其他檔案在 downloadly.ir 可以找到

 Understand 使用指南(中文)




2019年10月16日 星期三

swift 5: 關於 bit 的處理方法

關於 bit 的處理方法
 MCU端資料都是以 bit為單位處理

 當MCU資料經過 Bluetooth 傳輸到 iOS
 要如何將資料以bit 拆開
 請參考下面
  IOS--swift BLE藍牙通信管理(多設備)

  func convert83(data:[UInt8])
  {
   ....
  }

 高階語言對bit處理真的很痛苦.
 另一個方法, 把接收到的資料放到 Object-C Function 處理後
 再送回 Swift Function中.

swift 5: Assets.xcassets 圖片的大小

 Assets.xcassets 圖片的大小
 圖片的1x 2x 3x 是何意思
 請看這一篇
  Xcode: 實驗image view中的 1x 2x 3x

 但這樣就要準備很多不同大小的 JPG或 PNG圖檔
 現在有更省時的方法
 如果是用 illustrator畫圖, 可以直接另存新檔為
 illustrator: 檔案->另存新檔->存檔類型: adobe pdf
 再把 pdf檔直接拖拉到 Xcode的Assets.xcassets 中
 請看這一篇
  丟掉 PNG/JPEG 讓 Xcode 擁抱 PDF 吧


 這邊有世界各國的國旗
 List of country flags (png) by ISO3


2019年10月8日 星期二

swift 5: iOS 13.x UINavigationController 全螢幕顯示

iOS 13.x UINavigationController 全螢幕顯示
 在 iOS 12.x以前 UINavigationController會自動 Full Screen
 但 iOS 13.x  UINavigationController 已預設為縮小
  一縮小後, 所有元件的位置也都亂了

解決方案來源: 
 Presenting modal in iOS 13 fullscreen

 View Controller Presentation Changes in iOS 13


  let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
  let MyTableViewNvc = storyBoard.instantiateViewController(withIdentifier: "MyTableViewNvcId") as! UINavigationController
  
  MyTableViewNvc.modalPresentationStyle = .fullScreen  // 增加這一行
  
  let myTableViewVc = storyBoard.instantiateViewController(withIdentifier: "MyTableViewVcId") as! MyTableViewVc
  navigationController.pushViewController( myTableViewVc, animated: true)
  self.present( MyTableViewNvc, animated: true, completion: nil)

或者



2019年10月7日 星期一

iOS swift 5: 一些好的網站

UIKit · Swift 起步走

SwiftGG-走心的Swift 翻譯組

NSHipster-关注被忽略的 Objective-C、Swift 和 Cocoa 特性

航歌-Swift

青玉伏案 - Swift

青玉伏案 - iOS開發

Mr.林的博客- iOS開發日記

iT 邦幫忙鐵人賽-Swift菜鳥的30天系列

LikeABosshttps://likeabossapp.com/App

iOS:macOS Books

iOS網絡高級編程:iPhone和iPad的企業應用

Tools for UI Debugging in iOS

IOS app開發介紹 - 不同class溝通方式

IOS--swift BLE藍牙通信管理(多設備)

iOS藍芽開發CoreBlueTooth庫核心方法使用介紹

開發者指南:如何利用 Core Bluetooth 製作一個監控心率 App

iOS藍牙原生封裝

Bluetooth Low Energy in iOS Swift


教學 macOS vs VMware 

Unwind Segues in Swift 5 - Flawless iOS - Medium


iOS swift 5: iOS 13.x CoreBluetooth Framework 發生 sigabrt錯誤

iOS 13.x CoreBluetooth Framework 發生 sigabrt錯誤

簡單的說 就是只要使用到 CBCentralManager, 就要增加這個Key
在 info.plist 增加 NSBluetoothAlwaysUsageDescription 機碼

 Privacy – Bluetooth Always Usage Description
  NSBluetoothAlwaysUsageDescription

如果要能在 iOS 12.x 繼續使用, 則保留以前使用的 NSBluetoothPeripheralUsageDescription

實務上,  info.plist 共使用下面幾個
  <key>NSBluetoothAlwaysUsageDescription</key>
  <string>Play with BLE Compatible devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Play with BLE Compatible devices</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>


如果系統設定的 Privacy -> Bluetooth -> authorized被關閉
程式的 central.state就會回傳 .unauthorized









 func centralManagerDidUpdateState(_ central: CBCentralManager)
 {
   switch( central.state)
   {
    case .poweredOff:
        // "Bluetooth powered off"
    case .poweredOn:
        // "Bluetooth powered on"
        // Do Scanner Peripherals
    case .resetting:
        //   
    case .unauthorized:
        // "Bluetooth unauthorized"
    case .unknown:
        // 
    case .unsupported:
        // 
    default:
        // 
   }
 }

 另外別太相信有些教學的 RePaired功能
 已經把 Peripherals 斷電, 但 retrievePeripherals() 還是回傳找到 Peripherals....
 我的作法是重新呼叫 CBCentralManager.init() 一且重頭來
 這也是為何連線一些藍芽耳機要把藍芽關閉重開的原因

解決方案來源:
The Ultimate Guide to Apple’s Core Bluetooth
Property List Key: NSBluetoothAlwaysUsageDescription
 A message that tells the user why the app needs access to Bluetooth.


Bluetooth permission recommendation in iOS 13

2019年10月1日 星期二

ubuntu18.04修改 mysql root 密碼

ubuntu18.04修改 mysql root 密碼

//=============================
MySQL 效能監測工具 
 > sudo apt-get install mytop
 > mytop -u root -p

//=============================
> sudo nano /etc/mysql/debian.cnf
[client]
user     = debian-sys-maint
password = EyCgb3wHDcOwaCH4

> mysql -u debian-sys-maint -p
輸入上面debian.cnf中的那一組password密碼
進入 mysql指令後 依序 輸入下面指令
 mysql> update mysql.user set authentication_string=password('新的root密碼') where user='root' and Host ='localhost';
 mysql> update mysql.user set plugin="mysql_native_password";
 mysql> flush privileges;
 mysql> quit;

> sudo service mysql restart
> mysql -u root -p
 輸入上面已更新後的密碼

//=============================
mysql 5.7設定 提供外部連線(remote connect)
> sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
 #bind-address = 127.0.0.1

> mysql -u root -p
輸資料庫mysql 的密碼
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root密碼' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;
mysql> exit

啟用和停用防火牆
> sudo ufw enable
> sudo ufw disable
> sudo ufw allow in 3306
> sudo ufw allow out 3306
> sudo ufw status
> sudo ufw statusverbose
> sudo ufw delete 3306

BUG: ubuntu 18.04 ufw 開機自動啟動會失效

//=============================
安裝完成後可以嘗試使用MySQL Workbench或Navicat Premium進行連線測試
下載工具軟體 MySQL Workbench 8.0.17
下載工具軟體 Navicat Premium

https://www.navicat.com/en/download/navicat-premium
 Linux download navicat121_premium_en_x64.tar.gz
 > tar xvf navicat121_premium_en_x64.tar.gz
 > cd navicat121_premium_en_x64
 > bash start_navicat
 - 選擇 install
 - 退出 navicat
 - 安裝 Wine
   參考 https://magiclen.org/wine/
 > sudo dpkg --add-architecture i386 && wget -O - https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add - && sudo apt-add-repository "deb  https://dl.winehq.org/wine-builds/ubuntu/ $(cat /etc/os-release | grep UBUNTU_CODENAME | cut -d '=' -f 2) main" && sudo apt update && sudo apt install --install-recommends winehq-stable
 > wine uninstaller
 - 點選wine 畫面右上角 安裝
 - 選擇 Navicat-keygen.exe  (這是12.1.20版本的中所含的)
   參考 downloadly.ir
   或 DoubleLabyrinth
 - 選擇 \home\xxx\download\navicat121_premium_en_x64\Navicat\Navicat.exe
 - 選擇 patch
Windows版本教學 在這邊





Ubuntu 18.04 安裝 iredmail 0.9.9 Server



 解決方案來源:
   How to Easily Set Up a Full-Featured Mail Server on Ubuntu 18.04 with iRedMail

   Install iRedmail, Open-Source Mail Server, on Ubuntu


 注意事項:
  請下載 iRedMail-0.9.9.tar.bz2 版本就好
  iRedMail-1.0-beta1已將port 25關閉, 設定比較麻煩.


   > sudo apt update
   > sudo apt upgrade
   > sudo reboot  重新開機
  最好再做一次
   > sudo apt update
   > sudo apt upgrade

   > sudo hostnamectl set-hostname mail.MyDomain.com
   > sudo nano /etc/hosts
     Add Line
     127.0.0.1       mail.MyDomain.com localhost
   > hostname -f

   > wget https://bitbucket.org/zhb/iredmail/downloads/iRedMail-0.9.9.tar.bz2
   > tar xvf iRedMail-0.9.9.tar.bz2
   > cd iRedMail-0.9.9
   > chmod +x iRedMail.sh
   > sudo bash iRedMail.sh

Web Server 選擇 Nginx
Database 選擇  MySQL
防火牆的選項都選擇 Y

安裝完成後 重新開機
   > sudo reboot



2019年9月24日 星期二

關於蘋果企業帳號-退件心得

   企業帳號:
    不需要經過 Apple APP Store 認證及安裝
    企業可自行使用 Web Server Download Install
   AdHoc In-House費用:
    每年美金 299元


重點:
  1: 原有的 Apple ID 已經是 開發者帳號Developer Program
     無法再申請為 企業帳號 Enterprise Program
     必須要用另一個新的 Apple ID進行申請
     Apple Id後綴 domain name 要和公司網站domain name相同
     新申請 Apple Id 要先通過雙重認證
  2:
    https://developer.apple.com/programs/enterprise/enroll/
  3:
    D-U-N-S 可以和開發者帳號相同
    (因為一家公司只會有一個D-U-N-S)
  4:
    填寫資料時, 公司網站和 Apple Id後面 domain name相同
    而且網站最好有英文網頁
    因為企業版開發者帳號是美國總部負責審核
    (開發者帳號Developer Program 是新加坡負責審核)
  5:
   如果申請人不是公司負責人
   要填寫公司負責人相關資料
   Apple 會以電話詢問公司負責人一些申請事項
  6:
    填寫資料時, 公司內部員工人數要超過100以上,
    不然會被直接退件

   已被退件, 只好再重新申請....

  參考資料
   Apple 企業版開發者帳號Apple In House(Apple Developer Enterprise Program License) 申請心得
 
  任何人都可以安裝的 Enterprise App

 使用XCODE快速發佈IOS ad hoc測試版本


2019年9月8日 星期日

iOS swift 5: Swift UIView畫面觸發 OnClick事件

 當點選畫面上任何位置(不包含其他物件的地方),
 就會觸發 checkAction()

 解決方案來源:
  UIView touch event in controller

  myView 指的是 
  @IBOutlet var myView: UIView


let gesture = UITapGestureRecognizer(target: self, action:  #selector(self.checkAction))
self.myView.addGestureRecognizer(gesture)

@objc func checkAction(sender : UITapGestureRecognizer) {
    // Do what you want
}

2019年7月25日 星期四

關於 Embarcadero RAD Studio 10.3 Update 2 - Android build-tools


安裝後 Android SDK並沒有安裝完整
請依下列步驟
C:> CD C:\Users\Public\Documents\Embarcadero\Studio\20.0\PlatformSDKs\android-sdk-windows\tools
C:> android.bat update sdk --no-ui --all --filter platform-tools,build-tools-28.0.2,android-26




安裝後的目錄如下
C:\Users\Public\Documents\Embarcadero\Studio\20.0\PlatformSDKs\android-sdk-windows\build-tools\28.0.2



2019年7月24日 星期三

RAD Studio: 重新編譯 TeeChart


這個好像只有在中文環境的Windows 才會發生

解決方法
  先把作業系統語系換成英文
  編譯完成後 再換回中文

Cannot recompile Delphi package:
VCLTee.TeeConst.pas(612) Error: E2066 Missing operator or semicolon
VCLTee.TeeConst.pas(1014) Error: E2066 Missing operator or semicolon







2019年7月17日 星期三

iOS swift 5: Thread Mode-String Array Index Out of Range


  在兩個Thread中同時對同一個 String Array進行快速的 append 和 remove
  就會發生 Index Out of Range

  已使用網路上找到的各種修改 extension Array方案都無效
 


     
  var dataList = [String]()
  ...
  func Thread1_RemoveFun()
  {
   ...
   DispatchQueue.global().async  /* 問題就出在這一行 */ 
   {
    ...
    if( dataList.Count > 0)  
    {
     dataList.remove(at: 0)
    }  
    ...
   } 
   ...
  }
  
  func Thread2_AppendFun()
  {
   ...
    dataList.append("Test")
   ...
  }
  
  func Thread3_ReadFun()
  {
   if( nIndex < dataList.Count)  
   {
    // dataList.count 輸出是 1 但還是發生
    // 用Debug breakpoint去看, dataList.count 盡然是 0
    print( \(dataList.count)"
    readStr = dataList[nIndex] 
   } 
  }



同一個head, Auto Express 和 Add Express 竟然顯示不同數值
搞不懂...


2019年7月2日 星期二

iOS swift 5: Xcode Project SQLite Bundle


  解決方案來源:
   bundle to documents directory in iOS
 
 
  Step 1: 把 SQLite的資料庫檔案MyDatabase.db 拖拉到 Xcode Project中
  Step 2: 設定 Copy Bundle Resources 加入剛才拖拉到 Xcode Project中MyDatabase.db

 
  Step 3:
func copyfileToDocs()
{
        let bundlePath = Bundle.main.path(forResource: "MyDatabase", ofType: ".db")
        print(bundlePath!, "\n") //prints the correct path
        let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
        let fileManager = FileManager.default
        let fullDestPath = NSURL(fileURLWithPath: destPath).appendingPathComponent("fj200ls.db")
        let fullDestPathString = fullDestPath?.path
        print(fileManager.fileExists(atPath: bundlePath!)) // prints true
        do
        {
            try fileManager.copyItem(atPath: bundlePath!, toPath: fullDestPathString!)
            print("DB Copied")
        }
        catch
        {
            print("\n")
            print(error)
        }
}



2019年7月1日 星期一

iOS swift 5: SQLite 用法

都試過後, 選擇使用 FMDB 方式, 比較簡單


 方式1:
  解決方案來源:
   Accessing an SQLite Database in Swift
   Swift起步走-SQLite 
   在iOS裡操作SQLite筆記 - 資料集(一)
   Swift SQLite Tutorial for Beginners – Using SQLite in iOS Application
   muhlenXi-使用FMDB 管理SQLite 數據庫

 方式2: 使用 SQL.swift
  SQLite.swift
  SQLite.swift簡單實用筆記
  iOS Swift 4.0:第三方SQLite框架SQLite.swift 使用(一)

  如果是使用 swift 5 安裝流程
  一定要使用
[sudo] gem install cocoapods

target 'YourAppTargetName' do
    pod 'SQLite.swift', '~> 0.12.0'
end
pod install --repo-update



 方式3:
  解決方案來源:
   FMDB與SQLite 數據庫應用示範
  iOS - 利用 FMDB 第三方框架來操作 SQLite 檔案
  Swift - 第三方SQLite庫FMDB使用詳解1(安裝配置、工具類封裝)





2019年6月30日 星期日

iOS swift 5: NotificationCenter 的用法

 可以用於不同的畫面傳遞參數
 也可以用於Thread中程式, 通知Main Thread 更新畫面的動作

 解決方案來源:
  Swift - 透過 NotificationCenter 監聽特定的事件同時傳值


 override func viewDidLoad()
 {  
  MyFunNtfInit()
 } 
 func MyFunNtfInit()
 {
  NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "MYFUNNTF"),
    object: nil, queue: nil, using: MyFunNtf)
 }
 
 func MyFunNtfPost()
 {
  NotificationCenter.default.post(name: Notification.Name(rawValue: "MYFUNNTF"),
            object: nil, userInfo: ["MSG1":"F","MSG1":"MyMsg2"])
 }
 
 @onjc fileprivate func MyFuncThread
 {
  while( 1)
  {
   if( bDoNtfPost == true)
   {
    MyFunNtfPost()
   }
  }
 }
 
 func MyFunNtf(noti:Notification) -> Void
 {
  guard let userInfo = noti.userInfo,
    let sMsg1 = userInfo["MSG1"] as? String,
    let sMsg2  = userInfo["MSG2"] as? String else {
       return
    }
   if( sMsg1 == "MyMsg1" ) 
   {
    ...
   }
 }    


2019年6月28日 星期五

iOS swift 5: Function的多參數傳入及回傳



 解決方案來源:
  Function with multiple parameters and multiple return values

func greetUser(name:String, age:Int) -> (String, Int) {
    
    let msg = "Good Morning!" + name
    var userage = age
    if age < 0
    {
     userage = 0
    }
    return (msg, userage)
}
 let msg = greetUser(name: "Jack", age: -2)
 print(msg.0)
 print("You have \(msg.1) coins left")

2019年6月27日 星期四

iOS swift 5: 直接移除字串中不是數字的字元

字串中不是數字的直接移除
 解決方案來源:
  Filter non-digits from string

 sStrTmp2 = sStrTmp1.components(separatedBy:CharacterSet.decimalDigits.inverted).joined(separator: "")
 if( sStrTmp2.count > 0)
 {
  if let nNumber = Int(sStrTmp2)
  {
  }
 } 

字串的字元檢查
  sStrTmp1 = "0123456789"
  for nChar in sStrTmp1  /* 檢查是否為數字字元 */
  {
   if( nChar < "0" || nChar > "9")
   {
    bIsNumber = false
   }
  }

iOS swift 5: HEX ASCII 字串轉換為Int

HEX ASCII 字串轉換為Int
 解決方案來源:
  Swift Int 轉 16進位字串、16進位字串轉 Int
  Swift native functions to have numbers as hex strings

 func MyFunA()
 {
  let sStr1 = "7FFF"
  let nInt = strHex2Int(sStr1)
  print( nInt)
  let sStr2 = Int2strHex( nInt).uppercased() // 轉大寫
  print( sStr2)
 }
 
 func strHex2Int(_ strHex:String) -> Int
 { /* 16進位字串轉Int */
  let nInt = Int(strHex, radix: 16)
  return nInt!
 }

 func Int2strHex(_ int:Int)-> String
 { /* Int轉16進位字串 */
  let sStr = String(int, radix: 16)
  return sStr
 }

iOS swift 5: 數字陣列 Int Array

數字陣列 Int Array

Int Array 宣告方式
宣告 nSeqAvgArr 有9個 Integer陣列
並都初始化為 0x00

  var nSeqAvgArr = Array( repeating: 0, count:9)

 func MyFunA()
 {
  var nInArr = Array( repeating: 0, count:9)
  _ = MyFunB( &nInArr)
  print( nInArr.count )
  print( nInArr[0] )
  _ = MyFunC( &nInArr)
  print( nInArr.count )
  print( nInArr[0] )
 }

 func MyFunB(_ nInArr: inout [Int]) -> Bool
 {
  nInArr[0] = 1234
  ...
  return false
 }

 func MyFunC(_ nInArr: [Int]) -> Bool
 {
  nInArr[0] = 1234
  ...
  return false
 }








iOS swift 5: 字串的切割分離

iOS swift 5: 字串的切割分離

 解決方案來源:
  A Unicode string value that is a collection of characters.
  Swift Arrays
  Split a String into an array in Swift?

字串的切割分離
import Foundation
let fullName : String = "First Last";
let fullNameArr : [String] = fullName.componentsSeparatedByString(" ")

// And then to access the individual words:

var firstName : String = fullNameArr[0]
var lastName : String = fullNameArr[1]


  let sStr:String = "0,0001,0010,0100,1000,1023,0001,0001,0001"
  
  let sStrArr = sStr.components(separatedBy: ",")
  print( sStrArr.count )
  print( sStrArr[0] )
  ...
  print( sStrArr[6] )









2019年6月23日 星期日

iOS swift 5: 主執行緒或是背景執行緒


  主執行緒 DispatchQueue.main.async
  背景執行緒 DispatchQueue.global().async

 解決方案來源:
  Swift 3學習指南:重新認識GCD應用


DispatchQueue.main.async
{
 // 用於UI類
  self.MyBtn.setImage(UIImage(named: "MyBtnImage"), for: UIControl.State.normal)
}


let MY_TIMEOUT             = 100
let MySimaphore = DispatchSemaphore.init( value: 0)
MyThreadCreate()
{

hMyThread = Thread( target: self,
                       selector: #selector( MyThreadFunc), object: nil)
}

MyThreadFunc()
{
 ...
 DispatchQueue.global().async
 {
  //在 Thread中 用於 DispatchSemaphore類
  self.MySimaphore.signal()
 }
 ...
 if( MySimaphore.wait(timeout: .now() + .seconds(MY_TIMEOUT)) == .success)
 {
  ...
 }
 else
 {
  ...
 }
 
} 


2019年6月21日 星期五

iOS swift 5: 學習Swifte 一定要先弄懂 GCD,Delegates,dataSource


 解決方案來源:
  【給iOS新手】Delegate是啥?能吃嗎?
 
  Delegate 是什麼? 
 
  iOS 開發者指南:透過 Swift 4 學習 Delegates 與 Delegation
 

程式流成為了達到 A功能完成後,才能再執行B功能,再執行C功能
在 BCB 中使用 showmodal()就解決了
但在 iOS 中會是滿麻煩的事
建議使用下面兩種方式來達成

 iOS - GCD 多執行緒的說明與應用
 iOS開發之再探多線程編程:Grand Central Dispatch詳解
 JS - Promise使用詳解1(基本概念、使用優點)
 Swift - 異步編程庫PromiseKit使用詳解1(安裝配置、基本用法)

2019年6月20日 星期四

iOS swift 5: 關於 Debug Mode prinf function

iOS swift 5: 關於 Debug Mode prinf function

 swift 沒有C++的 redefine function 功能
 所以只能下面方式來暫用
 另一種方式使用 swift 內建的 debugPrint()

 解決方案來源:
  Swift: Extending functionality of print() function
  STRIPPING PRINT AND DEBUGPRINT IN SWIFT FOR RELEASE BUILDS
  Swift print, println, NSLog

public func debugPrint(_ items: Any..., separator: String = " ", terminator: String = "\n")

import Foundation
public func DPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") 
{
 #if DEBUG // for Debug Mode
  let output = items.map { "\($0)" }.joined(separator: separator)
  Swift.print(output, terminator: terminator)
 #else     // for Relase Mode
  // ...
 #endif
}
public func RPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") 
{
 #if DEBUG // for Debug Mode
 #else     // for Relase Mode
  let output = items.map { "\($0)" }.joined(separator: separator)
  Swift.print(output, terminator: terminator)
 #endif
}

import Foundation
class MyViewController: UIViewController 
{
  func MyFun()
  {
   print("FUNC: \(#file),\(#function)")
   debugPrint("FUNC: \(#file),\(#function)")
   DPrint("FUNC: \(#file),\(#function)")
   RPrint("FUNC: \(#file),\(#function)")
  }
}


2019年6月19日 星期三

iOS swift 5: 關於 bits shift

iOS swift 5: 關於 bits shift

 swift 沒有C++的 struct bit 對照
 所以只能用 bits shift 來一個一個處理
 解決方案來源:
  How do I shift bits using Swift?

  Bitwise Shifting (swift 3)
// Shifting Right
let example1 = 8 >> 1 // example1 == 4
let example2 = 8 >> 2 // example2 == 2
// Shifting Left
let example3 = 8 << 1 // example3 == 16
let example4 = 8 << 2 // example4 == 32
 let N1 = 1 << 0 // 0b0001
 let N2 = 1 << 1 // 0b0010
 let N3 = N1 & N2  // => 0b0000
 let N4 = N1 | N2  // => 0b0011


iOS swift 5: 在 Thread中 要呼叫另一個畫面

iOS swift 5: Thread And self.present

 解決方案來源:
   Xcode 9 的 Main Thread Checker

簡單的說如果在 Thread中 要呼叫另一個畫面
要使用 DispatchQueue.main.async 方法

Main Thread Checker: UI API called on a background thread: -[UIButton setImage:forState:]
PID: 1284, TID: 896227, Thread name: (none), Queue name: com.apple.root.default-qos.overcommit, QoS: 0

  DispatchQueue.main.async
  {
   let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
   let ShowVc = storyBoard.instantiateViewController(withIdentifier: "ShowVcId") as! TShowVc
   ShowVc.TitleStr = Title 
   self.present( ShowVc, animated: true, completion: nil)
  }   

iOS swift 5: 取得iOS設備型號

iOS swift 5: 設備型號
 解決方案來源:
  iOS獲取設備型號和App版本號等信息(OC+Swift)

    //  獲取設備名稱
    let deviceName = UIDevice.current.name
    print(deviceName)
    // 系統名稱
    let systemName = UIDevice.current.systemName
    print(systemName)
    // 系統版本
    let sysVersion = UIDevice.current.systemVersion
    print(sysVersion)
    // 設備的型號
    let sysmodel = UIDevice.current.model
    print(sysmodel)
    // 獲取設備唯一標識符
    let sUuid = UIDevice.current.identifierForVendor?.uuidString
    print( sUuid ?? "")
   

2019年6月17日 星期一

iOS swift 5: 簡易的的替換式密碼ROT13

 解決方案來源:
  Swift ROT13 Func - Dot Net Perls


func rot13(value: String) -> String 
{ /* https://www.dotnetperls.com/rot13-swift */
    // Empty character array.
    var result = [Character]()
    // Some ASCII constants.
    // A = 65
    // M = 77
    // Z = 90
    // a = 97
    // m = 109
    // z = 122
    let upperA = 65
    let upperM = 77
    let upperZ = 90
    let lowerA = 97
    let lowerM = 109
    let lowerZ = 122

    // Loop over utf8 values in string.
    for u in value.utf8 {

        let s = Int(u)
        var resultCharacter = Character(UnicodeScalar(s))
        if s >= lowerA && s <= lowerZ { // Between a and z.
            if s >= lowerM {
                resultCharacter = Character(UnicodeScalar(s - 13))
            } else {
                resultCharacter = Character(UnicodeScalar(s + 13))
            }
        } else if s >= upperA && s <= upperZ { // Between A and Z.
            if s >= upperM {
                resultCharacter = Character(UnicodeScalar(s - 13))
            } else {
                resultCharacter = Character(UnicodeScalar(s + 13))
            }
        }
        // Append to Character array.
        result.append(resultCharacter)

    }
    // Return String.
    return String(result)
}



iOS swift 5: 對話框 Show Message Box(Dialog)-UIAlertController


Swift 顯示警告訊息有兩種方式

解決方案來源:
  iOS - 客製化Dialog
  PopupDialog:採用Swif編寫的一個簡單、可定製的iOS彈出對話框
  UIAlertController  顯示警告訊息和選單

方式一
 使用 UIAlertController
 使用 CustomAlertController範例
 UIAlertController 教程:讓你輕鬆在 UIViewController 以外的地方呈現警告
 Making an Alert in iOS

方式二
 使用  PopupDialog
 import PopupDialog
 func ShowMessageBoxDlg( nTitle:Int, nMessage:Int)
 { 
  let Title = NSLocalizedString( String(nTitle), comment: "")
  let Message = NSLocalizedString( String(nMessage), comment: "")
  let PopupDlg = PopupDialog( title: Title,
                           message: Message,
                           buttonAlignment: .horizontal,
                           transitionStyle: .zoomIn,
                           tapGestureDismissal: true,
                           panGestureDismissal: true,
                           hideStatusBar: true)
   {
   }
   let MagOk = NSLocalizedString( "Ok", comment: "")
   let buttonOk = DefaultButton(title: MagOk)
   {
   }
   PopupDlg.addButtons([buttonOk])
   self.present( PopupDlg, animated: true, completion: nil)
 }

方式三
 自行用 UIViewController 設計一個 Dialog
 畫面部份同  iOS - 客製化Dialog
 多花點時間 就可以做出一個和Windows 一樣的MessageBob()

import UIKit
//-------------------------------------------------------------------
var bShowWaitDialogClose: Bool = false
//-------------------------------------------------------------------
class TShowWaitDialogVc: UIViewController
{
 var nCheckCloseDialogTimer:Timer?
 var sTitleStr:String      = ""
 var sMessageStr:String    = ""

 var activityIndicator:UIActivityIndicatorView = UIActivityIndicatorView()

 @IBOutlet weak var ViewDialog: UIView!
 @IBOutlet weak var LabelTitleStr: UILabel!
 @IBOutlet weak var LabelMsgStr: UILabel!
 //-------------------------------------------------------------------
 override func viewDidLoad()
 {
  super.viewDidLoad()
  bShowWaitDialogClose = false
  let yourColor : UIColor = UIColor(red:170.0/255.0, green:170.0/255.0, blue:170.0/255.0, alpha: 1.0 )
  ViewDialog.layer.masksToBounds = true
  ViewDialog.layer.borderColor = yourColor.cgColor
  ViewDialog.layer.cornerRadius = 30
  ViewDialog.layer.borderWidth = 2
  ViewDialog.layer.shadowOffset = CGSize(width: 5, height: 5)
  ViewDialog.layer.shadowOpacity = 0.7
  ViewDialog.layer.shadowRadius = 5
  ViewDialog.layer.shadowColor = UIColor(red:170.0/255.0, green:170.0/255.0, blue:170.0/255.0, alpha: 1.0 ).cgColor

  LabelTitleStr.text = sTitleStr
  LabelMsgStr.text = sMessageStr
  CheckCloseDialogTimerInit()
  ActivityIndicatorStart()
 }
 //-------------------------------------------------------------------
 func CheckCloseDialogTimerInit()
 {
  nCheckCloseDialogTimer = Timer.scheduledTimer(timeInterval: 0.2, target: self,
                           selector:#selector(self.CheckCloseDialogTimerDown),
                           userInfo: nil, repeats: true)
 }
 //-------------------------------------------------------------------
 @objc func CheckCloseDialogTimerDown()
 {
  if( bShowWaitDialogClose == true)
  {
   self.nCheckCloseDialogTimer?.invalidate()
   ActivityIndicatorStop()
   dismiss(animated: true, completion: nil)
  }
 }
 //-------------------------------------------------------------------
 @objc func ActivityIndicatorStart()
 {
  activityIndicator.center = self.view.center

  activityIndicator.hidesWhenStopped = false
  activityIndicator.style = UIActivityIndicatorView.Style.gray
  activityIndicator.isHidden = false
  view.addSubview(activityIndicator)

  activityIndicator.startAnimating()
  UIApplication.shared.beginIgnoringInteractionEvents()
 }
 //-------------------------------------------------------------------
 @objc func ActivityIndicatorStop()
 {
  self.activityIndicator.stopAnimating()
  self.activityIndicator.isHidden = true
  UIApplication.shared.endIgnoringInteractionEvents()
 }
 //-------------------------------------------------------------------
}
//-------------------------------------------------------------------


    func ShowWaitDialogVcShow()
    {
     bShowWaitDialogClose = false
     let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
     let ShowWaitDialogVc = storyBoard.instantiateViewController(withIdentifier: "ShowWaitDialogVcID") as! TShowWaitDialogVc
     ShowWaitDialogVc.sTitleStr = "Processing"
     ShowWaitDialogVc.sMessageStr = "In Processing, Please Wait..."
     self.present( ShowWaitDialogVc, animated: true, completion: nil)
    }


 func BtnCloseDialg(_ sender: Any)
 {
  print("FUNC: \(#file), \(#function)")
  bShowWaitDialogClose = true
 }



    var hBtnTouchTimer:Timer?
    @IBAction func BtnTestOnClick(_ sender: Any)
    {
     bShowWaitDialogClose = false
     hBtnTouchTimer = Timer.scheduledTimer( timeInterval: 20, target: self,
                                            selector:#selector( self.BtnTouchTimerDown2),
                                            userInfo: nil, repeats: false)
     ShowWaitDialogVcShow()
    }
    @objc func BtnTouchTimerDown2()
    {
     if( bShowWaitDialogClose == false)
      bShowWaitDialogClose = true
    }

iOS swift 5: 顯示gif 動畫圖片

試玩這套 滿有趣的

解決方案來源:
  用QuartzCode快速實現一個收藏動畫


測試玩玩看
QuartzCode 1.66.4

另還有一套 PaintCode 3.4.5

2019年6月10日 星期一

2019年6月5日 星期三

iOS swift 5: 關於 "String Array"


Swift 的 String 是一個Class Library
當宣告為 var MyStringList = [String]() 時
就變成一個類似 String List Clss

解決方案來源:
 Swift Array Examples, String Arrays - Dot Net Perls


另一種方案是用 swift queue 方法
解決方案來源:
 Swift4 Day96:資料結構 Queues
 Swift數據結構-隊列Queue
 原始程式: Queue-Optimized.swift



2019年6月3日 星期一

iOS swift 5: About "Unprintable ASCII character found in source file"

"Unprintable ASCII character found in source file"
有一些中文字在 swift String Source Code中就會發生

解決方案來源:
  iOS - 關於多國語系的那些事(Localization)
  iOS - 在 App 內切換語系

解決方法
 把中文字串放到 Localizable.strings中
  \zh-Hant.lproj\Localizable.strings
 再使用 NSLocalizedString() 取出
  let MyStriug = NSLocalizedString("56022", comment: "")

2019年5月23日 星期四

iOS swift 5: 關於兩個時間之間的時間差

關於兩個時間之間的時間差

解決方案來源:
swift 对日期的处理大全


 var Date1: Date = Date()
 var Date2: Date = Date()
 let nInterval = Date2.timeIntervalSince(Date1)

macOS: 超注音

原有 MacOS上的注音輸入法太難用 都亂猜字
這個輸入法現在用的還算不錯
試用版 30天
每次重新開機後, 第一次切換到中文輸入, 常常會失效
請用滑鼠點一下去切換,
切換成功後,未重新開機前, 使用上都滿順的.

產品: 超注音 for macOS
不限制 MacOS 電腦數量, 但只能用在MacOS不可用在 iPhone, iPad
下載: 超注音 for macOS Ver 1.0.6

購買用於 MacOS電腦
費用: NT$500元


小麥注音輸入法是一套免費macOS專用輸入法
有多種注音的鍵盤配置模式
McBopomofo 小麥注音輸入法

另外還有一套多種平台的免費輸入法
但注音的鍵盤配置只有傳統排列方式
  中州韻輸入法引擎


2019年5月22日 星期三

iOS swift 5: 關於 "UIAlertController actionSheet Exception"


解決方案來源:
1: 顯示警告訊息和選單的 UIAlertControlle
2: Swift 4 Tutorial : Show PopOver with TableView from Scratch (Source file link in 
description)
3: ActionSheet Popover on iPad in Swift

 *** Terminating app due to uncaught exception
 'NSGenericException', reason: 'Your application has presented a
  UIAlertController (<UIAlertController: 0x1160ef600>) of
   style UIAlertControllerStyleActionSheet from XXX.MainViewController (<XXX.MainViewController: 0x116036600>).
   The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. You must provide
   location information for this popover through the alert controller's popoverPresentationController. You must provide
   either a sourceView and sourceRect or a barButtonItem.  If this information is not known when you present the alert

   controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.'

意思就是沒有設置UIAlertController這個彈出窗口的位置信息



 @IBAction func BtnTestOnClick(_ sender: Any)
 {
    
  let alertController = UIAlertController(title: nil, message: "Alert message.", preferredStyle: .actionSheet)

  let defaultAction = UIAlertAction(title: "Default", style: .default, handler: { (alert: UIAlertAction!) -> Void in
    //  Do some action here.
  })

  let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (alert: UIAlertAction!) -> Void in
    //  Do some destructive action here.
  })

  let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { (alert: UIAlertAction!) -> Void in
    //  Do something here upon cancellation.
  })

  alertController.addAction(defaultAction)
  alertController.addAction(deleteAction)
  alertController.addAction(cancelAction)
  
  if let popoverController = alertController.popoverPresentationController {
   popoverController.sourceView = self.view
   popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
   popoverController.permittedArrowDirections = []
  }

  self.present(alertController, animated: true, completion: nil)
 }

iOS swift 5: 關於 "CoreBluetooth XPC connection invalid"

CBCentralManager 這個class, 真的很不容易使用,限制滿多的.

解決方案來源:
ShenYun's Memo: iOS CoreBluetooth swift 2 連線客製化藍芽 BLE 模組 - ( 2 ) 連線
swift - CoreBluetooth XPC connection invalid - Stack Overflow


解說
 1: 專案的 Target -> Capabilities -> Background Modes -> 勾選兩個 Bluetooth LE Accessor

 2: info.plist 要加上
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Play with BLE Compatible devices</string>

 3: 把 CBCentralManager 放在一個 Class中

class TBleDeviceConnect: NSObject, CBCentralManagerDelegate, BPeripheralDelegate
{
   var centralManager: CBCentralManager! = nil
   func centralManagerInit()
   {
    centralManager = CBCentralManager.init(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
   }
}

 4: 把包含 CBCentralManager Class放到 MainViewController 中
     CBCentralManager一定要在 UIViewController
class MainViewController: UIViewController
{
   /* Class Bluetooth CBCentralManager Control */
   var BleDeviceConnect: TBleDeviceConnect! = nil
}

 5: 如果是用在其他的 ViewController
    用 prepare for segue 方法將 CBCentralManager傳遞過去
    請參考 iOS swift 5: About Segue Prepare to NavigationController 

 6: 如果是在 Thread Function 中使用
DispatchQueue.main.async
{
 self.BleDeviceConnect.centralManagerInit()  
}

 7: 同一個Class 中的CBCentralManager 只能呼叫一次 centralManager = CBCentralManager.init(...)
   就會發生 "CoreBluetooth XPC connection invalid"
   解決方法還在尋找中....
   所以現在的做法呼叫 scanForPeripherals來重新掃描 Bluetooth Device











iOS swift 5: 關於 Segue Prepare to NavigationController


解決方案來源:
ios - Swift : prepareForSegue with navigation controller - Stack Overflow

class MainViewController: UIViewController
{
   /* Class Bluetooth CBCentralManager Control */
   var BleConnectDevice: TBleDeviceConnect! = nil
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
     if( segue.identifier == "SegueOptions")
     {
      let destinationNavigationController = segue.destination as! UINavigationController
      let OptionsVc = destinationNavigationController.topViewController as! OptionsTableViewController
      OptionsVc.BleScannerDevice = self.BleScannerDevice
     }
    }
}

class OptionsTableViewController: UITableViewController, UITextFieldDelegate
{
    var BleScannerDevice: TBlueScannerDevice! = nil
    @IBAction func BtnBleOnClick(_ sender: Any)
    {
      DispatchQueue.main.async
      {
       self.BleScannerDevice.CentralManagerInit()
      }
    }
}

class TBleDeviceConnect: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate
{
    var cCentralManager: CBCentralManager! = nil
    func CentralManagerInit()
    {
     cCentralManager = CBCentralManager.init(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
    }
}