2015年12月30日 星期三

[Qt] JavaScript / Qt 與Facebook Login SDK 使用

在完成這個專案之前,花了很多時間在research Facebook Login SDK,說實話實在是有點難搞懂

畢竟要透過Qt來使用JavaScript,不是這麼多範例可以參考,所以後來就改變策略-------

在html上寫JS然後透過QWebView+QUrl來呼叫網頁達成目的!

需要知道的

1.JavaScript如何轉址

2.JavaScript的FB login SDK流程如何

3.如何取得response與accessToken(最重要!App通行證)

//

第一個網址:

我們需要自動轉址到Facebook Login畫面

第二個網址:

Login成功以後,需要轉址到第二個網址(如果都轉址到第一個網址,會造成無窮迴圈)

取得accessToken

第三個網址:

為了讓Qt取得accessToken我們必須要在網址上面動手腳,

也就是帶accessToken在網址上,因此第二網址再轉址到第三網址


以上為說明
-----------------------------------------------------------------------------------------------------------------------

1.test1.html

<!DOCTYPE html>
<html>
<head>
<title>Facebook Login</title>
<meta charset="UTF-8">
</head>
<body>
<p>測試文字</p>
<script>

var url2 = 'https://www.facebook.com/dialog/oauth?client_id=你的App ID&scope=email,user_birthday&redirect_uri=第二網址';
setTimeout("location.href=url2",1);//轉址目的地,自動轉址秒數
console.log('test2');

</script>
<div id="status">
</div>

</body>
</html>

2.test2.html

<!DOCTYPE html>
<html>
<head>
<title>Facebook Login JavaScript Example</title>
<meta charset="UTF-8">
</head>
<body>
<p>123</p>
<script>

<!--開頭initial-->
var url;


  window.fbAsyncInit = function() {
    // init the FB JS SDK
  FB.init({
    appId      : '你的ID',
    cookie     : true,  // enable cookies to allow the server to access
                        // the session
    xfbml      : true,  // parse social plugins on this page
    version    : 'v2.2' // use version 2.2
  });


  FB.login(function(response) {
  console.log('in FB.login');
    if (response.authResponse) {
     console.log('Welcome!  Fetching your information.... ');
     FB.api('/me', function(response) {
       console.log('Good to see you, ' + response.name + '.');
     });
    } else {
     console.log('User cancelled login or did not fully authorize.');
    }
});

  // This is called with the results from from FB.getLoginStatus().
  function statusChangeCallback(response) {
    console.log('in function: statusChangeCallback');
    console.log(response);
    // The response object is returned with a status field that lets the
    // app know the current login status of the person.
    // Full docs on the response object can be found in the documentation
    // for FB.getLoginStatus().
    if (response.status === 'connected') {
      // Logged into your app and Facebook.
 var uid = response.authResponse.userID;
      var accessToken = response.authResponse.accessToken;
 console.log(accessToken);
 //alert(accessToken);

testAPI(response);
 //document.write(accessToken);
 //testAPI(response);
 document.getElementById('status').innerHTML = 'Entry success';
    } else if (response.status === 'not_authorized') {
      // The person is logged into Facebook, but not your app.
 FB.login();
      document.getElementById('status').innerHTML = 'Please log ' +
        'into this app.';
    } else {
      // The person is not logged into Facebook, so we're not sure if
      // they are logged into this app or not.
 console.log('in unknown');
      document.getElementById('status').innerHTML = 'facebook login failed';
    }
  }

  // This function is called when someone finishes with the Login
  // Button.  See the onlogin handler attached to it in the sample
  // code below.
  function checkLoginState() {
    console.log('in checkLoginState');
    FB.getLoginStatus(function(response) {
      statusChangeCallback(response);
    });
  }

  FB.getLoginStatus(function(response) {
    console.log('in getLoginStatus');
    statusChangeCallback(response);
  });

  };



  // Load the SDK asynchronously
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

  // Here we run a very simple test of the Graph API after login is
  // successful.  See statusChangeCallback() for when this call is made.

  function testAPI(response) {
  /*
    console.log('Welcome!  Fetching your information.... ');
    FB.api('/me', function(response) {
      console.log('Successful login for: ' + response.name);
      document.getElementById('status').innerHTML =
        'Thanks for logging in, ' + response.name + '!';
    });
*/

var accessToken = response.authResponse.accessToken;
  url = "第三個網址(轉址的)?" + accessToken;
setTimeout("location.href=url",1);
  }

window["checkLoginState()"];
console.log('get login');
</script>



<!--
<script src="http://connect.facebook.net/zh_TW/all.js"></script>
  Below we include the Login Button social plugin. This button uses
  the JavaScript SDK to present a graphical Login button that triggers
  the FB.login() function when clicked.
-->

<!--
<fb:login-button scope="public_profile,email" onlogin="checkLoginState();">
</fb:login-button>
-->
<div id="status">
</div>

</body>
</html>

3.test3.html

