lunes, 18 de diciembre de 2017

iOS: Show message / show alert

private func showAlert(withMessage message: String) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
        alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default) { alertAction in
           
            alertController.dismiss(animated: true, completion: nil)
        })
       
        self.present(alertController, animated: true, completion: nil)
    }

miércoles, 13 de diciembre de 2017

iOS: Tethering an Android device to an iPhone

1) On an Android powered phone, enter the Tethering and Hotspot Menu.
2) Select the option to enable Bluetooth Tethering.
3) Enable Bluetooth on the phone.
4) In the Bluetooth menu, make the phone discoverable by tapping the top message.
5) On the iPad, turn the Bluetooth on in Settings.
6) When the phone appears on the list of devices, Tap to connect.
7) Once connected, there will be a tethering icon in the top left of the screen.
8) The iPad now has internet access through the phones mobile data connection.

jueves, 30 de noviembre de 2017

iOS: Facebook login

1) Create fb application and install sdk steps

pod 'FBSDKCoreKit'
pod 'FBSDKShareKit'
pod 'FBSDKLoginKit'

2) In your app delegate:

Objective C


import FBSDKCoreKit

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
        
        return true
    }

...

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        
        let handled = FBSDKApplicationDelegate.sharedInstance().application(app, open: url, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String, annotation: options[UIApplicationOpenURLOptionsKey.annotation])
        
        return handled
    }

Swift

import FBSDKCoreKit

...

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
       
        FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
       
        return true
    }

...

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        
        let handled = FBSDKApplicationDelegate.sharedInstance().application(app, open: url, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as! String, annotation: options[UIApplicationOpenURLOptionsKey.annotation])
        
        return handled
    }

3) Add login button

Add default facebook button

Swift


let loginButton = FBSDKLoginButton()
loginButton.readPermissions = ["public_profile", "email"]
loginButton.delegate = self
loginButton.center = view.center

...


extension ViewController: FBSDKLoginButtonDelegate {
    
    func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
        if let token = result?.token?.tokenString {
            NSLog("Token: %@", token)
        } else {
            NSLog("Error: %@", error.localizedDescription)
        }
    }
    
    func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
        NSLog("Logged out from facebook")
    }
}

Add custom button

Swift 

FBSDKLoginManager().logIn(withReadPermissions: ["email","public_profile"], from: self) { (result, error) in

            if let let tokenString = result?.token?.tokenString {

            } else {
                // error
            }
        }

4) In you .plist add

<key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>fb{facebook_app_id}</string>
            </array>
        </dict>
    </array>
    <key>FacebookAppID</key>
    <string>{facebook_app_id}</string>
    <key>FacebookDisplayName</key>
    <string>ROMWOD</string>
    <key>LSApplicationQueriesSchemes</key>
    <array>
        <string>fbapi</string>
        <string>fb-messenger-api</string>
        <string>fbauth2</string>
        <string>fbshareextension</string>
    </array>

lunes, 13 de noviembre de 2017

iOS: TextView with placeholder text


class TextViewWithPlaceholder: UITextView {
    
    private var _placeholderTextColor: UIColor = UIColor.lightGray
    @IBInspectable var placeholderTextColor: UIColor {
        set {
            _placeholderTextColor = newValue
            textColor = newValue
        }
        get {
            return _placeholderTextColor
        }
    }
    @IBInspectable var placeholderText: String? {
        didSet {
            if text.isEmpty {
                text = placeholderText
            }
        }
    }
    
