2015年4月15日 星期三

Multiple SSH keys on your computer

平常用慣了Git來管理程式, 但是
公司跟個人的開發環境應該分清楚一點比較好.

例如說我平常自己個人在github或是bitbucket上開發
一開始會follow教學, 先產生SSH key
cd ~/.ssh
ssh-keygen -t rsa -C "user_name@whatever.com"

然後copy 公鑰id_rsa.pub到server上, 像是:
pbcopy < ~/.ssh/id_rsa.pub
或是手動複製
cat ~/.ssh/id_rsa.pub

但是要新增另外一把SSH key在同一台機器上, 可以使用下面的方法:
ssh-keygen -t rsa -f ~/.ssh/accountB -C "user_name@whatever.com"

然後新增並編輯config:
touch ~/.ssh/config
vi config

config內容大概長得像這樣:
Host bitbucket.org
User git
Hostname bitbucket.org
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa

Host bitbucket-accountB
User git
Hostname bitbucket.org
PreferredAuthentications publickey
IdentitiesOnly yes
IdentityFile ~/.ssh/accountB

如果SSH遇到connection refused很可能是因為拿了錯的key去連.
這時我們需要指定SSH連線用的key:
ssh -vv -i ~/.ssh/accountB -p port username@server.com
-vv:如果有permission denied的話要加
-i ~/.ssh/accountB:指定SSH key的位置
-p:不指定通常是22 port

如果要操作git的話:
git clone git@bitbucket-accountB:username/project.git

還有還有,
要有多個git username以及email的話
在各個repo底下去設定:
git config user.name "your name"
git config user.email "your@email.com"

參考連結:
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

2015年4月8日 星期三

VirtualBox設定Windows vm的網路

環境
host: Mac OS X 10.9.5
vm: Windows XP 32 bit

1. 設定網路
選擇NAT, 介面卡選Intel PRO/1000 MT Desktop這個, 然後按確定


2. 設定分享資料夾
指定分享的資料夾路徑, 確認Auto mount有勾選, 然後按確定.

有興趣可以參考youtube的教學, 到此為止應該都還順利,
但是教學影片中的作者電腦已經出現"網路磁碟機"(Network Drives).
我花了n個小時看了各種介紹, 有些要用cmd, 有些教說要新增"網路磁碟機", 但是都沒用!!!

3. NOTE! 非常重要的一步!
切換到windows vm畫面, 選擇視窗最上方一排Devices > Insert Guest Additions CD image…

安裝完一切都ok了!
快去看看"網路磁碟"終於出現啦.

那接下來才能把剛剛Intel官網下載的驅動程式給copy到桌面,並加以安裝.

呼~希望我浪費的時間, 可以省下你寶貴的時間, 嗚嗚...
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

2015年4月7日 星期二

Android Studio使用Cling library顯示支援UPnP的裝置


Cling介紹
Cling的四大模組:
Cling Core
實作UPnP 1.0協議, 可以在網路上宣告其服務, 也可用來寫一個control point尋找附近的UPnP裝置並使用其服務. 值得注意的是, Cling 2.x 要求API 15以上, 舊版Cling 1.x 才支援更舊的OS版本.

Cling Support
擴展UPnP服務的模組, 像是media server, renderer, 或是NAT port mapping等等.

Cling Workbench
是一個for桌機的應用程式

Cling MediaRenderer
基於gstreamer的一個獨立的UPnP MediaRenderer.

我們只要list出附近有支援UPnP的裝置,
所以只會用到Cling Core的部分
source code可以參考Github的連結


gradle file裡設定"repositories"跟"dependencies"
可以參考Github上的文件說明

repositories {
     mavenCentral()
     maven {
          url "http://4thline.org/m2"
     }
}

dependencies {
     // Cling
     compile group: 'org.fourthline.cling', name: 'cling-core', version:'2.0.1'
     compile group: 'org.eclipse.jetty', name: 'jetty-server', version:'8.1.12.v20130726'
     compile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'8.1.12.v20130726'
     compile group: 'org.eclipse.jetty', name: 'jetty-client', version:'8.1.12.v20130726'
}



