- 建立新專案[建立專案名稱]
- 點選將Firebase加入iOS專案
4. Firebase會直接下載GoogleService-info.plist, 將此檔案加進Xcode專案中
import Firebase
跟FIRApp.configure()
import Firebase
跟FIRApp.configure()
Create Bridging Header
後, 就可以把新增的專案刪除了. (或是watchOS或是tvOS或是OSX)
下的[Source]裡面的[Header File]自己產生一個標頭檔。 [Your project name]-Bridging-Header
參考Tensorflow官網的安裝流程, 按照以下步驟就可以在Mac上安裝當下最新的版本.
$ virtualenv --system-site-packages ~/tensorflow
$ source ~/tensorflow/bin/activate
(tensorflow)$ # Your prompt should change
(tensorflow)$ export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.11.0-py2-none-any.whl
(tensorflow)$ pip install --upgrade $TF_BINARY_URL
在這邊我是參考在Youtube上看的Tutorial .
(tensorflow)$ pip install --upgrade jupyter
(tensorflow)$ pip install --upgrade Pillow
接著去下載在Github上的課程文件. 執行完下面的指令後, 存取課程文件即可.
(tensorflow)$ jupyter notebook
Virtualenv 可以隔離函數庫需求不同的專案,讓它們不會互相影響。在建立並啟動虛擬環境後,透過 pip 安裝的套件會被放在虛擬環境中,專案就可以擁有一個獨立的環境,也降低了不同版本套件間衝突的可能。
簡而言之,Virtualenv 可以幫你做到:
1. 在沒有權限的情況下安裝新套件
2. 不同專案可以使用不同版本的相同套件
3. 套件版本升級時不會影響其他專案
使用方法
I. 建立虛擬環境
請於命令列模式下輸入下列指令:
$ virtualenv [指定虛擬環境的名稱]
例如下列指令會建立名為 “ENV” 的虛擬環境:
$ virtualenv ENV
預設在建立虛擬環境時,會依賴系統環境中的 site packages,如果想完全不依賴系統的 packages,可以加上參數 –no-site-packages 來建立虛擬環境:
$ virtualenv --no-site-packages [指定虛擬環境的名稱]
II. 啟動虛擬環境
請先切換當前目錄至建立的虛擬環境中。前例中,建立名稱為 “ENV”,則:
$ cd ENV
接著,啟動虛擬環境:
$ source bin/activate
III. 退出虛擬環境
$ deactivate
Reference:
1. https://www.tensorflow.org/
2. https://github.com/sherrym/tf-tutorial/blob/master/install-mac-native.md
通常在一般的專案中一定會有引用Library的時候, 但通常這些library伊多, 就會造成整個資料夾過肥, 但這些檔案都是可以透過使用者在本地端下載即可, 所以在這時就可以使用.gitignore
來將不需要上傳的檔案留在原地.
iOS的部分可以參考以下網址:
1.Objective-C.gitignore
https://github.com/github/gitignore/blob/master/Objective-C.gitignore
2.Stack
http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
3.Gitignore產生器
https://www.gitignore.io/
4.一些程式語言的參考
https://github.com/github/gitignore
可以手動加入.
.gitignore
利用VIM新增檔案即可.
會發現.gitignore
沒有作用, 原因是因為.ignore是後來加進來的 local cache裡面記錄的這個文件是不會忽略的, 所以需要進行緩存清除
$ git rm --cached iLedger.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate
$ git commit -m "Removed the stupid strange file that shouldn't be tracked"
$ git push
為一個從HTML5中開始的一個協定, 瀏覽器和伺服器之間只需要一個handshaking動作就形成了一條快速通道. 兩者之間可以直接互相傳送資料.
這種方式可以避免無謂的浪費伺服器頻寬和資源(舊式的是一段時間後再發一次request.)
一般的http協定不支援長久的連接, 需要不斷地發送request給server更新資料. 而Socket.io可以達到 server 和 client 的雙向溝通, 只要一建立連線 server 可以一直傳訊息給 client.
1.新增Chatroom資料夾並新增package.json
首先新增一個Chatroom的資料夾, 並執行以下指令安裝相關套件.
{
"name": "socket-chat-example",
"version": "0.0.1",
"description": "my first socket.io app",
"dependencies": {}
}
2.安裝express & Socket.io
$npm install --save express
$npm install --save socket.io
3.新增index.js
第一行: 建立一個express module的app function.
第二行: 建立 http 物件
第三行(app.get…): 處理 /
(根目錄) 的 URL GET 要求
sendFile為傳輸檔案, 在這裡傳的是
index.js
所在資料夾底下的index.html
檔
__dirname
: 返回被執行的.js
檔所在資料夾的絕對路徑
最後則是監聽PORT:3000
var app = require('express')();
var http = require('http').Server(app);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
4.建立Socket.io
首先導入Socket.io模組, 因 Socket.io 依賴 HTTP 所以這裡採用一併引入的方式.
socket.io 的核心函數:
emit :用來發射一個事件或者說觸發一個事件, 第一個參數為事件名,第二個參數為要發送的數據, 第三個參數為callback function.
on :用來監聽一個emit 發射的事件, 第一個參數為要監聽的事件名,第二個參數為一個callback function用來接收對方發來的數據.
如上述的介紹這裡的io.on
就可以解釋為使用 on() 函數將特定的事件連接到指定的匿名函數(用來定義當伺服器接收到請求時,該做什麼事情,以及該如何回應), 藉此定義整個資料傳輸過程要如何運作。
而connection/connect事件則為socket.io默認的事件之一.
在這邊socket.io提供的interface是基於事件的,server端監聽”connectione”事件, 如果接收到client的連結請求, 就會進入callback function,而連上的socket就會開始監聽事件,事件的名稱就是’chat message’,當socket發出’chat message’事件時就會執行所寫的程式內容,以下面為例,當觸發事件後會針對io所有的client socket發出’chat message’事件以及其後發所帶的msg參數。
io.on為監聽跟client是否達成連線, 內圈的socket.on則是監聽client的事作而回覆對應的function, 通常這裡的事件也需要在client端建立同樣的事件名稱才行.
var io = require('socket.io')(http);
io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
5.撰寫index.html
在使用 Socket.io 時, 伺服器端與客戶端都必須要有它的 JS 檔案才有辦法運作, 而在上一步已經為server端裝上 Socket.io module了, 接下來就是為client端, 也就是在html中裝上模組.
裝法為在 HTML 頁面中加入以下這段程式碼.
<script src="https://cdn.socket.io/socket.io.js"></script>
在Client端一樣要先註冊一個socket, 並建立監聽Server的’chat message’事件。
完整如下:
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script src="http://code.jquery.com/jquery-1.12.4.js"></script>
<script>
var socket = io.connect();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
</body>
</html>
參考:
1. http://socket.io/get-started/chat/
2. https://blog.allenchou.cc/socket-io-nodejs/
3. http://socket.io/docs/server-api/
先在專案中新增一個”lib”的資料夾, 並新建一個檔案”db.js”. 在這裡我們要使用mongoose來存取MongoDB.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Blog = new Schema({
Username: String,
Article: String,
CreateDate: Date
});
var Comment = new Schema({
Visitor: String,
Comment: String,
MessageID: Schema.Types.ObjectId,
CreateDate: Date
});
mongoose.model( 'Blog', Blog );
mongoose.model( 'Comment', Comment );
mongoose.connect('mongodb://localhost/blog');
在上面我們新增的兩個model, 一個叫Blog, 一個是Comment. 最後一行則是連線到本機端的MongoDB伺服器.
接著修改apis.js初始化資料庫
主要是利用require把資料庫設定載入, 再把定義好的model也加入.
require('../lib/db');
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Blog = mongoose.model('Blog');
var Comment = mongoose.model('Comment');
...
支線任務:
1.brew update error.
問題簡述在此:https://github.com/Homebrew/legacy-homebrew/issues/49879
解決方式就是利用下面兩組command就可以讓brew正常運作.
$sudo chown -R $(whoami):admin /usr/local
$cd $(brew --prefix) && git fetch origin && git reset --hard origin/master
2.MongoDB使用.
安裝:
//Step 1 - Installation ( Don't follow this step if you have already installed MongoDB ):
brew update
brew install mongodb
//Step 2 - Run Mongo Daemon:
mkdir -p /data/db
sudo mongod
//Step 3 - Run Mongo Shell Interface:
mongo
1.首頁列出所有文章
修改routes/index.js阿
利用find()找出資料庫中所有的資料, 並把資料存在blogs物件中, 最後把blogs物件回傳給頁面.
require('../lib/db');
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Blog = mongoose.model('Blog');
router.get('/', function(req, res, next) {
res.locals.username = req.session.name ;
res.locals.authenticated = req.session.logined;
Blog.find(function (err, blogs, count) {
res.render('index', {
title : 'Blog System';
blogs : blogs;
});
});
});
module.exports = router;
修改index.jade, 利用forloop把blogs物件的資料都列印在頁面上.
...
div(style='padding:50px;')
h1= title
for article in blogs
div
div(style='float:left;margin-top:10px;')= article.Username
div(style='float:right;margin-top:10px;')= article.CreateDate
br
div(style='clear:both')= article.Article
a(href='/users/message/'+article.id, style='float:right;margin-top:105px;margin-right:20px;') Leave a message
hr(style='margin-top:110px;')
2.新增文章
views/users/add_article.jade
doctype html
html
head
title= Login
body
div(style='float:left;margin:10px;')
a(href='/') Home
div(style='float:right')
if (username && authenticated)
span #{username}
a(href='/users/profile', style='margin:10px;') Profile
a(href='/users/signout', style='margin:10px;') Sign Out
else
a(href='/users/signin', style='margin:10px;') Log In
a(href='/users/register', style='margin:10px;') Register
a(href='/users/forget', style='margin:10px;') Forgot password
div(style='padding:50px;')
form(name="add", action="/apis/add", method="post")
h1 Add Article
div
p
span Writer:
label(style='padding-right:50px') #{username}
p
textarea(cols="50", rows="25", style='resize : none;', name="Content")
div
span(style='padding-right:377px')
input(type="submit", name="submit", value="Save")
後半段為一個POST方法, 當按下送出按鈕後會執行 /apis/add
的網址.
修改users.js跟apis.js
下面先判斷使用者是否有登入, 如果有登入才能進入新增文章.
#/users/js
router.get('/add_article', function(req, res, next) {
if((!req.session.name) || (!req.session.logined)) {
res.redirect('/');
return;
}
res.locals.username = req.session.name;
res.locals.authenticated = req.session.logined;
res.render('users/add_article');
});
...
#/apis.js
router.post('/add', function(req, res, next) {
if (!req.session.name) {
res.redirect('/');
return;
}
new Blog({
Username: req.session.name,
Article: req.body.Content,
CreateDate: Date.now()
}).save( function( err ){
if (err) {
console.log('Fail to save to DB.');
return;
}
console.log('Save to DB.');
});
res.redirect('/');
});
當用express generation指令後會產生一個node project的資料夾.
規劃網站各個頁面的路由路徑. 首先再次修改app.js: 新增apis路由, 用來負責POST方法的處理.
...
var routes = require('./routes/index');
var users = require('./routes/users');
var apis = require('./routes/apis');
...
app.use('/', routes);
app.use('/users', users);
app.use('/apis', apis);
在我們的設定中, user路由負責處理所有來自/user
網址的要求, 例如新增文章, 會員註冊等. user路由會視用戶的請求而回應不同的資料.
首先在views資料夾中建立一個users資料夾. 裡面會放置所有user路由會用到的頁面.
1.更新index.jade檔
中間的if判斷是會根據使用者是否登入而顯示不一定的連結給使用者.
doctype html
html
head
title= 'Blog System'
body
div(style='float:left;margin:10px;')
a(href='/') Home
div(style='float:right')
if (username && authenticated)
a(href='/users/signout', style='margin:10px;') Sign Out
a(href='/users/add_article', style='margin:10px;') Add Article
a(href='/users/profile', style='margin:10px;') Profile
span #{username}
else
a(href='/users/signin', style='margin:10px;') Log In
a(href='/users/register', style='margin:10px;') Register
a(href='/users/forget', style='margin:10px;') Forgot password
div(style='padding:50px;')
h1= title
2.修改routes/index.js
把session所存的變數所存的變數傳回頁面.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.locals.username = req.session.name ;
res.locals.authenticated = req.session.logined;
res.render( 'index', {title : 'Blog System'});
});
module.exports = router;
1.新增views/users/register.jade
這邊的重點是寫了一個form的POST表單來處理使用者設定帳密
doctype html
html
head
title= Register
body
div(style='float:left;margin:10px;')
a(href='/') Home
div(style='float:right')
a(href='/users/signin', style='margin:10px;') Log In
a(href='/users/forget', style='margin:10px;') Forgot password
div(style='padding:50px;')
form(name="login", action="/apis/login", method="post")
h1 Register
div
p
label(style='padding-right:50px') Username or e-mail
input(type="text", name="user", placeholder='username@example.com')
p
label(style='padding-right:110px') password
input(type="password", name="passwd")
p
span(style='padding-right:260px')
input(type="submit", name="submit", value="Sing Up")
2.修改routes/user.js
新增一個判斷式, 如果logined是true, 代表使用者已經是登入狀態, 就會直接回到首頁.
如果尚未登入則會顯示register.jade的頁面.
var express = require('express');
var router = express.Router();
router.get('/register', function(req, res, next) {
if (req.session.logined) {
res.redirect('/');
return;
}
res.render('users/register');
});
....
3.修改routes/apis.js
新增login函數, 處理註冊頁面傳來的POST方法.
先利用if來判斷使用者是否有輸入帳密, 當其中少一個的話則返回註冊畫面.
當輸入正確就把帳密給儲存到session中. 並把login設定為true代表使用者登入了.
ar express = require('express');
var router = express.Router();
router.post('/login', function(req, res, next) {
if ((!req.body.user) || (!req.body.passwd)) {
res.redirect('register');
return;
}
req.session.name = req.body.user;
req.session.passwd = req.body.passwd;
req.session.logined = true;
res.redirect('/');
});
...
1.使用者登入頁面跟註冊的register.jade比, 只有差在字改成”Login”.
doctype html
html
head
title= Login
body
div(style='float:left;margin:10px;')
a(href='/') Home
div(style='float:right')
a(href='/users/register', style='margin:10px;') Register
a(href='/users/forget', style='margin:10px;') Forgot password
div(style='padding:50px;')
form(name="login", action="/apis/login", method="post")
h1 Login
div
p
label(style='padding-right:50px') Username or e-mail
input(type="text", name="user", placeholder='username@example.com')
p
label(style='padding-right:110px') password
input(type="password", name="passwd")
p
span
a(href='/users/forget', style='padding-right:175px') Forgot password
input(type="submit", name="submit", value="login")
2.修改routes/user.js
...
router.get('/signin', function(req, res, next) {
if (req.session.logined) {
res.redirect('/');
return;
}
res.render('users/signin');
});
1.修改routes/user.js
當登出之後會回到首頁.
...
router.get('/signout', function(req, res, next) {
req.session.logined = false;
res.redirect('/');
res.end();
});
1.views/users/forget/jade
doctype html
html
head
title= 'Forget Password'
body
div(style='float:left;margin:10px;')
a(href='/') Home
div(style='float:right')
a(href='/users/register', style='margin:10px;') Register
a(href='/users/signin', style='margin:10px;') Log In
div(style='padding:50px;')
form(name="login", action="/apis/login", method="post")
h1 Forget Password
div
p
label(style='padding-right:50px') Username or e-mail
input(type="text", name="user", placeholder='username@example.com')
p
span(style='padding-right:210px')
input(type="submit", name="submit", value="Forget Password")
2.修改routes/user.js
...
router.get('/forget', function(req, res, next) {
if (req.session.logined) {
res.redirect('/');
return;
}
res.render('users/forget');
});
主要可以分成下列幾項下去實作.
1. 使用Express Web Framework建置一個網站
2. 使用cookie-based session建立會員機制
3. 新增Jade模板動態產生各個頁面
4. 新建自行設計的Restful API處理自訂路由
5. 使用mongoose控制MongoDB資料庫
$npm install express
$sudo npm install -g express-generator
$express blog-system
執行express指令時, 後方參數為資料夾名稱.
express命令預設會把express和jade套件填入預先產生的package.json
檔中, 所以我們能透過npm install指令把模組安裝完畢.
express 跟 Jade安裝好後就能透過Node.js啟動網站.
$SET DEBUG=blog-system:* & npm start
打開瀏覽器輸入:http://localhost:3000
就可以看到網站了.
Express產生的package.json只有基本的資訊, 這時可利用npm init指令更新專案的相關資訊.
當初入完自訂的資訊之後, 最後會把結果印出來(礙於畫面太長我只截取一小段表示), 如果確定資料正確就會存入package.json中.
在這邊我們要來修改package.json, 把mongoose套件寫到相依模組(dependencies), 這樣才能把文章寫入MongoDB或是從MongoDB中把文章讀取出來.
新增:
cookie-session是為了用來處理會員登入和登出時可以記得當下的status.
"*"
表示版本不限!!!
"cookie-session": "*",
"mongoose": "*"
接著透過”npm install”來安裝新增的套件.
官方定義:
Web Applications
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
主要幫忙解決許多node.js http server 所需要的基本服務,讓開發http service 變得更為容易,不需要像之前需要透過層層模組(module)才有辦法開始編寫自己的程式。
使用Express之後, 它包裝了大多數繁複的HTTP低階操作, 使得我們的程式碼更為精簡.
安裝:
要不要-g
(global)就看個人需求.
npm install -g express
app.<HTTP請求類型>(url,handler{});
app.get('/index', function(req, res){});
request: 連線要求的資訊和方法:
包含了瀏覽器傳來的各種信息,像是query ,body ,headers,都可以通過req對象訪問到。response: 回應連線的資訊和方法
我們一般不從裡面取信息,而是通過它來定制我們向瀏覽器輸出的信息,比如 header 信息,比如想要向瀏覽器輸出的內容。這裡我們調用了它的 #send 方法,向瀏覽器輸出一個字符串。
app.get 可以帶入兩個參數,第一個是路徑名稱設定,第二個為Call back function),這裡面就如同前一篇介紹的 createServer 方法,裡面包含 request跟response 兩個物件。使用者就可以透過瀏覽器,輸入不同的url 切換到不同的頁面,顯示不同的結果。
Example:
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('This is GET method');
res.end;
});
app.listen(process.env.PORT || 12345);
Express在處理一個連線要求時, 利用了middleware的設計, 讓開發者隨時可以自訂或擴充處理的流程. 也可以在適當的時機安插執行一些程式.
為了載入middleware要使用.use
, 一個基本middleware長的樣子如下:
function logger(req, res, next){
console.log(new Date(), req.method, req.url);
next();
}
Middleware 會收到 3 個參數:
1. req 是 Request 物件,存放這此請求的所有資訊
2. res 是 Response 物件,用來回應該請求
3. next 用來控制流程
上面所寫的logger middleware會把每次request的時間, method還有url都列印在終端機上, 然後呼叫next()往下個middleware過去。Express.js的任務, 就是管理你所有middleware chain。
再來我們寫一個叫作hello的middleware:
function hello(req, res, next){
res.write('Hello! \n');
next();
}
將這個middleware加到我們的middlware chain中:
app.use(logger);
app.get('/hello', hello);
`app.use`表示 所有的request都會執行這個middleware
`app.get`表示 只有針對該URL 做 GET request的時候才會執行這個middleware
Reference:
1. http://expressjs.com
2. https://www.youtube.com/watch?v=czmulJ9NBP0
3. https://dca.gitbooks.io/nodejs-tw-wiki-book/content/book/node_express/node_express.html
4. https://stormpath.com/blog/how-to-write-middleware-for-express-apps
5. http://www.jollen.org/blog/2013/11/expressjs-middleware.html
一個基本的網站伺服器流程:
1. 建立HTTP伺服器, 監聽指定的PORT
2. 等待連線
3. 取得新的客戶端連線要求
4. 回傳字串
Example:
var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World!\n');
});
server.listen(12345);
console.log('Server rinning at http:127.0.0.1:88888/');
上述主要是透過Node內建的http模組, 然後使用http.createServer()去建立伺服器. 最後使用listen()方法去監聽指定的port.
當在cmd中執行上述的程式, 就可以在瀏覽器上輸入127.0.0.1:88888, 就能得到hellow world的回傳結果.
Port:通過端口來區分出同一電腦內不同應用或者進程,從而實現一條物理網線(通過分組交換技術-比如internet)同時鏈接多個程序
端口號是一個16位的uint, 所以其範圍為1 to 65535 (對TCP來說, port 0 被保留,不能被使用. 對於UDP來說, source端的端口號是可選的, 為0時表示無端口).
server.listen(12345),程式在執行時,電腦接收到的12345端口的網路消息就會被發送給我們啟動的這個程式.
在這邊只需要記住一點“過濾器本身是一個函數”.
接著要新增一個filter來判斷食物辣或是不辣.
<tr>
<td> {{ food.name }} </td>
<td> {{ food.price }} </td>
<td> {{ food.is_spicy|yes_no:"辣/不辣" }} </td>
<td> {{ food.comment }} </td>
</tr>
首先打開myfilters.py
def yes_no(bool_value, show_str):
if bool_value:
return show_str.partition('/')[0]
else:
return show_str.partition('/')[2]
這邊的函式名稱不需要跟我們定義的過濾器名稱一樣. 但是一樣的話通常會比較好管理.
bool_value
是過濾器的第一個參數,他負責接收模板中pipe符號(|)左邊的值;而show_str
是第二個參數,會用來接收過濾器中的額外參數, 在這邊指的是”辣/不辣”
from django import template
...
register = template.Library()
register.filter('yes_no', yes_no)
在這邊我們使用register的filter函式來幫我們註冊, filter函式第一個變數是”過濾器名稱“, 第二個參數是他對應的過濾器函式
最後就是在模板中載入他
{% load myfilters %}
...
當我們網站模板太多, 而且每一個模板之間共同的部分又很多的時候. 為了一個Don’t repeat yourself的精神. 就可以利用模板繼承的方式把“相似度”高的頁面共同使用同一個模板,方便我們之後的開發.
一個繼承的模板區塊以{% block BLOCKNAME %}
為開頭, 以{% endblock %}
為結尾. 可以填入內容也可以空白. 接著使用{% extends%}
來繼承模板.
{% block BLOCKNAME %}
和{% endblock %}
.{% extends%}
來繼承模板Example:
#base.html
<!doctype html>
<html>
<head>
<title> {% block title %} {% endblock %} </title>
</head>
<body>
<nav>
{% if request.user.is_authenticated %}
<li><a href="#">Hi! {{ request.user }}</a></li>
<li><a href="/restaurants_list/">餐廳列表</a></li>
<li><a href="/accounts/logout/">登出</a></li>
{% else %}
<li><a href="/accounts/login/">登入</a></li>
<li><a href="/accounts/register/">註冊</a></li>
{% endif %}
</nav>
<h2>{% block h2 %}{% endblock %}</h2>
{% block content %}{% endblock %}
</body>
</html>
#index.html
{% extends "base.html" %}
{% block title %} 首頁 {% endblock %}
{% block content %}
<h2>歡迎來到Dinbendon</h2>
{% endblock %}
改成使用Zsh的最大原因是:美觀
雖然網路上也有很多心得分享.
1.先刪除原本的設定檔.
由於之前我有使用其他的設定檔, 先用ls來搜尋目標資料夾, 再利用rm
指令把檔案刪除就可以回覆成初始設定.
2.安裝Zsh
$ curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh
3.選擇主題
安裝完之後,可以對主題/套件做一些調整,Oh-My-Zsh 的主題可以參考
主題/套件主要的設定在~/.zshrc
$ vim ~/.zshrc
修改的內容如下:
ZSH_THEME = "agnoster"
# 將 ZSH_THEME 設為主題的名稱,以 agnoster 主題為例
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
# 中文設定的話要加上這兩行
4.調整字型
當完成主題設定重開iTerm時會顏色區塊中有出現“?”.
所以這裡就必須要調整字體的設定. 先下載背景顏色以及字體 選擇powerline字體
到 iTerm 的設定, 選擇 Profiles -> Colors -> Load Presets,選擇 Solarized Dark
點擊安裝完字體後, iTerm -> Preferences -> Profiles -> Text -> Change Font
兩個字體都選擇Change Font
, 選擇下載的[Menlo for Powerline]
即可.
如果用其他主題的話字體不必調整,請直接按照該主題的設定用就好了.
5.成果
參考網址:
1. http://v123582.github.io/blog/2015/12/06/MacOS-%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83%E8%A8%AD%E5%AE%9A/
2. https://gist.github.com/kevin-smets/8568070
3. http://iphone4.tw/forums/showthread.php?t=206652
這次在練習利用code把UITableView建立出來
1. 在'h'
中宣告delegate 跟 datasource.
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
@property (strong, nonatomic) UITableView *tableview;
@end
@interface ViewController ()
{
NSMutableArray *testArray;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_tableview = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
[self.view addSubview:_tableview];
_tableview.delegate = self;
_tableview.dataSource = self;
testArray = [NSMutableArray arrayWithObjects:@"Iron Man", @"Spiderman", @"Superman", @"Batman", nil];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [testArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}
cell.textLabel.text = [testArray objectAtIndex:indexPath.row];
return cell;
}
@end
Solution:
1. http://stackoverflow.com/questions/25826383/when-to-use-dequeuereusablecellwithidentifier-vs-dequeuereusablecellwithidentifi
2. http://stackoverflow.com/questions/12737860/assertion-failure-in-dequeuereusablecellwithidentifierforindexpath
'+'
後 'add'
便成功加進專案'iAd.h'
檔案並加上 delegate#import <iAd/iAd.h>
@interface ViewController : UIViewController <ADBannerViewDelegate>
or
@interface ViewController ()
{
ADBannerView *bannerTestView;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
bannerTestView = [[ADBannerView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, 320, 50)];
bannerTestView.delegate = self;
// 預設為隱藏 load成功後再在 delegate 的 method 中顯示
bannerTestView.alpha = 0.0;
[self.view addSubview:bannerTestView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
//banner 廣告載入
NSLog(@"Ad Banner did load ad.");
// Show the ad banner.
[UIView animateWithDuration:0.5 animations:^{
bannerTestView.alpha = 1.0;
}];
}
- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
//使用者點了banner廣告後開啟廣告內容畫面
return YES;
}
-(void)bannerViewActionDidFinish:(ADBannerView *)banner
{
//使用者關掉廣告內容畫面
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
//廣告載入錯誤時會呼叫此方法
}
定義一個已知資料型態的別名, 也就是說可以用這個名稱代替設定的資料型態.
typedef int NewINT; //幫int取一個別名叫NewINT
typedef struct Node
{
int no;
char Name[20];
} NewNode;
上述程式可以分成兩個部分來看:
1. 就是定義一個Node的結構.
2. 幫這個Node的結構取一個別名叫NewNode.
結合起來的結論就是“定義一個Node的結構並給他一個等義的別名NewNode”
``````
typedef struct CSNode
{
int no;
char Name[20];
} MyNode, CSTree;
意義:宣告一個CSTree變數, 其為一個CSNode的結構, 別名為MyNode.
這邊練習如何得知目前device的方向以及種類.
在ViewDidLoad中透過traitCollection屬性判斷裝置的種類.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (self.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
NSLog(@"Pad");
} else if (self.traitCollection.userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
NSLog(@"Phone");
}
}
實作- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
,這個方法是當width或是height的regular或compact有變化時呼叫.
iPad的長寬都是regular, 因此iPad方向變化時這個method不會被呼叫.
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
if (newCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {
NSLog(@"Compact width");
}
if (newCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
NSLog(@"Regular width");
}
if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {
NSLog(@"Compact Length");
}
if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassRegular) {
NSLog(@"Regular Length");
}
}
實作- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
:當width或是height的解析度改變時呼叫, 可以在這個方法中透過UIDevice來判斷裝置是橫向還是直向.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
if (orientation == UIDeviceOrientationLandscapeLeft) {
NSLog(@"橫向, 頂端在左邊");
}
if (orientation == UIDeviceOrientationLandscapeRight) {
NSLog(@"橫向, 頂端在右邊");
}
if (orientation == UIDeviceOrientationPortraitUpsideDown) {
NSLog(@"直向但上下顛倒");
}
if (orientation == UIDeviceOrientationPortrait) {
NSLog(@"直向");
}
if (orientation == UIDeviceOrientationUnknown) {
NSLog(@"方向未知");
}
}
Assuming that MyClass is a subclass of BaseClass, the following happens when you call
[super init]
calls the superclass implementation of init with the self argument, which is a MyClass object, not a BaseClass object.
NSURLSession從iOS9開始取代了Connection.
NSURLSessionTask 為抽象類別,它有三個子類別:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask 負責處理網路任務、取得JSON或XML資料,以及上傳下載文件: 範例
NSURL *url = [NSURL URLWithString:@"http://www.imdb.com/"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//NSLog(@"%@", data);
NSLog(@"%@", response);
}];
[dataTask resume];
當完成session的request並執行後發生以下錯誤:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app’s Info.plist file.
A: 原因是因為Applw將原HTTP協議改成了HTTPS協議,使用SSL TLS1.2加密請求數據.
iOS 9新增App Transport Security(ATS)的項目,重點是你在APP中如果有網路的要求時,其網址必定要是加密協定(https),否則連線要求會被禁止。主要是保護您的APP在進行網路通訊時一切資料都是加密,防止被嗅探。
解法:在info.plist中添加
<key>NSAppTransportSecurity</key><dict>
<key>NSAllowsArbitraryLoads</key>
<true/></dict>
參考:
1. http://my.oschina.net/u/2340880/blog/618888
2. http://cms.35g.tw/coding/ios-9-xcode7-http-%E9%8C%AF%E8%AA%A4/
3. https://www.raywenderlich.com/67081/cookbook-using-nsurlsession
提供了一種儲存資料到persistent store的方式, 可以將APP中的物件與資料庫中的表格相對映.
被綁訂在CoreData的物件稱為託管物件. 它用來呈現APP的資料且存於托管物件內容中
託管物件是NSManagedObject的子類別, 代表一個Entity. 在Swift中, 我們在每一個所定義的property前面加上@NSManaged
, 讓他對應Restaurant Entity的屬性(attribute).
import CoreData
class Restaurant:NSManagedObject{
@NSManaged var name:String
@NSManaged var type:String
@NSManaged var location:String
@NSManaged var phoneNumber:String?
@NSManaged var image:NSData?
@NSManaged var isVisited:NSNumber?
@NSManaged var rating:String?
指定Entity給Restaurant類別
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext { ...
}
restaurant = NSEntityDescription.insertNewObjectForEntityForName("Restaurant", inManagedObjectContext: managedObjectContext) as! Restaurant
do {
try managedObjectContext.save()
} catch {
print(error)
return
}
最基本的方式就是用託管物件內容所提供的方法來達成:
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
let fetchResultController = NSFetchedRequest(entityName: "Restaurant")
do {
try managedObjectContext.executefetchRequest(fetchRequest) as! [Restaurant]
} catch {
print(error)
}
}
特別設計用來處理Core Data讀取後所回傳的結果, 並提供資料給表格視圖. 會監看託管物件內容中的物件變更, 然後將變更結果報告給其代理.
import CoreData
class RestaurantTableViewController: UITableViewController, NSFetchedResultsControllerDelegate
var fetchResultController:NSFetchedResultsController!
NSSortDescriptor
可以讓你指定物件間的排序. performFetch()
來執行讀取結果. 完成後我們存取fetchedObjects來存取Restaurant物件. fetchedObjects回傳的事AnyObject屬性所以這邊要強制轉型!let fetchRequest = NSFetchRequest(entityName: "Restaurant")
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do {
try fetchResultController.performFetch()
restaurants = fetchResultController.fetchedObjects as! [Restaurant]
} catch {
print(error)
}
}
如果有任何內容變更, 以下方法會被呼叫.
// MARK: - NSFetchedResultsControllerDelegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {
tableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type {
case .Insert:
if let _newIndexPath = newIndexPath {
tableView.insertRowsAtIndexPaths([_newIndexPath], withRowAnimation: .Fade)
}
case .Delete:
if let _indexPath = indexPath {
tableView.deleteRowsAtIndexPaths([_indexPath], withRowAnimation: .Fade)
}
case .Update:
if let _indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([_indexPath], withRowAnimation: .Fade)
}
default:
tableView.reloadData()
}
restaurants = controller.fetchedObjects as! [Restaurant]
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
tableView.endUpdates()
}
第一個方法是當讀取結果控制器準備開始處理內容變更時呼叫, 我們只是告訴表格視圖 “我們要更新表格了 快準備好”.
第二個方法是當託管物件內容有任何變更時會被呼叫.
第三個方始只是用來告訴表格視圖我們已經完成更新.
呼叫deleteObject的方法來刪除託管物件即可.
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete",handler: { (action, indexPath) -> Void in
// Delete the row from the database
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext {
let restaurantToDelete = self.fetchResultController.objectAtIndexPath(indexPath) as! Restaurant
managedObjectContext.deleteObject(restaurantToDelete)
do {
try managedObjectContext.save()
} catch {
print(error)
}
}
})
簡化了建立搜尋欄位的方法以及搜尋結果的處理. 甚至提供開發者透過自訂的動畫物件, 能更彈性的改變搜尋欄的動畫.
第二行為建立instance, 傳遞nil的值表示搜尋結果會顯示於你正在搜尋的相同視圖中.另外也可以指定其他ViewController來顯示搜尋結果.
第四行為告訴searchResultsUpdater, 哪一個物件會負責更新搜尋結果
var searchController:UISearchController!
searchController = UISearchController(searchResultsController: nil)
tableView.tableHeaderView = searchController.searchBar
searchController.searchResultsUpdater = self
UISearchController在預設環境下並沒有提供任何過濾環境的功能, 必須自己負責實作內容的過濾規則.
在Swift中有一個內建方法叫filter
, 是用來過濾目前的陣列. 只需要在閉包中提供過濾規則即可.
在下面的程式中, 我們使用rangeOfString來檢查是否有相對應的名稱在搜尋文字內, 若是沒有找到就會回傳nil, 最後再檢查是否有找到搜尋文字, 並回傳相對應的Bool value.
func filterContentForSearchText(searchText: String) {
searchResults = restaurants.filter({ (restaurant:Restaurant) -> Bool in
let nameMatch = restaurant.name.rangeOfString(searchText, options: NSStringCompareOptions.CaseInsensitiveSearch)
return nameMatch != nil
})
}
接著要實作如何在畫面上更新與顯示搜尋到的結果.
UISearchResultsUpdating
Delegate.func updateSearchResultsForSearchController(searchController: UISearchController) {
if let searchText = searchController.searchBar.text {
filterContentForSearchText(searchText)
tableView.reloadData()
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active {
return searchResults.count
} else {
return restaurants.count
}
}
// Customize the appearance of the search bar
searchController.searchBar.placeholder = "Search restaurants..."
searchController.searchBar.tintColor = UIColor(red: 100.0/255.0, green: 100.0/255.0, blue: 100.0/255.0, alpha: 1.0)
searchController.searchBar.barTintColor = UIColor(red: 240.0/255.0, green: 240.0/255.0, blue: 240.0/255.0, alpha: 0.6)
如果無法確定變數是否為nil就貿然使用! 運算子,當該變數真的為nil時,就會發生Run-Time Error。所以當在執行Optional型別的運算的時候,最好是先判斷是否為nil值再進行運算。
var a:String = "Hello "
var b:String? = "bbb"
if b != nil {
var c = b!
print(a+c)
}
上面的寫法其實有些麻煩,所以Swift提供了一個語法 - 使用if let或if var。如下例所示,意思是如果b變數不是nil,則c變數等於b變數,並執行後面的statement。如果b變數是nil,則不執行該段statement。if let或if var就是所謂的Optional Binding語法
var a:String = "Hello "
var b:String? = "bbb"
if let c = b {
print(a+c)
}