    private var initialTextColor: UIColor?
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        
        commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        commonInit()
    }
    
    private func commonInit() {
        
        initialTextColor = textColor
        
        text = placeholderText
        
        NotificationCenter.default.addObserver(self, selector: #selector(didBeginEditing(_:)), name: UITextView.textDidBeginEditingNotification, object: self)
        NotificationCenter.default.addObserver(self, selector: #selector(didEndEditing(_:)), name: UITextView.textDidEndEditingNotification, object: self)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc func didBeginEditing(_ notification: NSNotification) {
        
        if let textView = notification.object as? TextViewWithPlaceholder, textView == self, text == placeholderText {
            
            text = ""
            textColor = initialTextColor
        }
    }
    
    @objc func didEndEditing(_ notification: NSNotification) {
        
        if let textView = notification.object as? TextViewWithPlaceholder, textView == self, text.isEmpty {
            
            text = placeholderText
            textColor = placeholderTextColor
        }
    }
}

miércoles, 18 de octubre de 2017

ionic: Lists

ionic: Buttons

<button
 // (click)="onClick()"
// [navPush]="yourPage"
  ion-button
// color="primary"
// outline | clear | round
// block (from inline element to block element)
// small | medium | large
// icon-only
// icon-left | icon-right
>
Button name
// <ion-icon name="icon-name"></ion-icon>
</button>

jueves, 28 de septiembre de 2017

ionic: Color

src/theme/variables.scss

In here define your colors inside $colors map:

$colors: (
  primary:    #488aff,
  secondary:  #32db64,
  danger:     #f53d3d,
  light:      #f4f4f4,
  dark:       #222,
  ...
  myColor: #ff0000,
);

custom_scss.scss

.my-class {
...
background-color: color($colors, myColor);
}

ionic: Save index from *ngFor

*ngFor="let item of items; let i = index"

martes, 26 de septiembre de 2017

ionic: Menu

app.html

<ion-menu [content]="id_name">

<ion-header>
<ion-toolbar>
<ion-title>menu_name</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<button ion-item (click)="loadPage(page1)">
<ion-icon name="menu_item_1" item-left></ion-icon>
item_1
</button>
<button ion-item (click)="loadPage(page2)">
<ion-icon name="menu_item_2" item-left></ion-icon>
item_2
</button>
</ion-list>
</ion-content>
</ion-menu>

<ion-nav [root]="rootPage" #id_name></ion-nav>

app.component.ts

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage = RootPage;
  page1 = Page1;
  page2 = Page2;
  @ViewChild('nav') nav: NavController; // Because the menu is inside the ion-nav

  ....

  onLoad(page: any) {
    this.nav.setRoot(page);
    this.menuController.close();
  }
}

your_page.html

<ion-header>
  <ion-navbar>
  <ion-buttons start>
  <button ion-button menuToggle>
  <ion-icon name="menu"></ion-icon>
  </button>
  </ion-buttons>
    <ion-title>your_page</ion-title>
  </ion-navbar>
</ion-header>

...


ionic: Theming

In theme folder, in variables.scss:

1) Redefine a one:

$content-padding: 8px;

2) Add one, inside a map, for instance, colors or outside of it.

Inside:

$colors: ( primary: #488aff, secondary: #32db64, danger: #f53d3d, light: #f4f4f4, dark: #222, myColor: #f2f2f2 );

Outside:

$myColor: #f2f2f2

ionic: View hooks

willEnter Observable, fired when Component is about to become active
didEnter Observable, fired when Component has become active
willLeave Observable, fired when Component is about to become inactive
didLeave Observable, fired when Component has become inactive
willUnload Observable, fired when Component has been destroyed
onWillDismiss Observable, fired when Component will be dismissed
onDidDismiss Observable, fired when Component was dismissed

lunes, 25 de septiembre de 2017

ionic: Import data

your_data.ts

export default / data_name [
   // Json object
]

to use it, import it:

import any_name (if default) / data_name from 'your_data_relative_location';

...

ngOnInit() {
  this.quoteCollection = quotes;

  }

viernes, 22 de septiembre de 2017

ionic: Interface

export interface interface_name {
// definition
}

Note: Can by used to import data.

To use it import it in your file:

import { interface_name } from 'interface_relative_location';

miércoles, 30 de agosto de 2017

martes, 29 de agosto de 2017

ionic: Navigate from one page to another passing parameters

One page

onepage.ts

import { Component } from '@angular/core';

import { NavController } from 'ionic-angular';

import { UserPage } from './otherpage';

@Component({
  selector: 'page-one',
  templateUrl: 'one.html'
})
export class OnePage {

constructor (private navController: NavController) {

}

onLoad(someParameter: string) {
this.navController.push(OtherPage, {parameter1: someParameter});
}
}

onepage.html

<ion-header>

  <ion-navbar>
    <ion-title>One Page</ion-title>
  </ion-navbar>

</ion-header>

<ion-content padding>
<button ion-button (click)="onLoad('Value1')"> Value 1 </button>
<hr>
<button ion-button (click)="onLoad('Value2')"> Value 2 </button>
</ion-content>

Other page

otherpage.ts

import { Component } from '@angular/core';

import { NavParams } from 'ionic-angular';

@Component({
selector: 'page-other',
templateUrl: 'other.html'
})
export class OtherPage {
parameter: string;

constructor (private navParams: NavParams) {

}

ngOnInit() {
this.name = this.navParams.get('parameter1');
}
}

otherpage.html

<ion-header>
<ion-navbar>
<ion-title>{{parameter}}</ion-title>
</ion-navbar>
</ion-header>

<ion-content padding>
<p>{{parameter}}</p>
</ion-content>

Add onepage and other page to app.module.ts.

ionic: Add a page

Automatic way

1) You can do it “automatically”
ionic generate page users
2) Modify app.module.ts and add your page to declarations and entryComponents don’t forget to add the import.
3) Link from another existing page, don’t forget to include the import here as well.