AndroidManifest.xml的設定
1. 要加入使用權限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

2. 聲明我們所使用的service 
<application
     ...
     <service android:name="org.fourthline.cling.android.AndroidUpnpServiceImpl" />
     <service android:name="com.wistron.wimira.testcling.BrowserUpnpService" />
</application>

source code import進來, 上述檔案設定完後, project應該就可以正常build了!
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

2014年7月11日 星期五

[Android] 使用UIL套件並將網路資源儲存在手機端

來源:eleZeta@flickr, CC BY-ND 2.0

前言
Android的開發者一定都知道Universal Image Loader (UIL)套件
也一定知道套件初始化的一些基本設置

1
2
3
4
5
 ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                .discCacheExtraOptions(800, 800, CompressFormat.JPEG, 25, null)
                              .memoryCache(new WeakMemoryCache())
                              .threadPoolSize(5)
                              .build();

我這邊將預設圖片的長寬範圍在800x800以內, 壓縮率取到25%, 依據不同使用情況會有不同的設定.


問題
這樣子的設定並無法讓網路資源保存在手機上,
也就是說當app被關閉, 下次再開啟時又要再跟server要一次了.
如果這份資源不會有所變動,  我們又想節省server的負載以及手機的網路使用流量.
那就要考慮這樣的實作了.


實作
剛剛的config我們再增加一個discCache的設定
1
.discCache(new FileCountLimitedDiscCache(cacheDir, new Md5FileNameGenerator(), 500))

參數有cache路徑, file name, 以及cache資源的上限, 這裡設定500個項目.

好吧, 那cacheDir又要如何取得呢?
作者nostra13在github上有提到 (討論串)

1
2
3
4
5
6
7
8
9
File cacheDir;
if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
    // 偵測有SD卡
    cacheDir = new File(Environment.getExternalStorageDirectory(), "data/someapp/cache");
} else {
    // 偵測沒SD卡
    cacheDir = context.getCacheDir();
}
cacheDir.mkdirs();

在Activity中有 getFileDir() 和 getCacheDir() 這兩個方法
可以取得目前app在手機裡儲存空間的預設文件路徑
getFileDir() 對應到 /data/data/appname/files
getCacheDir() 對應到 /data/data/appname/cache

Returns the absolute path to the application specific cache directory on the filesystem. 
These files will be ones that get deleted first when the device runs low on storage
There is no guarantee when these files will be deleted. 
Note: you should not rely on the system deleting these files for you; 
you should always have a reasonable maximum, such as 1 MB, 
for the amount of space you consume with cache files, and prune those files when exceeding that space.

官方文件對 getCacheDir() 的使用也有溫馨的提醒
除存在cache的資料會在系統偵測空間不足時首先被移除,
所以系統不保證什麼時候會做刪除動作, 開發者也不應該依賴這個機制.
而是應該設置個合理的cache上限, 然後在超過時進行資料刪減的動作.

當然也要記得在project的Manifest檔裡面設定SD卡的存取權限
<!-- if you want to allow UIL to cache images on SD card -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

[Android] google-play-services_lib 的小問題

在Android的開發環境中, 設定google-play-service的library是滿稀鬆平常的事情.
不過我後來發現, 常常在Android SDK Manager做了update後, 這error又出現了!



如果又遇到Unable to resolve target 'android-x' (無論是8還是9還是什麼鬼數字)
不妨先將google-play-services的專案移除, 並重新import.
然後進入project的properties選單, 確認一下project build target跟我們自己的專案是不是有一樣!


我的專案使用的API level是14, 重新勾選後就正常啦!
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

2014年7月10日 星期四

iOS7 UITableViewCell 分隔線偏移的問題

前言
除了先前文章提到過iOS7 的UITableViewCell如果想讓image貼齊靠左的技巧.
還有分隔線啦!


問題
我想這直接看圖片是再清楚不過了!



實作

1
2
if ([tableView respondsToSelector:@selector(setSeparatorInset:)])
    [tableView setSeparatorInset:UIEdgeInsetsZero];