<!DOCTYPE html>
<html>
<head>
<title>Facebook Login</title>
<meta charset="UTF-8">
</head>
<body>
<p>test3</p>
<script>
 //URL
    var url = location.href;
   
    //取得問號之後的值
    var temp = url.split("?");

    //將值再度分開
    var vars = temp[1].split("&");

    //一一顯示出來
    for (var i = 0; i < vars.length; i++) {
     alert(vars[i]);
    };

 

</script>
<div id="status">
</div>

</body>
</html>


可以看到test.html是轉址透過

https://www.facebook.com/dialog/oauth?

+

client_id=你申請的App ID(數字)

+

&scope=email,user_birthday(你要的權限有多少)

+

&redirect_uri=轉址一號站


第二部分則是

facebook SDK

:

// Load the SDK asynchronously
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)) return;
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));





大概內容就是三種情況

1.connected : 已經連線,成功登入(取得accessToken)

2.unauthorized: 已經登入,但是沒有允許權限開啟(呼叫login() )

3.unknown:尚未登入(呼叫login() )

如果輸入都正確就會取得accessToken,但這時候要仍然是在網頁上面要怎樣讓Qt去取得呢?

在Qt的QWebView裡面有一個Signal--urlchanged(),這意思是甚麼呢?就是當網址有所變動時(轉

址)會發送信號,因此我們就可以透過這個signal去取得轉址後的網址(當然是有帶accessToken)

如此,就達成目的了!

*******************************************************************************

照這方法做一定會遇到無法轉址的問題

"無法xxxxx,畫布...."之類的錯誤訊息







要把你要轉址的網址(可以超過一個)放在valid OAuth redirect URL這一欄裡面,這樣才能夠正常

運作!

the END

2015年12月1日 星期二

[Qt] Unicode / UTF8 / Big5 轉碼 QTextCodec 各種轉換相關

在寫Qt時候遇到問題實在搞太久了,把他記錄起來以免忘記!

1.不同system的locale會不同(中文windows vs 英文windows),因此搭配

QTextCodec::setCodecForLocale會有所不同,toLocal8bit()亦同

2.Latin1 其實就是ISO 8859-1

3.toLocal8bit()會因為locale而有所不同:

QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));

通常會有所不同,但如果是填utf8的話就跟.toUtf8()是一樣的功能囉!

4.QString內部就是Unicode編碼(最大相容於各系統),因此如果要從QString取出東西,通常都會使

用fromUnicode()

--------------------------------------------------------------------------------------------------------------------------

使用QString -->UTF8 則使用toUtf8()即可

使用QString -->Big5 (中文系統) 使用toLocal8bit()即可

使用QString -->Big5(英文系統)   ???? 翻遍網路都沒有找到,這個問題搞了兩天,終於....

使用系統: Windows POS Ready 2009 English version 2 SP3

使用語言: Qt(類C)

首先,必須要了解的是一個中文 =  兩組碼 ,一個英文 = 一組碼,如果要中英混合的話呢?

這邊我利用toUtf8()來分析length判斷目前字元是中文還是英文

(先用QString的mid()切割每一個字元)

中文的解決方法比較麻煩,英文則相較簡單。

  //英文or符號
 QString str2 = str_Trans.mid(i,1);
 QByteArray byte_array = str2.toLatin1();

 unsigned char command_english[str2.length()];
 memcpy(command_english,byte_array,str2.length());

透過toLatin1()就可以把QString轉為QByteArray,再用memcpy copy進去就可以囉!


中文:

 QTextCodec *codec = QTextCodec::codecForName("Big5");
 QByteArray b_array = codec->fromUnicode(str_Trans);

 unsigned char command_chinese[4];
 memcpy(command_chinese,b_array,4);

 for(int i = 0 ; i< sizeof(b_array); i++)
{
     int tmp = (int)command_chinese[i];
     qDebug()<<tmp;
}

透過codec的fromUnicode來轉碼,轉完就變成QByteArray,一樣透過memcpy copy

這邊如果run到沒有安裝Qt的系統(或電腦)一下就會Crash,why ??

一開始上網查,結果查到都是qcncodec4.dll (這是給簡體字的GBK,GB....),繁體必須用

qtwcodec4.dll,放進去創建的Plugins/Codecs裡面,結果!!還是Crash!!

後來把lib檔(.a)跟dll檔(.dll)整個資料夾都Copy進去以後就可以了~



















至於要放在哪邊呢? 放在你的.exe檔同一個資料夾即可


*********************************************************************************

不要再相信網路上加上

QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));

類似這種(UTF-8換GBK,GB2312之類的)

就可以跑這種奇怪的解法,好好去了解這些編碼才是實際。

Unicode可以容納最多編碼方式

UTF-8則是unicode的一種表現方式

Latin1是歐美編碼

Big5是繁中

簡體則有許多GBK,GB2312...之類的

CSDN上這篇 也解釋得不錯,可以參考看看

希望下一個遇到Unicode to Big5的人可以順利解決

2015年11月17日 星期二

[Qt] FTP續傳功能

FTP續傳功能主要分為 上傳 / 下載