Manual way

0) Create a folder (if needed)
1) Create a file <custom_page>.ts

import { Component } from '@angular/core';

@Component({

   selector: 'page-custom',
   templateUrl: 'custom.html';
})
export class CustomPage {


}
2) Create a <custom>.html

<ion-header>
   <ion-navbar>
      <ion-title>My title</ion-title>
   </ion-navbar>
</ion-header>

<ion-content>
</ion-content>

2) Modify app.module.ts and add your page to declarations and entryComponents don’t forget to add the import.
4) Link from another existing page, don’t forget to include the import here as well.

jueves, 24 de agosto de 2017

ionic: Start

0) Download node
1) npm install ionic cordova -g (-g install globally)
3) Create project
      ionic start <project_name> (blank)

blank if you want no template

4) cordova platform add ios
5) cordova platform add android
6) cordova build
7) Open project on browser, keeps track of changes and rebuilds / re starts the app
      ionic serve
8) Add platform
codova platform add ios (or android)

miércoles, 12 de julio de 2017

iOS: Unit testing

1) Make sure your podfile looks like this:

platform :ios, '10.0'
use_frameworks!
source 'https://github.com/CocoaPods/Specs.git'

def common_pods
    // your pods here
end

target 'MyApp' do
    common_pods
end

target 'MyApp Tests' do
    common_pods

end

2) Your test files should include:

import XCTest
@testable import MyApp

3) Configuration:





viernes, 7 de julio de 2017

iOS: Google maps markers

Google maps marker view's icon view is placed as shown on the image


That is, the icon view's bottom center is placed in the (lat, lng) provided. All the drawing has to be done inside the icon view, the marker will ignore the value of "clipToBounds" and will always take it as if it were true.

You can change the "groundAnchor":

The ground anchor specifies the point in the icon image that is anchored to the marker's position on the Earth's surface.
This point is specified within the continuous space [0.0, 1.0] x [0.0, 1.0], where (0,0) is the top-left corner of the image, and (1,1) is the bottom-right corner.
If the image has non-zero alignmentRectInsets, the top-left and bottom-right mentioned above refer to the inset section of the image.

jueves, 8 de junio de 2017

iOS: Select picture from camera or library

MyViewController

class MyViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    ...
 
    @IBOutlet weak var pictureImage: UIImageView!
 
    let picker = UIImagePickerController()
 
    ...
 
    override func viewDidLoad() {
        super.viewDidLoad()
     
        picker.delegate = self
     
        ...
    }

    ...

    // MARK: - Actions     @IBAction func changePictureAction(_ sender: Any) {
        ...
        changePhoto()
    }

    ...

    func changePhoto() {
     
        let optionMenu = UIAlertController(title: nil, message: "Lets get a picture", preferredStyle: .actionSheet)
     
        let selectFromLibraryAction = UIAlertAction(title: "Select photo from library", style: .default, handler: {
            (alert: UIAlertAction!) -> Void in
            self.selectPictureFromLibrary()
        })
        optionMenu.addAction(selectFromLibraryAction)
     
        if (UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)) {
            let takePictureAction = UIAlertAction(title: "Take a picture", style: .default, handler: {
                (alert: UIAlertAction!) -> Void in
                self.selectPictureFromCamera()
            })
            optionMenu.addAction(takePictureAction)
        }
     
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {
            (alert: UIAlertAction!) -> Void in
            print("Cancelled")
        })
        optionMenu.addAction(cancelAction)
     
        self.present(optionMenu, animated: true, completion: nil)
    }
 
    func selectPictureFromLibrary() {
        picker.sourceType = .photoLibrary
        present(picker, animated: true, completion: nil)
    }
 
    func selectPictureFromCamera() {
        picker.sourceType = .camera
        present(picker, animated: true, completion: nil)
    }
 