夠簡單了吧 ;)
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

救命的一行文 - MAC OS X使用Wireshark卻發現Interface找不到

在MAC OS X上想要使用Wireshark一定要有X11的環境,
可惜從Mountain Lion開始就被Apple給移除了.
因此要下載XQuartz來解決, 官方網站

不知打哪個版本開始, Wireshark只有user權限,
所以無法直接去選擇我們想要攔截的interface.
所以現在請打開command line, 並輸入
sudo chown  /dev/bpf*
這樣就會有權限可以選interface囉!

參考連結:justincarmony

2014年3月3日 星期一

[Android] 打包, 發佈APK到GooglePlay

打包APK

1. Project (Right Click) > Android Tools > Export Signed Application Package...




2. Click ""Browse" to choose the project we want to export. Then click "Next"


3. Suppose we have keystore already. Enter the password of the keystore.
Keystore可以把它想作是鑰匙圈, 裡面可存放不同名字(Alias)的key. 
通常適用於同一個公司/團體下, 有不同的產品. (一個Keystore多個key)


4. Again, enter the password of the key.


5. Then we will see the key information, and then click "Finish".


發佈APK

Log into Google Play Developer Console > "APK" tab view

That's it~
Evernote helps you remember everything and get organized effortlessly. Download Evernote.

2014年1月21日 星期二

iOS SDK Release Notes for iOS 7.1 beta 4

iOS7.1 beta4今早release了,翻了一下release note跟大家分享一下。

Bluetooth Known Issues 

32-bit apps running on a 64-bit device cannot attach to BTServer.

已知問題32位元的app在iPhone 5S會有藍牙連線的問題,已經beta好幾版都沒有改= =+


CFNetwork Notes

A compatibility behavior has been added to address an issue where some web servers would send the wrong Content-Length value for "Content-Encoding: gzip" content. Previously, NSURLConnection and NSURLSession would send a "network connection was lost" / NSURLErrorNetworkConnectionLost (-1005) error in this situation. The compatibility behavior applies only if the Content-Length value exactly matches the expanded gzip'd content. It won't apply for "off by 1" or similar miscounting.

針對一些網站的 content encoding是 gzip情況下又給了錯誤的Content-Length,有提昇兼容性。
先前NSURLConnection跟NSURLSession會拿到 network connection was lost或是 NSURLErrorNetworkConnectionLost (-1005)的錯誤。
但兼容性的情況僅限於gzip的內容擴展後要與Content-Length一樣才可以。


Messages Fixed in iOS 7.1 beta 4

Messages no longer indicates a send failure immediately after sending.

這版看起來唯一有解掉的就是Message送出訊息不會馬上出現失敗的訊息。


Safari Notes

A property, minimal-ui, has been added for the viewport meta tag key that allows minimizing the top and bottom bars on the iPhone as the page loads. While on a page using minimal-ui, tapping the top bar brings the bars back. Tapping back in the content dismisses them again.
For example, use <meta name="viewport" content="width=1024, minimal-ui">.

meta標籤viewport新增了minimal-ui的屬性,讓Safari載入頁面時可以最小化上下狀態欄,點擊可以再次顯示/隱藏狀態欄。


UIKit Known Issues

Bar button background images are ignored in apps built and deployed to iOS7.1 when they are set using UIBarButtonItem setBackgroundImage:forState:style:barMetrics: with UIBarButtonItemStyleBordered as the style argument.
Workaround: Use UIBarButtonItemStylePlain or UIBarButtonItemStyleAny in this case, or use UIBarButtonItem setBackgroundImage:forState:barMetrics:.

Bar Button背景圖片在使用 setBackgroundImage:forState:style:barMetrics:中類型設定為UIBarButtonItemStyleBordered的時候會失效。
解決方法是設定成 UIBarButtonItemStylePlain或是 UIBarButtonItemStyleAny抑或是使用 setBackgroundImage:forState:barMetrics:

If a UITextField or a UILabel that is baseline aligned with constraints has attributes that change after the constraints have been added, the layout may be incorrect. The exception to this is -setFont: on UILabel, which should work as expected.
Workaround: Avoid making changes in UITextField or UILabel after adding baseline-alignment constraints. If you must make changes, you should remove the constraints and then reapply them afterward. Note that this is a performance hit, so don't do it unless it is necessary.

UITextField跟UILabel如果設定了baseline aligned之後再設定其他屬性,這樣會使得layout錯誤。(除了UILabel設定字型是沒問題的)
目前要避免這樣的問題就是要調整先後順序,把baseline aligned放到最後設定。

The backIndicatorTransitionMaskImage from a storyboard or a xib will not be interpreted correctly at runtime.
Workaround: Set the backIndicatorTransitionMaskImage in code.

導航欄的按鈕圖案若是透過storyboard或是xib去設定的會無法作用,解決方法就是手動在code裡面設定

Sent from Evernote

2013年12月12日 星期四

iOS App 串接 Dropbox API第一次就上手

前言
最近發現我好飢渴阿(大誤!)
很多網路服務API都很有趣, 也很完整, 高手滿坑滿谷.
就像Steve Jobs說的 'stay hungry stay foolish'
今天就來玩玩Dropbox的API吧~


申請APP帳號
Dropbox開發者首頁(Link)
左側有App Console的標籤, 點擊然後選擇Create app.

> 要先同意使用者條款跟隱私權政策


> 然後要選擇app的類型.
1. 先選右邊的Dropbox API app
2. 然後選擇Files and datastores (注意!選Datastores only沒法對data做access動作!)
    就像這位仁兄遇到的問題一樣.
3. 我只允許我的app access自己app所建立的資料夾
4. 設定一下app名稱 (之後還可以更改)


> 建立完成, 複製App key以及App secret等等要用.


> 一切就緒, 讓我們切換到Core API的標籤頁, 點選Install SDK


先從Example Project試試
剛剛下載的iOS SDK裡面有個examples > DBRoulette 開啟DBRoulette.xcodeproj
把剛剛的App key跟App secret 貼到 DBRouletteAppDelegate.m
注意!root = kDBRootAppFolder/kDBRootDropbox 不要用預設的nil
不然跑起來會有錯誤訊息:
[WARNING] DropboxSDK: error making request to /1/metadata/(null) - (400) Expected 'root' ...

然後DBRoulette-Info.plist右鍵點選Open as > Source code
注意!db-(your app key) 記得保留'db-' .

設定完成來執行看看吧!

一開始可能會有一堆錯誤視窗跑出來.
因為資料夾是空的呀~不過登入自己的Dropbox可以發現.

Dropbox有建立了一個新的資料夾了, 丟一些照片進去就大功告成了!
下台一鞠躬, 謝謝大家XD
Sent from Evernote

2013年12月11日 星期三

iOS App share with Google Plus

前言
Google Plus的分享功能要加到iOS專案裡面不會太困難.
官方網站的原文教學

環境設定
下載Google+ iOS SDK (official link)
檢查一下
AssetsLibrary.framework
Foundation.framework
CoreLocation.framework
CoreMotion.framework
CoreGraphics.framework
CoreText.framework
MediaPlayer.framework
Security.framework
SystemConfiguration.framework
UIKit.framework

並從下載回來的Google+ iOS SDK資料夾內
拖曳&import
GooglePlus.framework
GoogleOpenSource.framework



建立一個API project
舊版的設定頁面


註冊一個app


完成後應該長得像這樣


新版的設定頁面
啟用Google+API


註冊一個app


填寫必要資訊


完成後應該長得像這樣



程式
在AppDelegate.m

#import 
#import  
static NSString * const kClientID = @"blahblahblah.apps.googleusercontent.com";

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Set app's client ID for |GPPSignIn| and |GPPShare|.
    [GPPSignIn sharedInstance].clientID = kClientID;
    ...
}