在開始看續傳功能之前必須要先了解,FTP有所謂的"原生指令"(Raw Command)

Raw Command包括:

TYPE - set transfer type
      TYPE I - image (binary data)
PASV - enter passive mode
PORT - open a data port
    PORT a1,a2,a3,a4,p1,p2
SIZE - return the size of a file
REST - Sets the point at which a file transfer should start
    REST position
RETR - Begins transmission of a file from the remote host.
    RETR remote-filename

Common commandsABOR - abort a file transfer
CWD - change working directory
DELE - delete a remote file
LIST - list remote files
MDTM - return the modification time of a file
MKD - make a remote directory
NLST - name list of remote directory
PASS - send password


PWD - print working directory
QUIT - terminate the connection
RETR - retrieve a remote file
RMD - remove a remote directory
RNFR - rename from
RNTO - rename to
SITE - site-specific commands

STOR - store a file on the remote host

USER - send username
Less common commandsACCT* - send account information
APPE - append to a remote file
CDUP - CWD to the parent of the current directory
HELP - return help on using the server
MODE - set transfer mode
NOOP - do nothing
REIN* - reinitialize the connection
STAT - return server status
STOU - store a file uniquely
STRU - set file transfer structure
SYST - return system type


每一個Command各自代表其中一個意思,而續傳只需要其中五個指令而已

(上傳)
1、rawCommand("TYPE I");設置傳輸數據的類型:二進制數據或ASCII
2、rawCommand("PASV");設置服務器被動接收方式。發送PASV命令後,服務器會返回自己開?的數據傳輸的端口,等待客戶端連接進行數據傳輸。返回的數據格式?:“227 Entering Passive Mode (192, 168, 2, 18, 118, 32)”,然後從返回的信息裏面或去相關的信息,ftp服務器的IP地址:192.168.2.18;ftp服務器開?的用于數據傳輸的端口:118*256 + 32 = 30240;獲得該信息後就需要建立TcpSocket的通信鏈路,連接ftp服務器。
3、rawCommand("APPE  remote-file-path");設置服務器端remote-file-path追加的方式。如果此時改文件不存在,則服務器端會創建一個。
 4、完成上述流程後,就可以打開本地文件進行讀取,並通過tcpsocket鏈路發送出去(write)。

(下載)
1、rawCommand("TYPE I");設置傳輸數據的類型:二進制數據或ASCII
2、rawCommand("PASV");設置服務器被動接收方式。發送PASV命令後,服務器會返回自己開?的數據傳輸的端口,等待客戶端連接進行數據傳輸。返回的數據格式?:“227 Entering Passive Mode (192, 168, 2, 18, 118, 32)”,然後從返回的信息裏面或去相關的信息,ftp服務器的IP地址:192.168.2.18;ftp服務器開啟的用于數據傳輸的端口:118*256 + 32 = 30240;獲得該信息後就需要建立TcpSocket的通信鏈路,連接ftp服務器。
3、rawCommand("REST  size");該命令設置ftp服務器從本地文件的哪個地方開始進行數據傳輸。
4、rawCommand("RETR  remote-file-path");開始從遠程主機傳輸文件。

        文件上傳時在設置APPE返回之後,就可以打開本地文件進行上傳;文件下載時,收到PASV的返回信息建立tcpsocket的連接後,需要建立readyRead()的信號槽,在該槽函數中實現數據的讀取。

上面是網路上找來的資料

*所謂的PASV = passive mode 被動接收模式
*size就是要從哪一點開始續傳
*端口就是Port

End(有待補充)

2015年11月4日 星期三

[Qt] Regular Expression 正規表示法 用法

正規表示法在檢查字串是否合法時,非常好用

剛好寫到就寫起來紀錄一下。

下面有三個Regular Expression

1. QRegExp regNum("\\d*");

接受所有數字(原本為\d,但C++ compiler需要再一個\ 所以就變成double \ )

2.QRegExp regCode39("[A-Z0-9+-./$%]*");

接受大寫英文,數字+-./$%,記得後面要加上* 這樣所有字元才都能檢查到,沒有*會產生邏輯上的錯誤

3.  QRegExp regCode128("[\\w\\S]*");

\w 是接受所有大小寫英文(a~z + A~Z)

\S則是接受所有特殊字元(除空白)

\s則是各種空白( \r\t\n\f)

用法很簡單,只要使用exactMatch(QString)就可以知道是否正確了

可以加速,不需要for跑很久!

END

2015年10月14日 星期三

[Qt] Read Qss file (讀取QSS檔案)

qss file內容約為:

CImageBrowseDialog {
border: none;
background-image: url(Skins/CImageBrowseDialog/Common/004-back.jpg);
}

QPushButton#previousButton {
qproperty-geometry: rect(15 656 98 70);
background-image: url(Skins/CImageBrowseDialog/Common/004-normal_25.jpg);
}

QPushButton#previousButton:pressed {
background-image: url(Skins/CImageBrowseDialog/Common/004-push_25.jpg);
}

