Saturday, September 23, 2017

UIButton Selector Error with Swift 4: Argument of '#selector' refers to instance method that is not exposed to Objective-C

Swift 4 (Xcode 9.0) does not work with the below Swift 3 code while drawing a UIButton:


The error message says that:

Argument of '#selector' refers to instance method 'click()' that is not exposed to Objective-C

Add '@objc' to expose this instance method to Objective-C

By clicking the "Fix" button, Xcode automatically fix this error by placing '@objc' in front of func click() as below:



The complete code to draw a button that prints a message while touching it:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
        button.center = view.center
        button.setTitle("Press", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.setTitleColor(.cyan, for: .highlighted)
        button.addTarget(self, action: #selector(click), for: UIControlEvents.touchUpInside)
        view.addSubview(button)
    }
    @objc func click() {
        print("Pressed!")
    }

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

}


Saturday, August 19, 2017

Mac Technique: Slide show for pdf file with Preview

To display the slide show for a pdf file in Preview, open the pdf file with Preview and press the hotkey:

[command ⌘] + [shift] + [F] 

or select:

View -> Slideshow





Saturday, August 5, 2017

Run an Xcode project on an iOS device without US$99 Apple Developer account (Configuration for iPhone)

Since iOS 9, it is possible to install the app of an Xcode project to an iOS device without paying for a US$99 Apple Developer account. The steps to download the app to an iPhone/iPad are demonstrated with an iOS 10 device as below:

When press the build and run button of Xcode:

You may see a Could not launch "deviceName" dialog like this:


So follow the instruction on the Xcode dialog to select General -> Profiles & Device Management on the iOS device (iPhone/iPad).


Select the device.
Select "Trust (Apple ID)".
 Select "Trust".

Then the app should run on the iPhone / iPad.

For more information about Xcode configuration, see this:

Friday, August 4, 2017

Visualized custom color in swift code with Color Literal

This post is written with Xcode 8.3.3 and Swift 3.1.

It is very common to set a UIColor with autocomplete as below:



However, there is a better way to select a custom color visually.
Type Co to find Color Literal with Xcode' automatic complete feature.


Then select a color. There are more color options with the color literal than with UIColor.colorName.


So we can see the color in the code instead of strings such as orange, red, ... ect.

There are also literal icons/images. For more information, see:

Be Literal! – iOS App Development

Monday, June 26, 2017

Mac Technique: Show the desktop with F11 key

To show the desktop with Mac, simply press [fn] + [F11] on a Mac's keyboard or press [F11] on an external USB keyboard.

Saturday, May 27, 2017

How to Sort Array, Dictionary, and Array of Tuples

The code below shows how to sort an array, a dictionary or an array of tuples in Swift 3.1 with Xcode 8.3.1 Playground. Sorting can be done in ascending or descending order. A dictionary can be sorted by key or by value.

Note: When sorting a dictionary, the returned type is an array of tuples.

//Array
let array = [3, 5, 9, 7, 4, 1, 2]

let arrayInc = array.sorted()
let arrayDec = array.sorted(by: >)

//Dictionary
let dict = ["A": 123, "B": 789, "C": 567, "D": 432]

print(dict)

let dictKeyInc = dict.sorted(by: <)
let dictKeyDec = dict.sorted(by: >)

print(dictKeyInc)
print(dictKeyDec)

let dictValInc = dict.sorted(by: { $0.value < $1.value })
let dictValDec = dict.sorted(by: { $0.value > $1.value })

print(dictValInc)
print(dictValDec)

for item in dictValDec {
    print("key:\(item.key) value:\(item.value)")
}

//Array of Tuples
let tupleArray = [("A", 123), ("B", 789), ("C", 567), ("D", 432)]

let tupleArrayInc = tupleArray.sorted(by: { $0.1 < $1.1 })


print(tupleArrayInc)


Result:

["B": 789, "A": 123, "C": 567, "D": 432]
[(key: "A", value: 123), (key: "B", value: 789), (key: "C", value: 567), (key: "D", value: 432)]
[(key: "D", value: 432), (key: "C", value: 567), (key: "B", value: 789), (key: "A", value: 123)]
[(key: "A", value: 123), (key: "D", value: 432), (key: "C", value: 567), (key: "B", value: 789)]
[(key: "B", value: 789), (key: "C", value: 567), (key: "D", value: 432), (key: "A", value: 123)]
key:B value:789
key:C value:567
key:D value:432
key:A value:123
[("A", 123), ("D", 432), ("C", 567), ("B", 789)]

Reference

Sort Dictionary by Key Value
cannot assign value of type '[(string, string)]' to type '[string : string]'

Monday, May 22, 2017

Dictionary of Arrays in Swift 3

The code below shows how to include an array in a dictionary in Swift 3.1 with Xcode 8.3.1 Playground.

dictionarySemitones["abc"]=[1,2,3]
dictionarySemitones["def"]=[4,5,6]

print(dictionarySemitones)
print(dictionarySemitones["abc"]!)

Reference:

A Dictionary of Arrays in Swift

Sunday, May 21, 2017

Convert an Integer Array to a String

The code below shows how to convert an integer array to a string in Swift 3.1 with Xcode 8.3.1 Playground.

let arrayInt = [1, 2, 3, 4, 5]
let string = "\(arrayInt)"
print(string)

Reference:

How can I convert an Int array into a String? array in Swift

Conversion from Numbered Musical Notation to Musical Note Letters

The code below converts Numbered Musical Notation (簡譜) to musical note letters using Xcode 8.3.2 playground in Swift 3.1. Techniques used in the code involves:

1. Separating a string into an array
2. Type casting each array element into an integer

3. Converting the numerical notation into a matched letter
4. Joining all letters into a single string

//Twinkle, Twinkle, Little Star 一閃一閃亮晶晶
let numberedNotation = "1 1 5 5 6 6 5"

//1. Separating a string into an array let numberedNoteArray = numberedNotation.components(separatedBy: " ")

let lookupTable = ["C","D","E","F","G","A","B"]

var noteArray = [String]()

for note in numberedNoteArray {
    //2. Type casting each array element into an integer
    let noteInt = Int(note)!

    //3. Converting the numerical notation into a matched letter
    let noteChar = lookupTable[noteInt-1]
    noteArray.append(noteChar)
}

//4. Joining all letters into a single string let noteString = noteArray.joined(separator: " ")

print("\(noteString)")

Result:

C C G G A A G

Saturday, May 13, 2017

Mac Technique: External keyboard configuration for Home and End keys

To configure the [Home] and [End] keys of an external USB keyboard to move the cursor to the front or the end of line in Xcode, try:

1. create the following path:

~/Library/KeyBindings/

2. Create a file called DefaultKeyBinding.dict in this directory with:

{
    "\UF729" = moveToBeginningOfParagraph:; // home
    "\UF72B" = moveToEndOfParagraph:; // end
    "$\UF729" = moveToBeginningOfParagraphAndModifySelection:; // shift-home
    "$\UF72B" = moveToEndOfParagraphAndModifySelection:; // shift-end
}

With this solution, holding a [Shift] key and [Home]/[End] makes a selection for the characters to the beginning or end of the line.

References:

Remap “Home” and “End” to beginning and end of line
Mac OS X and Home / End keys

Sunday, May 7, 2017

Modified Version of AudioKit's Microphone Analysis Example without using the Storyboard

The code below is modified from AudioKit's Microphone Analysis example. The UI components are written programmatically without using the storyboard. The code is developed with Xcode 8.3.2 (Swift 3.1) and iOS 10.

1. Use CocoaPods to include the AudioKit framework by add this line to the Podfile:

pod 'AudioKit'

*************** Updated October 4, 2020 for AudioKit 4 *****************
Type this terminal command:

pod install



*************************** Update 2020 End *****************************

2. Enable the microphone by adding NSMicrophoneUsageDescription and a request string such as "This app needs microphone access." to Info.plist.

Your Info.plist should be like this:


When app is run at the first time, this message should be displayed:



Without this modification, you'll see error like this:


This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.