在ViewController.m
@interface YourViewController ()  {
     ...
- (void)viewDidLoad {
     [super viewDidLoad];
     [GPPShare sharedInstance].delegate = self;
}
     ...
- (void)gplusBtnPressed:(id)sender {   
    id shareBuilder = [[GPPShare sharedInstance] shareDialog];
   
    // This line will fill out the title, description, and thumbnail of the item
    // you're sharing based on the URL you included.
    //[shareBuilder setURLToShare:[NSURL URLWithString:@"The url you want to share"]];
    [shareBuilder setContentDeepLinkID:@"DeepLinkID"];
    [shareBuilder setTitle:@" 標題 "
               description:@" 描述"]
              thumbnailURL:[NSURL URLWithString:[@"縮圖網址"]]];

    [shareBuilder setPrefillText:msg];
    [shareBuilder open];
}

#pragma mark - GPPShareDelegate
- (void)finishedSharing:(BOOL)shared {
   
}

- (void)reportAuthStatus {
    if ([GPPSignIn sharedInstance].authentication) {
        NSLog(@"Status: Authenticated");
    } else {
        // To authenticate, use Google+ sign-in button.
        NSLog(@"Status: Not authenticated");
    }
}

如果分享時發生Error 404 Not Found, 那最有可能就是client ID沒有符合

希望大家都分享順利囉~
Sent from Evernote

[Android] share with Facebook SDK 3.5

前言
原本在開發者沙盒(SandBox)模式下一切都還滿順利的,
不料上到Google Play發現原本的功能不work, 
花了一些時間才發現小細節.
自己記錄一下, 同時也給需要的人參考一下.

以下我就自己的開發歷程, 
先從沙盒模式說起, 
最後再談關閉沙盒模式要注意的地方.


下載&Import FaceBook SDK
可以參考官方文件的原文教學
我是用SDK 3.5.2 (Official Download Link / Github SDK3.6)

I'll skip the installation of Facebook in emulator here.

> Right click / 'File' on top

> Existing Android Code Into Workspace

> make sure the 'facebook' project is checked

如果Project有Error可以先檢查android-support-v4.jar的版本是否一致
最快的方法就是把最新的jar在project list裡面copy起來,
然後把其他project有用到的都先delete掉, 再paste上去.


環境設定
接下來看看我們的project要做哪些設定
1. library

2. Manifest設定
     
     
        
    

3. res/values/strings.xml加上
(your Facebook App ID)


Facebook上建立應用程式後, copy App ID

4. 在Facebook App設定頁面加上Hash Key
Mac User
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64



程式 - Facebook分享
// facebook
private static final String PERMISSION = "publish_actions";
private UiLifecycleHelper uiHelper;
private boolean canPresentShareDialog;
private PendingAction pendingAction = PendingAction.NONE;
private enum PendingAction {
     NONE,
    POST_PHOTO,
    POST_STATUS_UPDATE
}

// Facebook Login
Session.openActiveSession(this, true, new Session.StatusCallback()
{
     // callback when session changes state
     @Override
     public void call(final Session session, SessionState state, Exception exception)
     {
          if (session.isOpened())
          {   
               // make request to the /me API
               Request request = Request.newMeRequest(session, new Request.GraphUserCallback()
               {
                    @Override
                    public void onCompleted(GraphUser user, Response response)
                    {
                         // If the response is successful
                        Log.d("facebook", "GraphUserCallback" + user.getId()+" " + response.toString());
                                        
                        if (session == Session.getActiveSession())
                        {
                              if (user != null)
                             {
                                   performPublish(PendingAction.POST_STATUS_UPDATE, canPresentShareDialog);
                             }
                        }
                                        
                        if (response.getError() != null)
                        {
                              // Handle errors, will do so later.
                        }
                     }
               });
               request.executeAsync();
          }
     }
});

private void performPublish(PendingAction action, boolean allowNoSession) {
     Session session = Session.getActiveSession();
    if (session != null) {
        pendingAction = action;
        if (hasPublishPermission()) {
               // We can do the action right away.
            handlePendingAction();
            return;
        } else if (session.isOpened()) {
            // We need to get new permissions, then complete the action when we get called back.
            session.requestNewPublishPermissions(new Session.NewPermissionsRequest(this, PERMISSION));
            return;
          }
    }

    if (allowNoSession) {
          pendingAction = action;
        handlePendingAction();
     }
}
    