QPushButton#previousButton:disabled {
background-image: url(Skins/CImageBrowseDialog/Common/004-gray_25.jpg);
}

QPushButton#nextButton {
qproperty-geometry: rect(907 658 97 70);
background-image: url(Skins/CImageBrowseDialog/Common/004-normal_28.jpg);
}

QPushButton#nextButton:pressed {
background-image: url(Skins/CImageBrowseDialog/Common/004-push_28.jpg);
}

QPushButton#nextButton:disabled {
background-image: url(Skins/CImageBrowseDialog/Common/004-gray_28.jpg);
}

QLabel#PreLabel
{
  border: none;
  qproperty-geometry: rect(15 720 98 40);
  color : white;
}
 
QLabel#NextLabel
{
  border: none;
  qproperty-geometry: rect(907 720 98 40);
  color : white;
}


該如何讀取呢?

在main.cpp裡面

#include <QtGui/QApplication>
#include "mainwindow.h"


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    QFile file("D:\\MyCode\\Qt\\CImageBrowseDialog(QSS file)\\COMMON.QSS");
    file.open(QFile::ReadOnly);
    QString styleSheet(file.readAll());
    qDebug()<<styleSheet;
    a.setStyleSheet(styleSheet);


    return a.exec();
}


讀取路徑可以設為相對(這邊是絕對路徑)

file.readAll()就會把所有的都讀近QString當中,最後設定application的setStyleSheet()即可完成

(當然UI介面要有相對應的button/label,objectName也要相同就是了)

END

2015年10月12日 星期一

[Qt] SQLite + Qt 組合

開始要把Qt搭配SQLite了,

遇到一些問題把他先記錄起來

(*為你的project name)

1. *.pro檔案裡面需要加入

QT += sql 

2. *.h需要

 #include <QSqlDatabase>

3.使用如下:

    QSqlDatabase my_db;
    my_db = QSqlDatabase::addDatabase("QSQLITE","Qt_DB1");
    //QSLITE = 參數(driver)
    //Qt_DB1 = database名稱(請勿使用default,也就是不設,這樣使用其他的程式也呼叫default的時候會覆蓋)
    my_db.setDatabaseName("test.sqlite");
    if(my_db.open()) QMessageBox::information(0,"Ok","Connection ok");
    else QMessageBox::information(0,"Error","some error.");

