在此簡單記錄一下開發過程遇到的問題與參考的資料,GitHub上有demo影片,這邊就不重複放囉~

App demo/GitHub:

在searchBar搜尋,下方會跳出相對應的結果,點擊play button會播放previewUrl music

APP content:

Defined static shared, call function by shared
Defined function with completion
Get API data, decode to JSON format

App function detail:

UISearchResultsUpdating:

URLComponents, URLQueryItem:
當QueryItem不只一項時,可以用component.queryItems來新增

var queries = [String:String]()
queries["term"] = "告五人"
queries["media"] = "music"
queries["country"] = "tw"…

Peter請的飲料喝得很開心,也該來寫作業了吧 :D
一步步紀錄我這次寫作業的流程跟心得

Debug流水帳:
*建立自定義的API:建立google sheet, 註冊sheetDB並貼上sheet url產生api url, 在程式抓api data,用JSON.decode成自定義的struct(飲料訂單的詳細資料), 定義menu.plis …


#5 文字的縮放和字數 — UISlider & UITextField 練習

本次作業重點:
get flickr API key
DatePicker與Slider連動
時間自動快轉的照片輪播功能(Timer)


練習在UIViewController使用collectionView來做照片牆,點選後切換到下一頁顯示大圖,可左右滑動瀏覽其它照片(gif右下角有顯示圖片的index)

Peter的作業範例:

debug流水帳:
*CollectionView的兩種方法
1. collectionViewController:

Delegate不用自己加
FlowLayout可直接用collectionViewLayout as? UICollectionViewFlowLayout
可直接用collectionView.bounds.width計算寬度

2. viewController + collectionView

需要在Main.storyboard把collectionView拉線到viewController, add UICollectionViewDataSource & UICollectionViewDelegate
class viewController也需要加上面2個,修改func(ex:numberOfSections)
FlowLayout需要從Main.storyboar拉UICollectionViewFlowLayout
只能用UIScreen.main.bounds.width計算寬度

*設定collection邊界的方法:

collectionViewFlowLayout.minimumLineSpacing = 3
collectionViewFlowLayout.minimumInteritemSpacing = 3
collectionViewFlowLayout.sectionInset = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)

*設定collection cell大小的方法:
1. 不是改 auto layout:
但元件有設 auto layout的話要記得改 estimatedItemSize = .zero

let width = floor((UIScreen.main.bounds.width — 20 – 3 * 2 ) / 3)
collectionViewFlowLayout.estimatedItemSize = .zero
collectionViewFlowLayout.itemSize = CGSize(width: width, height: width)

2. 是改 auto layout/constraint:

static let width = floor((UIScreen.main.bounds.width - 20 - 3 * 2 ) / 3)
override func awakeFromNib() {
super.awakeFromNib()
imageWidthConstraint.constant = Self.width }

*傳值到下一個頁面,時間點問題

didSelectItemAt跑得比prepare慢,造成傳入的是上一筆的資料

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
photoIndex = indexPath.item
print("didSelectItemAt:\(photoIndex)")
}

*在prepare使用collectionView.indexPathsForSelectedItems來傳值
因為我是在UIViewController用collectionView,無法直接呼叫collectionView,該怎麼辦呢?從storyboard拉就可以了!!

*UISwipeGestureRecognizer拉到imageView,imageView check “User Interaction Enabled”,即可拉@IBAction來做手勢功能(支援上下左右4個方向,要各別寫@IBAction)

GitHub:


愛麗絲追著兔子先生來到了女王的茶會,撲克牌士兵卻要她輸入邀請函上的通行密碼,該怎麼辦才能進去呢?

Debug流水帳:

(1)passcode pass: performSegue to correct page
起點controller拉線到目的地controller, 線設id, performSegue(“id”)

performSegue(withIdentifier: "correctPasscode", sender: self)

(2)passcode fail: show alert
參考Peter的詳細介紹~