...

// MARK: - UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
    {
        let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
        pictureImage.contentMode = .scaleAspectFit
        pictureImage.image = chosenImage
        dismiss(animated:true, completion: nil)
    }
 
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }    
}

Info.plist

Key: Privacy - Photo Library Usage Description
Type: String
Value: <you message>

Key: Privacy - Camera Usage Description
Type: String
Value: <you message>

iOS: WKWebKit not persisting cookies (login in not persisting when navigation out and into the web view)

I was having an issue that when loging in to a page through a WKWebKit the session didn't stick. Everytime I arrived at the page I was prompted to login.

We have to do the following.

We need to create a unique process pool to be shared among all WKWebKits and pass it along as a parameter to all of them (this wasn't necessary on UIWebView the way Apple handles the coookies changed):

var processPool: WKProcessPool?

somewhere where we create the WebViewController we pass said pool:

let webViewController = WebViewController()
webViewController.url = "some-url"
webViewController.processPool = processPool

then the web view controller that contains the WKWebKit we configure it like so:

class WebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
 
    var url: URL?
    var webView: WKWebView!
    var processPool: WKProcessPool?
 
    override func loadView() {
     
        let webConfiguration = WKWebViewConfiguration()
     
        if let pool = processPool {
            webConfiguration.processPool = pool
        }
     
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
             
        view = webView
    }
...

}

miércoles, 26 de abril de 2017

jueves, 6 de abril de 2017

iOS: Search view

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdating {

@IBOutlet var tableView: UITableView! // Add in interface builder

var items = [Item]()
    var filteredItems = [Item]()
    var searchController: UISearchController!

override func viewDidLoad() {
        super.viewDidLoad()
       
        ...
       
        self.searchController = ({
            let controller = UISearchController(searchResultsController: nil)
            controller.searchResultsUpdater = self
            controller.dimsBackgroundDuringPresentation = false
            controller.hidesNavigationBarDuringPresentation = false
            controller.searchBar.sizeToFit()
            //            controller.searchBar.barStyle = UIBarStyle.black
            controller.searchBar.barTintColor = settings.get(settingNamed: SettingCollection.General.PrimaryButtonBackgroundColor)?.colorValue
            controller.searchBar.backgroundColor = settings.get(settingNamed: SettingCollection.General.PrimaryButtonBackgroundColor)?.colorValue
            self.tableView.tableHeaderView = controller.searchBar
            return controller
        })()
    }
   
    ...
   
    // MARK: - UITableViewDelegate & UITableViewDataSource
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
   
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filteredItems.count
    }
   
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       
        let item = filteredItems[indexPath.row]
       
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyCell
        // Populate cell
       
        return cell
    }
   
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
       
    }
   
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
   
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if cell.responds(to: #selector(setter: UITableViewCell.separatorInset)) {
            cell.separatorInset = UIEdgeInsets.zero
        }
        if cell.responds(to: #selector(setter: UIView.preservesSuperviewLayoutMargins)) {
            cell.preservesSuperviewLayoutMargins = false
        }
        if cell.responds(to: #selector(setter: UIView.layoutMargins)) {
            cell.layoutMargins = UIEdgeInsets.zero
        }
    }
   
    // MARK: - UISearchResultsUpdating
    func updateSearchResults(for searchController: UISearchController) {
        filterContentForSearchText(searchText: searchController.searchBar.text!)
    }
   
    func filterContentForSearchText(searchText: String, scope: String = "All") {
       
        if searchText.isEmpty {
            filteredItems = items
            tableView.reloadData()
           
            return
        }
       
        filteredItems = items.filter { item in
            return item.text.lowercased().contains(searchText.lowercased())
        }
       
        tableView.reloadData()
    }
}