(使用QMessageBox也是要include,這邊就不多贅述 // sqlite檔案使用firefox 外掛SQLite來創建)

2015年10月7日 星期三

[Qt] qmake 如何跳到非C槽(ex D槽,E槽....)



相信一定有人也遇到這樣的問題,所以上來搜尋

其實很簡單 ,跳到其他槽時候不要加cd即可

end

2015年9月25日 星期五

[Qt] Qt creator 的qmake使用方式








第一步找出Qt的 command & Project位置

 第二步:cd 到該位置


第三步: 輸入 qmake -project


 第四步:可以看到自動生成.pro檔,然後qmake (該檔案名稱),pro

就會生成下面圖片的這些檔案,完成





























即可看到所有檔案都出現囉!


2015年9月22日 星期二

[Qt] 初次使用Qt Creator

Qt是一個跨平台語言,也就是說可以放在iOS,Android,Linux,Mac,Windows....等等都共通的語言

Qt Creator 我使用的版本是: 4.6.2


語法目前觀察是滿像C++,不過我對C++沒有很熟就是了0rz

整個project會包含.ui / .h / .cpp  / .pro


.h就是標頭檔
.ui就是ui介面
.cpp就是主程式
.pro不太清楚但應該是類似qmakefile 用的

如果你要輸出的話,會必須要把下圖中這些檔案放進debug資料夾,否則你的exe檔無法執行


(error debug)如果遇到Id return 1 exit status的話,那就代表你已經在執行程式,但又在run一次,把目前程式關掉,才能run!

END

2015年8月21日 星期五

[iOS] 上架須知

終於把iOS App上架了!

上架需要的幾個步驟花了我不少時間,在這邊記錄一下以免忘記

step1 : (當然首先已經有帳號了)

https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/wa/signOutCompleted

進入itunes connect 選擇 我的app



選左上角的+號,就會有出現一串需要設定的包刮icon,版權,截圖等等.....

特別需要注意的就是截圖,

如果你的版本支援5.5吋(iphone 6 plus),4.7吋(iphone 6),4吋(5/5s),3.5吋(4/4s)的話,

那必須要把這些截圖都對應尺寸放上去,否則是無法通關的!

接下來就是程式上傳,該如何上傳呢??

這邊是使用xcode 6.x上傳,以下為示範


在模擬器的部份必須要選擇iOS Device,然後才能在product 選擇 Archieve *沒選的話會變

成灰色不能按

在build/version的部份 也必須要特別注意(圖片中間偏右邊可以看到)

如果你已經上傳同一個version / build的話,那當然就是不能重複上傳,不然會有error!

進入到archieve的部份代表上傳已經完成到一半了(如果沒有錯誤就上傳完成,回到itunes

connect,選擇版本然後就可以上傳,不過需要花一些些時間才看的到,剛開始上傳是看不到

的)

這邊遇到一些問題ex: need 120x 120 icon 之類的



app icon + launch Image 這幾個是一定要填入的icon,

可以透過這個網站來製作


可以看一下是否有images.xcassets的警告,如果有很可能會過不了!

都完成以後,就可以順利上架囉! 審核時間還不確定多長,只能說一定比android長!


2015年7月13日 星期一

[iOS] 滑動式可模糊文字顯示框(PAC Blurry Scroll View)


一樣是逛cocoa controls逛到的一個元件,覺得很有用因此就把它拿來用了

(不過沒想到有點難操作0rz)

PACScrollView

步驟只有三到四步,基本上也是把檔案先copy進去專案,然後  #import (有import才能夠使用該元件)

不外乎就github上基本的操作:

Basic usage

First, import PACBlurryScrollView.h header in th e .h file.
XIB way :
just add a scrollView in your Xib file and change the class type to PACBlurryScrollView
then, add the following code
    [pacScrollView setBackgroundImage:[UIImage imageNamed:@"bg.jpg"]];
Code way
    PACBlurryScrollView *pacScrollView = [[PACBlurryScrollView alloc] initWithFrame:self.view.bounds];
    [pacScrollView setBackgroundImage:[UIImage imageNamed:@"bg.jpg"]];
    [self.view addSubview:pacScrollView];
Don't forget to add content to your scrollView !
上面意思是說有兩種方法
1.XIB檔拉一個ScrollView,改class為PACBlurryScrollView,程式碼裡面增加這一行:
[pacScrollView setBackgroundImage:[UIImage imageNamed:@"bg.jpg"]];

2.第二種當然就是使用coding

    PACBlurryScrollView *pacScrollView = [[PACBlurryScrollView alloc] initWithFrame:self.view.bounds];
    [pacScrollView setBackgroundImage:[UIImage imageNamed:@"bg.jpg"]];
    [self.view addSubview:pacScrollView];

初始化一個pacScrollview格式,然後後續差不多,最後一定要addSubView!


以為這樣教學就結束了嗎?

如果你希望一開始的模糊感就有的話,就需要調整一下他原本的.m file

在PACBlurryScrollView.m這邊有一行

[_backgroundImageLayer setOpacity:(1.0f - (self.contentOffset.y / (self.contentSize.height - self.frame.size.height)))];

[_backgroundBlurryImageLayer setOpacity:(1.0f - _backgroundImageLayer.opacity)];

把1.0f調整為0.5f 就可以看出效果囉!

本次的教學就到這邊 END

2015年7月9日 星期四

[iOS] 處理iphone4/4s 與iphone5/5s 和 iphone 6/6 plus 尺寸不同問題(xib file)

在開發iOS的時候一定會遇到當不同機身(4s,5s,6,6plus)介面會跑掉的問題!

而這個問題也讓我苦惱了一個禮拜,網路上沒有非常好的解法(stackoverflow有,不過你要看

的懂他寫的,而且解法非完美要結合另一篇才行)


首先,概念其實很簡單,為每一個xib檔案配一個不同尺寸的xib檔(也就是各尺寸各一)

然後,判定現在的機型是哪一種,iphone 4s ? 5/5s? 6? 6 plus? 決定要套用哪一個xib檔案

該怎樣判定呢?

先定義螢幕長寬吧!

#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)
#define SCREEN_MAX_LENGTH (MAX(SCREEN_WIDTH, SCREEN_HEIGHT))
#define SCREEN_MIN_LENGTH (MIN(SCREEN_WIDTH, SCREEN_HEIGHT))

#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
#define IS_RETINA ([[UIScreen mainScreen] scale] >= 2.0)

#define iPhone4s_or_less (IS_IPHONE && SCREEN_MAX_LENGTH < 568.0)
#define iphone5 (IS_IPHONE && SCREEN_MAX_LENGTH == 568.0)
#define iphone6 (IS_IPHONE && SCREEN_MAX_LENGTH == 667.0)

#define iphone6plus (IS_IPHONE && SCREEN_MAX_LENGTH == 736.0)

接著我們需要在initWithNibName()這邊判斷

現在是4s,5,6,6plus

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if (iphone6plus) {
        NSLog(@"iphone 6 +");
        self = [super initWithNibName:@"Page2_6_plus" bundle:nibBundleOrNil];
    }
    else if(iphone6)
    {
        NSLog(@"iphone 6");
        self = [super initWithNibName:@"Page2_6" bundle:nibBundleOrNil];
    }
    else if (iphone5) {
        NSLog(@"iphone 5/5s ");
        self = [super initWithNibName:@"Page2_5s" bundle:nibBundleOrNil];
    }
    else{
        NSLog(@"iphone 4s ");
        self = [super initWithNibName:@"Page2" bundle:nibBundleOrNil];
    }
    
    return self;
    
}

當然,也是要把xib檔案複製->改名為相對應的名稱,這樣一來,就完成囉!

end

