# Xamarin.ios 學習筆記 ###### tags: `xamarin` , `intern` :::success >**Homework** >build a log in / log out Android page > [time=Mon, Jul 9, 2018] > [API in swagger](https://app.swaggerhub.com/apis/dentall7/sample-api/0.0.1#/) > [color=#28b51e] ::: ##### Xamarin * [Xamarin developer api](https://developer.xamarin.com/api/) * [youtube tutorial](https://www.youtube.com/channel/UCCYR9GpcE3skVnyMU8Wx1kQ) ##### mac * [mac 快捷鍵們](https://support.apple.com/zh-tw/ht201236) * [build ios with VS for mac](https://docs.microsoft.com/en-us/xamarin/ios/get-started/hello-ios/) ### POST [referrence](https://stackoverflow.com/questions/36458551/send-http-post-request-in-xamarin-forms-c-sharp) ```csharp= public async Task PostFuncAsync(string json) { var client = new HttpClient(); client.BaseAddress = new Uri("http://sample.dentall.io"); var content = new StringContent(json, Encoding.UTF8, "application/json"); HttpResponseMessage response = null; response = await client.PostAsync("/api/authenticate", content); // this result string should be something like: "{"token":"rgh2ghgdsfds"}" var result = await response.Content.ReadAsStringAsync(); //response.StatusCode is `Enum` Debug.WriteLine($"code: {(int)response.StatusCode}"); //404,200,401... } ``` ### GET ```csharp= async public void GetUserInfo(string token) { //use token to request user info var client = new HttpClient(); string url = "http://sample.dentall.io/api/photos"; //add token get from `/api/authenticate/` client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var response = await client.GetAsync(url); int status = (int)response.StatusCode; if(status == 200){ var result = await response.Content.ReadAsStringAsync(); Debug.WriteLine(result); } } ``` ### JSON #### parse class into json form: ```csharp= //`newUser` is a class using Newtonsoft.Json; string json = JsonConvert.SerializeObject(newUser); ``` #### parse json into object form: ##### 方法1 ```csharp= // define the inout type corresponding to the json form var definition = new { id_token = "" }; //`result` is the response.content var id1 = JsonConvert.DeserializeAnonymousType(result, definition); Debug.WriteLine(Convert.ToString(id1.id_token)); ``` ##### 方法2 ```csharp public class Img{ public int id { get; set; } public string url { get; set; } } Img[] id1 = JsonConvert.DeserializeObject<Img[]>(result); ``` ### User Preference [referrence](https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/user-defaults#writing-default-values) 要存取一些使用者的 Global Variable (ex.`token`)時,iOS有特定的`NSUserDefaults`套件可以使用 ```csharp //自訂屬性`userToken` 並設為字串 token var t = NSUserDefaults.StandardUserDefaults; t.SetString(token, "userToken"); ``` 這樣整個專案的任何檔案就可以拿到同一份使用者資料了 ```csharp var plist = NSUserDefaults.StandardUserDefaults; var useHeader = plist.StringForKey("userToken"); //token ``` ### Page Navigation #### 1. Navigation Controller [youtube tutorial](https://www.youtube.com/watch?v=b-cwEQdGkS0) 可以像影片教的那樣直接拉UI,可是按鈕事件一定要手動補上去! ```csharp partial void CreateAccount_TouchUpInside(UIButton sender) { //裡面什麼都不寫也沒關係 } ``` #### 2. manually create ui [referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/app-fundamentals/ios-code-only?tabs=vsmac) ###### step 1 在 `Main.storyBoard`裡選相對應的 view,設定它在 `ViewController`中的`StoryBoard ID`屬性(這邊我取`main`) ###### step 2 在 `AppDelegate.cs` 裡 ```csharp= public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { var storyboard = UIStoryboard.FromName("Main", null); var mainController = storyboard.InstantiateViewController("main") as ViewController; var navController = new UINavigationController(mainController); // set `main` view to the root view Window.RootViewController = navController; return true; } ``` ###### step 3 在 `ViewController.cs` 裡 ```csharp= partial void CreateAccount_TouchUpInside(UIButton sender) { //這樣是產生一個新的粉紅畫面指過去 var user = new UIViewController(); user.View.BackgroundColor = UIColor.Magenta; this.NavigationController.PushViewController(user, true); //這樣是指到名為`logIn`的 view var storyboard = UIStoryboard.FromName("Main", null); var mainController = storyboard.InstantiateViewController("LogIn") as LogInPageViewController; this.NavigationController.PushViewController(mainController, true); } ``` #### 用法們~ ###### 1. ShowViewController(viewController, sender) 是為 `viewController` 開一個新頁 ###### 2. PushViewController(viewController, true) 功能同上,但據說是舊式寫法(?),會搭配`NavigationController`使用 ```csharp this.NavigationController.PushViewController(mainController, true); ``` ###### 3. PresentViewController(alert, true, null) 把 alert 以跳出視窗 `modal` 的形式呈現 ###### 4. PopViewController(true) 把 `Navigation view stack` 上最上層的 view pop 掉 ### Modal [youtube tutorial](https://www.youtube.com/watch?v=DmWv-JtQH4Q) 感覺要用ui拉,因為現在沒有 `PushModalAsync` 的 function * present modaly * add `view` (which will be your modal) * add `blur effect` on the background * model view 的 `background` 設成 `clear color` (背景變黑表示不顯示 :+1: ) * `segue` 上一定要設 `presentation : over current context` * ### Update UI after async [referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/user-interface/ios-ui/ui-thread) ```csharp= async partial void LogInBtn_TouchUpInside(UIButton sender) { UiIndicator.StartAnimating(); var logInSuccess = await PostFuncAsync(json); /* 下面的程式會等到 await 後面的 function 執行完畢才接著做 */ UiIndicator.StopAnimating(); } ``` ### Dynamic Arrangement >[youtube tutorial](https://www.youtube.com/watch?v=pDlCx0C3v74) > 要有強大的耐心啦啦啦[color=#ffc802] ### Load Image URL and HTTP Exception `storyboard` 那邊拉好一個`ImageView`之後(取名為`userImg`),想用 `url` 當 `Image Source` 的方式: ```csharp public static UIImage UIImageFromUrl(string uri) { using (var url = new NSUrl(uri)) using (var data = NSData.FromUrl(url)) return UIImage.LoadFromData(data); } //用法 userImg.Image = UIImageFromUrl("https://.../picture.jpg"); ``` > 注意!! > 如果網址是`http`就會跳 Error 説 data is null > 但真正原因是ios會把不安全連線擋下來以保護使用者 > [color=red] 這時 debug view 那邊會顯示: `Transport security has blocked a cleartext HTTP ` 如果信任對方http是沒有危險也確定想給予特權的話 可以在 `Info.plist` 加上這一段: ```htmlmixed= <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <false/> <key>NSExceptionDomains</key> <dict> <key>sample.dentall.io</key> <!--Include your domain at this line --> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSTemporaryExceptionMinimumTLSVersion</key> <string>TLSv1.1</string> </dict> </dict> </dict> ``` 參考>> [stackOverflow](https://stackoverflow.com/questions/31254725/transport-security-has-blocked-a-cleartext-http/32560433#32560433) > 如果 visual studio 不知道怎麼改 > 可以帥帥的去 terminal 那邊 `vim Info.plist` XD > [name=遠志][time=Sat, Jul 14, 2018 12:15 PM] ### PopUP Alert [youtube tutorial](https://www.youtube.com/watch?v=y3I9FoOFfyc) : should be modified when displaying alert [referrence](https://docs.microsoft.com/zh-tw/xamarin/ios/user-interface/controls/alerts) :100: ```csharp= //create alert var alert = UIAlertController.Create("Success", "Go and check your email to activate your account!", UIAlertControllerStyle.Alert); //create an option "OK" that changes view when clicked alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, (UIAlertAction obj) => { this.NavigationController.PopViewController(true); })); //another way: do nothing when the option is clicked alert.AddAction(UIAlertAction.Create ("OK", UIAlertActionStyle.Default, null)); //display the alert as a popover view on top of the current view PresentViewController(alert, true, null); ``` ### Defensive Programming ```csharp= try { //程式主執行區或可能發生錯誤的地方 } catch (Exception ex) { //例外的處理方法,如秀出警告 } finally { //不論是否發生例外事件都會執行的區塊 } ``` ### 滿滿的錯誤大平台 XD :::danger **[Error]** Unreachable code after throw ::: * The exception `System.NotImplementedException ();` is thrown when a requested method or operation is not implemented. * If there is any inplementation after it, those operations will never be executed. > [solution](https://stackoverflow.com/questions/14890246/unreachable-code-after-throw-new-system-notimplementedexception) :::danger **[Error]** This Class is not Key Value Coding-Compliant for the Key <keyname> ::: **發生情形** 在做 PageNavigation 時,同時給一個 view 特定的 `Name` 和 `StoryBoard ID` (in its `View Controller`) > [solution](http://www.tech-recipes.com/rx/52021/how-do-i-fix-the-issue-this-class-is-not-key-value-coding-compliant-for-the-key-in-xcode-6/) > 看一下 `<keyname>`指的是什麼 > 我這次刪掉 `Name` 就行了,應該是因為重複命名的關係 > [name=遠志][time=Fri, Jul 13, 2018 10:05 AM]