let alert = UIAlertController(title: "通行密碼錯誤", message: "請查看邀請函", preferredStyle: .alert)
let action = UIAlertAction(title: "再試一次", style: .default) { (_) in self.initial() }
alert.addAction(action)
present(alert, animated: true, completion: nil)

(3)delay for show guessImage
(不然第4個guessImage會一閃即逝,繼續執行下一步)

let delaySeconds = 0.2
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delaySeconds) { self.checkPasscode() }

(4)Number button全部拉到同一個IBAction,用sender.tag判斷

(5)guessImage mask, 還沒輸入密碼時,底層顯示圖案的剪影
輸入密碼時把image.isHidden = false
我在這邊卡了好幾個小時…2個重點
1.Don’t addSubView in UIImageView
本來我想直接在UIImageView addSubView & mask
最後改成新增UIView, 在此addSubView & mask當剪影
上層的圖案一樣做在UIImageView,用image.isHidden切換
2.做剪影時addSubView & mask的frame要注意
不是跟UIView一樣(只有寬高一樣), 座標要改為(0,0)
不然圖案會貼在天涯海角…QQ

GitHub:


柯南隱身幕後的大功臣:蝴蝶結變聲器,一起來揪出兇手吧!

目的: 學習使用 AVSpeechSynthesizer 講話。
功能需求:
1 唸出 text field 輸入的文字。
2 利用 slider 控制講話的速度。
3 利用 slider 控制講話的音調。
4 利用 label 顯示 slider 的數值。
5 利用 slider 控制講話的音量。
6 加入暫停 & 繼續講話的 button。語音播放完畢把status改回play
7 加入多個講話的 button,點擊不同的 button 會講不同的語言。
比方有講中文,講英文的按鈕。(測試中文時,有時沒聲音,先略過)
8 學貓叫 App,點選 🐈 會唸出 我們一起學貓叫,一起 meow meow meow meow meow

Debug流水帳:

App沒有辦法講話, error: failed to find the AHS sub-server port, Error: 0x10000004
Occured on Xcode 12 Beta 3 & 4, beta 6 fixed

UITextField設定IBAction-Did End on Exit, keyboard key打勾,讓鍵盤能自動出現/消失

speechUtterance.rate 0–1
speechUtterance.volume 0–1
speechUtterance.pitchMultiplier 0.5–2
AVSpeechSynthesisVoice每個國家有預設語音,不一定是男聲/女聲,pitchMultiplier只能讓聲音變低沉,不能讓女->男

synthesizer.pauseSpeaking
synthesizer.continueSpeaking
synthesizer.stopSpeaking
用enum來取代數字表示目前的狀態,一目瞭然且不會打錯字

當語音播放完畢時,把status從pause改回play:
用extension新增delegate AVSpeechSynthesizerDelegate
speechSynthesizer/didFinish/didStart
在viewDidLoad後面設定synthesizer.delegate = self(不然不會動)

GitHub:


七夕倒數計時App~大家趕快幫另一半裝起來!!

Debug流水帳:
Timer.scheduledTimer設定每一秒更新時間
UIDatePicker練習抓時間跟設定初始時間,minimumDate
DateFormatter利用這個來轉換 時間/字串,與呈現的格式
Int(date.timeIntervalSinceNow)可以直接把目標時間減掉現在時間,並換算成秒數,存成Int
Date()是把時間轉成字串的形式,可以用來顯示
Calendar()是把日期變成數字的形式,可以用來寫判斷式

Date時間格式參考的文章:

Calendar 練習參考文章:

GitHub:


目的: 熟悉 table view cell 的重覆利用原理。
基本版:
1 在表格顯示待辦事項,想讀的書,想去玩的國家等,顯示的資料可先寫死在 array 裡。
2 利用點選 cell 顯示 / 取消打勾,當 cell 顯示打勾時表示已完成。請特別 注意表格上下捲動後,原本打勾的 cell 是否變沒打勾,原本沒打勾的 cell 是否變打勾。