[iOS] 特效進度條(ASProgressPopupView)的使用方法

在Code4app網站中找到一個不錯的特效包--ASProgressPopupView


點此連結到Code4app


上面寫的很簡單,但實際使用起來卻不是這樣

"使用方法:


self.progressView.font = [UIFont fontWithName:@"Futura-CondensedExtraBold" size:26]; 
self.progressView.popUpViewAnimatedColors = @[[UIColor redColor], [UIColor orangeColor], [UIColor greenColor]]; 
self.progressView.popUpViewCornerRadius = 16.0;"

實際在github上的描述是:

github

How to use it

It’s very simple. Drag a UIProgressView into your Storyboard/nib and set its class to ASProgressPopUpView – that's it. The example below demonstrates how to customize the appearance.
self.progressView.font = [UIFont fontWithName:@"Futura-CondensedExtraBold" size:26];
self.progressView.popUpViewAnimatedColors = @[[UIColor redColor], [UIColor orangeColor], [UIColor greenColor]];
self.progressView.popUpViewCornerRadius = 16.0;
To show the popUpView, just call:
[self.progressView showPopUpViewAnimated:YES];
And to hide:
[self.progressView hidePopUpViewAnimated:YES];
You update the value exactly as you would normally use a UIProgressView, just update theprogress property self.progressView.progress = 0.31;
詳細的可以去github上面看

這邊的教學:
1.先把下面這四個檔案copy進去專案,然後在你要寫的.m file裡面新增
#import "ASValueTrackingSlider.h"
2.開啟你的xib file,拉一個slider進去畫面,把class改為ASValueTrackingSlider
然後把該元件拉到.m file產生一個名叫slider1的物件

可以看到他的屬性是ASValueTrackingSlider,不是uislider,是的話就搞錯囉!

接著我們就可以使用slider1來做事情(控制他)

3.為了讓我的進度條上面顯示的文字最後面都有個"V",所以這邊要增加三行(第一行是宣告)

    NSNumberFormatter *voltFormatter = [[NSNumberFormatter alloc] init];
    [voltFormatter setPositiveSuffix:@"V"];
    [voltFormatter setNegativeSuffix:@"V"];
再來就是一些基本的設定
    self.slider1.popUpViewCornerRadius = 12.0;
    [self.slider1 setNumberFormatter:voltFormatter];
    [self.slider1 setMaxFractionDigitsDisplayed:0];
    self.slider1.popUpViewColor = [UIColor colorWithHue:0.55 saturation:0.8 brightness:0.9 alpha:0.7];
    self.slider1.font = [UIFont fontWithName:@"GillSans-Bold" size:22];
    self.slider1.popUpViewAnimatedColors = @[[UIColor redColor], [UIColor orangeColor], [UIColor greenColor]];
//這邊是讓我前中後段的顏色有不同

    [self.slider1 showPopUpViewAnimated:YES];
//這一段一定要show不然他不會顯示文字出來


如果是要有%數的話則改用這段

    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setNumberStyle:NSNumberFormatterPercentStyle];
    [self.slider1 setNumberFormatter:formatter];
    self.slider1.popUpViewAnimatedColors = @[[UIColor greenColor], [UIColor orangeColor], [UIColor redColor]];
   self.slider1.font = [UIFont fontWithName:@"Futura-CondensedExtraBold" size:26];
    [self.slider1 showPopUpViewAnimated:YES];

又或者你想要到某個區段就顯示某些文字的話,請加入這一行

self.slider1.dataSource = self;

然後加入這一段

- (NSString *)slider:(ASValueTrackingSlider *)slider stringForValue:(float)value;
{
    value = roundf(value);
    NSString *s;
    if (value < 30) {
        s = @"Warning!! Low Voltage";
    } else if (value > 29.0 && value < 50.0) {
        s = [NSString stringWithFormat:@"😎 %@ 😎", [slider.numberFormatter stringFromNumber:@(value)]];
    } else if (value >= 50.0) {
        s = [NSString stringWithFormat:@"%dV", value];
    }
    return s;
}

最後,完成版本就像這樣~



























然後如果要調整他的弧度的話(邊角弧度)


self.slider2.popUpViewCornerRadius = 12.0;

這邊可以調整


希望大家都能夠開心使用,(畢竟作者沒有講很清楚,我也摸了一會兒才會),end

2015年6月9日 星期二

[iOS] 如何儲存使用者設定?(NSUserDefaults) / 類似Android的SharedPreferences

相信大家寫的app如果有登入之類的東西一定會遇到一個問題,就是儲存使用者設定!

不然每次叫使用者重新輸入應該會崩潰吧?!


--------------------------------------------------------------------------------------------------------------------------


正文開始:

Step1:

先到.h檔 (看是appdelegate.h or ViewController.h)

宣告一個NSUserDefaults屬性的變數

//
//  AppDelegate.h
//
//  Created by daniel on 2015/4/29.
//  Copyright (c) 2015 daniel. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "AsyncUdpSocket.h"
#define global ((AppDelegate *)[[UIApplication sharedApplication] delegate])