@SuppressWarnings("incomplete-switch")
private void handlePendingAction() {
    PendingAction previouslyPendingAction = pendingAction;
    // These actions may re-set pendingAction if they are still pending, but we assume they
    // will succeed.
    pendingAction = PendingAction.NONE;

    switch (previouslyPendingAction) {
          case POST_PHOTO:
               //postPhoto();
            break;
        case POST_STATUS_UPDATE:
            postStatusUpdate();
            break;
    }
}
    
private FacebookDialog.ShareDialogBuilder createShareDialogBuilder() {
    return new FacebookDialog.ShareDialogBuilder(this)
          .setName("fb app name")
        .setDescription("app description")
        .setLink("site link");
}
    
private void postStatusUpdate() {
     if (canPresentShareDialog) {
        FacebookDialog shareDialog = createShareDialogBuilder().build();
        uiHelper.trackPendingDialogCall(shareDialog.present());
    } else if (hasPublishPermission()) {
        Bundle params = new Bundle();
        params.putString("name", " link主標題 ");
        params.putString("caption", " link副標題 ");
        params.putString("message", " 描述 ");
        params.putString("link", " 分享連結 ");
        params.putString("picture", " 圖片url ");

        Request request = new Request(Session.getActiveSession(), "me/feed", params, HttpMethod.POST);
        request.setCallback(new Request.Callback() {
               @Override
             public void onCompleted(Response response) {
                    showPublishResult(null, response.getGraphObject(), response.getError());
             }
        });
        request.executeAsync();
     } else {
          pendingAction = PendingAction.POST_STATUS_UPDATE;
    }
}
    
private void showPublishResult(String message, GraphObject result, FacebookRequestError error) {
     String title = null;
    String alertMessage = null;
    if (error == null) {
          title = getString(R.string.success);
        alertMessage = getString(R.string.successfully_posted_post);
    } else {
        title = getString(R.string.error);
        alertMessage = error.getErrorMessage();
    }

    new AlertDialog.Builder(this)
          .setTitle(title)
        .setMessage(alertMessage)
        .setPositiveButton(R.string.ok, null)
        .show();
}

@Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {  
    // fb 登入結果
    Session.getActiveSession().onActivityResult(this, requestCode, responseCode, intent);
}


正式版本
1. Facebook APP設定頁面一定要把SandBox mode關閉

2. 確認Android app package name
> Export Signed Application Package

> KeyStore path別忘記副檔名.keystore !

> 輸入key的資訊 (一個keystore可以有很多把keys), Alias就是key的名稱

3. 複製正式版本的hashed key (建議在keystore的目錄下)
keytool -exportcert -alias (key的名稱) -keystore (keystore名稱).keystore | openssl sha1 -binary | openssl base64

這樣submit的正式版app就能夠正常分享資訊到Facebook啦!

Sent from Evernote

2013年12月4日 星期三

在iOS app用webview播放YouTube影片

前言
如果要直接launch YouTube app來播放, 那需要URL scheme:
  • youtube://
  • http://www.youtube.com/v/VIDEO_IDENTIFIER
  • http://www.youtube.com/watch?v=VIDEO_IDENTIFIER
今天我需要用嵌入webview 方式播放YouTube影片來提供比較好的使用者體驗:D



實作
原理就是用webview 的loadHTMLString 方法來讀取我們assign好的HTML內容.
廢話不說, 直接上code!
webplayer = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    webplayer.scalesPageToFit = YES;
    webplayer.delegate = self;
    [self.view addSubview:webplayer];
    NSString *videoURL = [NSString stringWithFormat:@"http://www.youtube.com/embed/%@", youtubeID];
    NSString *videoHTML = [NSString stringWithFormat:@"\
                 \
                 \
                 \
                 \
                 \
                 \
                 \
                 ", videoURL];
    [webplayer loadHTMLString:videoHTML baseURL:nil];
    webplayer.backgroundColor = [UIColor blackColor];
    webplayer.opaque = NO;