3. Modify ViewController.swift as below

import UIKit
import AudioKit

class ViewController: UIViewController {
    
    var labelFrequencyValue : UILabel!
    var labelAmplitudeValue : UILabel!
    var labelSharpValue : UILabel!
    var labelFlatValue : UILabel!
    let mic = AKMicrophone()
    var tracker : AKFrequencyTracker!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let labelSing = UILabel(frame: CGRect(x: 0, y: 28, width: view.frame.width, height: 50))
        labelSing.text = "Sing into the Microphone"
        labelSing.textAlignment = .center
        labelSing.font = UIFont.systemFont(ofSize: 24, weight: UIFontWeightBold) //System Font Bold
        labelSing.textColor = UIColor.white
        labelSing.backgroundColor = UIColor(red: 2/255, green: 181/255, blue: 31/255, alpha: 1.0)
        view.addSubview(labelSing)
        
        let labelFrequency = UILabel(frame: CGRect(x: 16, y: 86, width: 85.5, height: 20.5))
        labelFrequency.text = "Frequency:"
        view.addSubview(labelFrequency)
        
        labelFrequencyValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 86, width: 50, height: 20.5))
        labelFrequencyValue.text = "0"
        labelFrequencyValue.textAlignment = .right
        view.addSubview(labelFrequencyValue)
        
        let labelAmplitude = UILabel(frame: CGRect(x: 16, y: 114.5, width: 85.5, height: 20.5))
        labelAmplitude.text = "Amplitude:"
        view.addSubview(labelAmplitude)
        
        labelAmplitudeValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 114.5, width: 50, height: 20.5))
        labelAmplitudeValue.text = "0"
        labelAmplitudeValue.textAlignment = .right
        view.addSubview(labelAmplitudeValue)
        
        let labelSharp = UILabel(frame: CGRect(x: 16, y: 142, width: 111.5, height: 20.5))
        labelSharp.text = "Note (Sharps):"
        view.addSubview(labelSharp)
        
        labelSharpValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 142, width: 50, height: 20.5))
        labelSharpValue.text = "C4"
        labelSharpValue.textAlignment = .right
        view.addSubview(labelSharpValue)
        
        let labelFlat = UILabel(frame: CGRect(x: 16, y: 170.5, width: 94, height: 20.5))
        labelFlat.text = "Note (Flats):"
        view.addSubview(labelFlat)
        
        labelFlatValue = UILabel(frame: CGRect(x: view.frame.width-70, y: 170.5, width: 50, height: 20.5))
        labelFlatValue.text = "F4"
        labelFlatValue.textAlignment = .right
        view.addSubview(labelFlatValue)
        
        let labelPlot = UILabel(frame: CGRect(x: 0, y: 199, width: view.frame.width, height: 21.5))
        labelPlot.text = "Audio Input Plot"
        labelPlot.textAlignment = .center
        view.addSubview(labelPlot)
        

        tracker = AKFrequencyTracker.init(mic)
        let silence = AKBooster(tracker, gain: 0)
        AudioKit.output = silence
        AudioKit.start()
        
        setupPlot()
        
        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateUI), userInfo: nil, repeats: true)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func setupPlot() {
        let audioInputPlot = EZAudioPlot(frame: CGRect(x: 0, y: view.center.y, width: view.frame.width, height: 200))
        
        let plot = AKNodeOutputPlot(mic, frame: audioInputPlot.bounds)
        plot.plotType = .rolling
        plot.shouldFill = true
        plot.shouldMirror = true
        plot.color = UIColor.blue
        audioInputPlot.addSubview(plot)
        view.addSubview(audioInputPlot)
    }
    
    func updateUI() {
        
        let noteFrequencies = [16.35,17.32,18.35,19.45,20.6,21.83,23.12,24.5,25.96,27.5,29.14,30.87]
        let noteNamesWithSharps = ["C", "C","D","D","E","F","F","G","G","A","A","B"]
        let noteNamesWithFlats = ["C", "D","D","E","E","F","G","G","A","A","B","B"]
        
        if tracker.amplitude > 0.1 {
            labelFrequencyValue.text = String(format: "%0.1f", tracker.frequency)
        
            var frequency = Float(tracker.frequency)
            while (frequency > Float(noteFrequencies[noteFrequencies.count-1])){
                frequency = frequency / 2.0
            }
            while (frequency < Float(noteFrequencies[0])) {
                frequency = frequency * 2.0
            }
            
            var minDistance : Float = 10000.0
            var index = 0
            
            for i in 0..<noteFrequencies.count {
                let distance = fabsf(Float(noteFrequencies[i]) - frequency)
                if (distance < minDistance) {
                    index = i
                    minDistance = distance
                }
            }
            let octave = Int(log2f(Float(tracker.frequency) / frequency))
            labelSharpValue.text = "\(noteNamesWithSharps[index])\(octave)"
            labelFlatValue.text = "\(noteNamesWithFlats[index])\(octave)"
        }
        labelAmplitudeValue.text = String(format: "%0.2f", tracker.amplitude)
    }

}