@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
    NSUserDefaults *userDefaults;

}

這樣我們就可以在.m file內使用該變數(userDefaults)



Step2:

在.m file內的viewdidload()這裡面(反正就是第一個讀到的地方)

先初始化我們的userDefaults,這一部很重要!!沒有初始化會叫不出儲存的東西!!

初始化:userDefaults = [NSUserDefaults standardUserDefaults];

接下來就是儲存/讀取

儲存:

    NSString *string = @"192.168.0.1";
    [userDefaults setObject:string forKey:@"store IP"];

    NSArray *array = @[@"123",@"456"];
    [userDefaults setObject:array forKey:@"Array"];

    BOOL isValid = YES;
    [userDefaults setBool: isValid forKey:@"isValid"];

    int number = 100;
    [userDefaults setInteger:number forKey:@"number"];

可以看到上述四種不同的類型(nsstring,nsarray,BOOL,int)

大致上都是一樣的,差別只是在於setXXX(類型)

格式:(儲存)

[NSUserDefaults型態變數  setObject(或其他型態): 變數名稱 forKey: @"隨意取key 名稱"]

格式:(讀取)

[NSUserDefaults型態變數 stringforKey: @"隨意取key 名稱"]

(或是valueforKey:)

範例:NSString *store_ip = [userDefaults stringForKey:@"store IP"];


*****非常重要*****

在儲存完畢以後,要記得!!

[NSUserDefaults型態變數 synchronize];

如果沒有這行,就不會真正儲存到硬碟裡面!

完成以後,就可以透過這樣的方式來儲存使用者設定了:)

END


2015年6月4日 星期四

[iOS] NSData NSMutableData append / NSString to Char 等問題解法

不是我要說iOS,轉來轉去每個都好麻煩0rz

好吧!

首先,這邊目標是:結合NSString + char[] ,最後變成NSData輸出

NSString *myString = @"123abcdfhhhhhhh";
char *aa = [myString UTF8String];

NSLog(@"%s",aa);
NSLog(@"%lu",sizeof(aa));

NSLog(@"%lu",(unsigned long)[myString length]);

這邊設定一個string ,然後讓他轉utf8格式放到char指標裡面


nslog會顯示

2015-06-04 15:55:32.897 v1.0[4462:60b] 123abcdfhhhhhhh
2015-06-04 15:55:32.898 v1.0[4462:60b] 4

2015-06-04 15:55:32.900 v1.0[4462:60b] 15

所以要特別注意sizeof()並不是mystring分開後的個數,而是4byte 

因為這個錯誤搞了半天0rz.....

到這邊已經很順利的把nsstring 改為char了,但是要怎樣把這兩樣結合呢?


    const unsigned char two[]    = {80 ,67 ,77 ,83 , 2 ,2};//第二個char[]
    
    NSMutableData * muData = [[NSMutableData alloc] init];//用來結合的容器
    
    NSMutableData * data = [NSData dataWithBytes:two length:sizeof(two)];
    NSMutableData * data2 = [NSData dataWithBytes:aa length:[myString length]];//這邊要用length


------------------結合--------------------

   [muData appendData:data];
   [muData appendData:data2];

------------------結合--------------------

    NSString *strData = [[NSString alloc]initWithData:muData encoding:NSUTF8StringEncoding];
   
    NSLog(@"%@",strData);

最後輸出成NSString 方便nslog看成果為如何?


2015-06-04 15:55:43.650 v1.0[4462:60b] PCMS123abcdfhhhhhhh


最後使用


NSData *aData = [strData dataUsingEncoding: NSASCIIStringEncoding];

轉為NSData~


完成!

end

2015年5月26日 星期二

[iOS]NSString & EXC_BAD_ACCESS ERROR solution

問題:

使用全域變數NSString *string,使用時候出現exc_BAD_ACCESS???


答案:


@property (nonatomic,retain)NSString *string;

不可以使用assign 要使用retain!


解決!

end

[iOS] Switch Case Error : switch case in protected scope

錯誤代碼:switch case in protected scope

解決方法:

case 1:

{
    code....

}

break;

case 2:

{
   code2....
 
}

break;


解決! end

2015年5月15日 星期五

[iOS] 可展開/收和型的tableView

在找expantable listview,結果找了半天沒有找到,

原來是android叫做listview,ios則是叫做tableview!

這次在.h file完全不用作任何事情,所以就不說.h file了!

原始檔案在這邊:

http://code4app.com/ios/ExpansionTableView/5121cac66803fae949000002

是參考這個弄出來的


------------------------------------------------------------------------------------------------------------------

首先,如果你有開ARC的話,那不需要release/dealloc之類的

第一步:

在viewDidLoad裡面加入

    NSString *path  = [[NSBundle mainBundle] pathForResource:@"ExpansionTableTestData" ofType:@"plist"];//指定要去哪一個檔案名稱,檔案類型找,放進去path裡面

    _dataList = [[NSMutableArray alloc] initWithContentsOfFile:path];