進階版:
1 加入新增,修改,刪除功能。
(未實作)2 加入儲存功能,App 重新啟動後,可以看到之前的記錄和已完成的項目。

Debug流水帳:

1.我有新增todoTableViewCell.swift,在TodoListTableViewController.swift把資料todo傳進來然後執行cell.update(or edit/ finish edit),可是finish edit時需要把資料todo傳回去,試了好久都失敗
後來把 finish edit的func寫在TodoListTableViewController.swift就可以直接更新資料(需用座標算出indexPath.row)並且執行cell.update。(本來是寫在todoTableViewCell.swift需要回傳todo)
2.textField要enable Keyboard Key, IBAction用Did End On Exit鍵盤才會自動收起來

todoTableViewCell.swift    func update(){
itemLabel.isHidden = false
editText.isHidden = true
itemLabel.text = todo.item
if todo.check{
accessoryType = .checkmark
}else{
accessoryType = .none
}
return
}
@IBAction func editTodo(_ sender: Any) {
print("edit todo: \(todo.item)")
itemLabel.isHidden = true
editText.isHidden = false
editText.text = todo.item
}
TodoListTableViewController.swift@IBAction func finishEditTextController(_ sender: UITextField) {

let point:CGPoint = sender.convert(.zero, to: tableView)
if let indexPath = tableView.indexPathForRow(at: point){
print("finish edit row \(indexPath.row)")
todoList[indexPath.row].item = sender.text!
tableView.reloadData()
}
}

GitHub:


阿笠博士的冷笑話時間!
作業有2個地方用到outlet collection:選擇題的Label & Button改背景顏色

Debug流水帳:
1. array jokes想做random出題,使用jokes.shuffle()洗牌,出下一題之前把當前的題目刪掉jokes.remove(at: 0),出題時的資料都會在jokes[0]
2. Button select時我會改變背景顏色,不要反灰的動畫(uncheck Highlighted Adjusts Image)
3. UIButton 可用sender.tag來判斷是按到哪一個(要設定tag)
4. 用IBSegueAction傳值到result page
button先拉segue-show to resultViewController, set var data:Int!(一開始沒有值,但以後一定會有值), 從箭頭拉IBSegueAction to 起始ViewController, 把值填入controller?.data, return controller
5. 換頁方式(從箭頭選屬性):Present Modally可選full screen, Cross Dissolve變成全螢幕且無換頁動畫
6. 跳轉到別的controller的方法:(只能選一種)
a.把btn的線拉到目的地controller
b.在目的地controller寫好unWind func,起點controller拉線到Exit即可選擇
c.起點controller拉線到目的地controller, 線設id, btn設action, performSegue(“id”)
d.present//let controller = storyboard?.instantiateViewController(identifier: “xxxView”
e.push ViewController
prepare+identifier+unWind不能同時使用(因為要1才能設identifier)
f. tabBarController?.selectedIndex//print tabBarController?.viewControllers
7. 傳資料到別的controller的方法:
先把btn的線拉到目的地controller(並宣告要傳的變數)
a.IBSegueAction(iOS 13以上): 從線去拉IBSegueAction去寫入要傳的值
b.prepare: let controller = segue.destination去選目的地controller, 直接使用controller傳值(可判斷segue.identifier來傳入不同的值)

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "playSegue"{
if let controller = segue.destination as? badJokesViewController{
controller.correctCount = 5
}
}
}

c.performSegue(sender)可傳到prepare用,要轉型

GitHub:


這次作業用上次製作的小蘭姊姊再加上其他功能:
利用 CABasicAnimation & CAShapeLayer 繪製線條動畫
利用 SpriteKit Particle File 製造下雪的粒子效果
=>下雪好像只能放在背景,目前沒找到方法顯示在上層
利用 CGAffineTransform 縮放,位移,旋轉和鏡像翻轉

Debug流水帳:

利用 …

Alice Fang

你不需要很厲害才能開始,但你需要開始才能很厲害

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store