重點一
如果是使用xib來製作webplayer 那可以直接把delegate給定

不然就是在.m
webplayer.delegate = self;
以及在.h 加上
@interface webPlayerViewController : UIViewController <UIWebViewDelegate> 


重點二
videoURL 是 http://www.youtube.com/embed/(youtubeID)
不是一般網址
https://www.youtube.com/watch?v=b1aHBlaC0de
也不是分享用的縮址
http://youtu.be/b1aHBlaC0de
舊版的嵌入網址也不適用
www.youtube.com/v/b1aHBlaC0de?version=3&amp;hl=zh_TW&amp;rel=0


最後重點
HTML 樣式的調整, 可以觀察一下NSString中一些跳脫字元的使用方式.
另外, 我們可以加上一個按鈕來關閉這頁.
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" 
         style:UIBarButtonItemStylePlain 
         target:self 
         action:@selector(onClickedDone:)];
self.navigationItem.rightBarButtonItem = doneButton;
[doneButton release];

- (void)onClickedDone:(id)sender {
    [webplayer release];
    webplayer = nil;
    //popViewController or dismissViewController
}


參考連結: MightyMeta
Sent from Evernote     

2013年11月21日 星期四

iOS7 UITableViewCell imageView offset issue

前言
iOS7的新界面風格確實給UX設計師跟工程師帶來不少困擾,
尤其是UITableView的layout變動讓我花了不少時間去調整。

問題
我在cell.imageView裡頭放的圖片位置跑掉了。

嘗試
// Not work 1.
[cell.imageViewsetFrame:CGRectOffset(cell.imageView.frame,-15,0)];

// Not work 2.
[tableViewsetContentOffset:CGPointMake(-15,0)];

// Not work 3.
if([selfrespondsToSelector:@selector(edgesForExtendedLayout)])
 self.edgesForExtendedLayout=UIRectEdgeNone;
 
// Not work 4.
if([mainTablerespondsToSelector:@selector(setSeparatorInset:)])
 [tableViewsetSeparatorInset:UIEdgeInsetsZero];
 
// Not work 5.
if([mainTablerespondsToSelector:@selector(setContentInset:)])
 [tableViewsetContentInset:UIEdgeInsetsZero];

實作
解決方法要點就是要實作custom cell.
第二點就是在custom cell的.m裡面 overwrite layoutSubviews 這個function.
因為只有iOS7會跑版, 所以多加個判斷式檢查.
#define IOS_SEVEN ([[UIDevice currentDevice].systemVersion floatValue] >= 7)

- (void)layoutSubviews{
 [superlayoutSubviews];
 if(IOS_SEVEN){
  self.imageView.frame=CGRectOffset(self.imageView.frame,-15,0);
 }
}

Sent from Evernote

2013年10月3日 星期四

[Android] 後悔了, 我想修改ActionBar的樣式

From Evernote:

[Android] 後悔了, 我想修改ActionBar的樣式

前言

一開始乖乖跟著別人的教學做出來的App大概像這樣,
不知道是那裡怪怪的, 總覺得有點生硬:
瞧瞧人家的就是比較fashion XD
對吼!就是看起來憨憨的ActionBar啦!

一開始專案建立沒選好,要怎麼辦呢?
還是可以反悔的,沒別招,手動改囉~



動手

事情是這樣發生的:
在AndroidMainifest.xml裡頭我節錄的最後一行,
說明了app theme是參考styles.xml裡面AppTheme的設定。

有頭緒了!而styles.xml藏在哪?
在專案/res/values,/res/values-v11,跟/res/values-v14底下都有stlyes.xml需要修改

然後重點來了AppBaseTheme 的parent值改成 android:Theme.XXX.NoTitleBar

至於有哪些Theme可以設定呢?
懶惰的方式除了參考xml的graphical layout設定後的效果


也可以看看官方網站是怎麼寫的~ Styles and Themes

參考資料:greenhandtobe开源中国

內容回應