pathForResource這邊是檔案名稱 

ofType則是檔案類型

所以我們要讀取的列表就是ExpansionTableTestData.plist這個檔案


顯然還沒宣告_dataList,所以在前面interface下面就要先宣告一個

NSMutableArray *_dataList;


像這樣

@interface Page3 ()<UITableViewDataSource,UITabBarDelegate>
{
    NSMutableArray *_dataList;
}
@property (assign)BOOL isOpen;
@property (nonatomic,retain) NSIndexPath *selectIndex;
@property (nonatomic,retain) IBOutlet UITableView *expansionTableView;

@end

@implementation Page3
@synthesize isOpen,selectIndex;

紅色部分為新增的


再來就是新增加建構子,

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [_dataList count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (self.isOpen) {
        if (self.selectIndex.section == section) {
            return [[[_dataList objectAtIndex:section] objectForKey:@"list"] count]+1;;
        }
    }
    return 1;
}

//iphone 5以下可以用float,5s~6 plus CGFloat
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 40;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.isOpen&&self.selectIndex.section == indexPath.section&&indexPath.row!=0) {
        static NSString *CellIdentifier = @"Cell2";
        Cell2 *cell = (Cell2*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        
        if (!cell) {
            cell = [[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
        }
        NSArray *list = [[_dataList objectAtIndex:self.selectIndex.section] objectForKey:@"list"];
        cell.titleLabel.text = [list objectAtIndex:indexPath.row-1];
        return cell;
    }else
    {
        static NSString *CellIdentifier = @"Cell1";
        Cell1 *cell = (Cell1*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (!cell) {
            cell = [[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil] objectAtIndex:0];
        }
        NSString *name = [[_dataList objectAtIndex:indexPath.section] objectForKey:@"name"];
        cell.titleLabel.text = name;
        [cell changeArrowWithUp:([self.selectIndex isEqual:indexPath]?YES:NO)];
        return cell;
    }
}


#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    //展開items,開始判斷式
    if (indexPath.row == 0) {
        //第一層
        if ([indexPath isEqual:self.selectIndex]) {
            self.isOpen = NO;
            [self didSelectCellRowFirstDo:NO nextDo:NO];//進入下一段函式
            self.selectIndex = nil;
            
        }else
        {
            if (!self.selectIndex) {
                self.selectIndex = indexPath;
                [self didSelectCellRowFirstDo:YES nextDo:NO];
                
            }else
            {
                
                [self didSelectCellRowFirstDo:NO nextDo:YES];
            }
        }
        
    }else
    {
        //第二層
        NSDictionary *dic = [_dataList objectAtIndex:indexPath.section];
        NSArray *list = [dic objectForKey:@"list"];
        NSString *item = [list objectAtIndex:indexPath.row-1];
        //顯示點擊的item
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:item message:nil delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil];
        [alert show];//跳出訊息
    }
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}


- (void)didSelectCellRowFirstDo:(BOOL)firstDoInsert nextDo:(BOOL)nextDoInsert
{
    self.isOpen = firstDoInsert;
    
    Cell1 *cell = (Cell1 *)[self.expansionTableView cellForRowAtIndexPath:self.selectIndex];
    [cell changeArrowWithUp:firstDoInsert];//疑似把箭頭往上?
    
    [self.expansionTableView beginUpdates];
    
    int section = self.selectIndex.section;
    int contentCount = [[[_dataList objectAtIndex:section] objectForKey:@"list"] count];
    NSMutableArray* rowToInsert = [[NSMutableArray alloc] init];
    for (NSUInteger i = 1; i < contentCount + 1; i++) { //第二層有幾行 content count
        NSIndexPath* indexPathToInsert = [NSIndexPath indexPathForRow:i inSection:section];
        [rowToInsert addObject:indexPathToInsert];
    }
    
    if (firstDoInsert)
    {   [self.expansionTableView insertRowsAtIndexPaths:rowToInsert withRowAnimation:UITableViewRowAnimationTop];
    }
    else
    {
        [self.expansionTableView deleteRowsAtIndexPaths:rowToInsert withRowAnimation:UITableViewRowAnimationTop];
    }
    
    //[rowToInsert release];
    
    [self.expansionTableView endUpdates];
    if (nextDoInsert) {
        self.isOpen = YES;
        self.selectIndex = [self.expansionTableView indexPathForSelectedRow];
        [self didSelectCellRowFirstDo:YES nextDo:NO];
    }
    if (self.isOpen) [self.expansionTableView scrollToNearestSelectedRowAtScrollPosition:UITableViewScrollPositionTop animated:YES];
}

然後我們要建構xib檔,拉出一個table view (記得要選autolayer)

然後要與@property (nonatomic,retain) IBOutlet UITableView *expansionTableView;

這一行做連結(滑鼠右鍵拉過去connect)

















然後還要把dataSource/delegate也連結到file's Owner

如果要修改列表的內容,就要去expansiontabletestdata.plist那邊修改喔!

完整檔案在code4app可以下載,這邊就不附上囉!

end