4. Result:



Related Information

AudioKit
Beethoven (Pitch Detection)




Monday, May 1, 2017

UIButton - Update button label when pressed

This post shows how to change the text/title of a button every time the button is pressed.
The code below is written in Swift 3.1 with Xcode 8.3.2 and tested with iOS 10.3.1.

Code:

import UIKit

class ViewController: UIViewController {
    
    var count : Int = 1
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
        button.center = view.center
        button.setTitle("Press", for: UIControlState.normal)
        button.setTitleColor(UIColor.blue, for: UIControlState.normal)
        button.setTitleColor(UIColor.cyan, for: UIControlState.highlighted)
        button.addTarget(self, action: #selector(buttonPressed(sender:)), for: UIControlEvents.touchUpInside)
        view.addSubview(button)
    }
    
    func buttonPressed(sender: UIButton) {
        sender.setTitle("Pressed \(count)", for: UIControlState.normal)
        count = count+1
        print("button pressed!!")
    }
}

Reference:

UIButton - Selector Warning (Update with Swift 3.1)

Sunday, March 12, 2017

Mac Technique: Hotkey to enable Mac's dictionary

To look up a word in Mac's dictionary, highlight the word and press the hotkey:
啟動Mac內建字典的快捷鍵:

[control] + [command ⌘] + [D]

To add a new Traditional Chinese-English dictionary for users in Taiwan or Hong Kong, refer to this:

「教學」Mac 內建字典安裝繁體英漢辭典

Monday, January 30, 2017

Mac Technique: Save Photos from iPhone in a Mac folder with correct date and time information in

While directly dragging photos from a Mac's Photos app to a Finder folder, the date and time that the photos were taken cannot be preserved. Here is the solution to keep the correct date and time of photos:

1. Import photos from an iPhone using the Photos app on a Mac.

2. Select photos and select File -> Export -> Export Unmodified Original For ## Items...


3. Select the folder to export the files.

Tuesday, January 17, 2017

Mac Technique: Steps to Create a Bootable macOS Installer on a USB drive

This post shows how to create a bootable USB drive for macOS Sierra installation. The USB drive was created with a MacBook Pro running OS X 10.8.5 (Mountain Lion), which was installed on a clean SSD by holding down the option key during the first boot after swapping the Mac's hard drive with the SSD.

The steps to create the bootable macOS USB installer are as below:

The original OS X version is Mountain Lion 10.8.5.

Go to the Mac's App Store and get the latest macOS.

Quit the macOS installer.

The installer may be found in the Applications folder of Finder:
Application\Install macOS Sierra

Open the terminal and type this command:

sudo /Applications/Install\ macOS\ Sierra.app/Contents/Resources/createinstallmedia --volume /Volumes/MyUSB/ --applicationpath /Applications/Install\ macOS\ Sierra.app/

For other paths for USB drive or  Sierra.app or for installing other OS X versions, refer to Apple's official document: Create a bootable installer for macOS.


Now the USB drive has been successfully formatted as a bootable macOS installer.

Reference:

Create a bootable installer for macOS