четверг, 15 октября 2015 г.

Font names in iOS

Might be interesting for you as Quick Win within the Debugger:
(lldb) po [UIFont fontNamesForFamilyName:@"Helvetica Neue"]

(id) $1 = 0x079d8670 <__NSCFArray 0x79d8670>(
HelveticaNeue-Bold,
HelveticaNeue-CondensedBlack,
HelveticaNeue-Medium,
HelveticaNeue,
HelveticaNeue-Light,
HelveticaNeue-CondensedBold,
HelveticaNeue-LightItalic,
HelveticaNeue-UltraLightItalic,
HelveticaNeue-UltraLight,
HelveticaNeue-BoldItalic,
HelveticaNeue-Italic
)
source: http://stackoverflow.com/questions/11083954/uifont-fontwithname-font-name

вторник, 13 октября 2015 г.

Pull to refresh swift


var refresher: UIRefreshControl!

    override func viewDidLoad() {
        super.viewDidLoad()
//...
        initRefresher()
    }
    
    // MARK: Helper
    func initRefresher() {
        refresher = UIRefreshControl()
        refresher.attributedTitle = NSAttributedString(string: "Потяните вниз для обновления")
        refresher.addTarget(self, action: "refreshData", forControlEvents: UIControlEvents.ValueChanged)
        tableView.addSubview(refresher)
        

    }


    
    func refreshData() {
//... обновляемся
    refresher.endRefreshing() //выключаем обновление
    }

Хорошая статья: pull to refresh

Core Data NSPredicate checking for BOOL value

Based on Apple Document Here, we can use the following two methods to compare Boolean:
NSPredicate *newPredicate = [NSPredicate predicateWithFormat:@"anAttribute == %@",[NSNumber numberWithBool:aBool]];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:@"anAttribute == YES"];
However, the above predicate cannot get out the ones with empty anAttribute. To deal with an empty attribute, you need the following method according to Apple document here:
predicate = [NSPredicate predicateWithFormat:@"firstName = nil"]; // it's in the document
or
predicate = [NSPredicate predicateWithFormat:@"firstName == nil"]; // == and = are interchangeable here

Corner Radius

import QuartzCore
...
view.layer.cornerRadius = 5;
view.layer.masksToBounds = YES;

пятница, 25 сентября 2015 г.

вторник, 22 сентября 2015 г.

Решение проблемы с HTTP в iOS 9

Если при попытке доступа к ресурсам в сети Интернет вы получаете получаете ошибку вида:
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. "
Значит, Вы один из тех страдальцев, кто по каким-то причинам не знает, что Apple озаботилась нашей с вами безопасностью)))).
Теперь по умолчанию запрещено использовать HTTP протокол в качестве транспорта. Разрешено HTTPS. Для тех кто не знает что такое HTTP/HTTPS вики/гугл/яндекс и т.п. в помощь.
Маленькое отступление.
В теории это наверное хорошо. Наверное, потому что, не все алгоритмы шифрования используемые в слое представлений транспорта HTTPS коллизионно устойчивы.  Так алгоритм md5 несколько года назад считался абсолютно надежным и теоретически коллизионно устойчивым, теперь же им не стоит шифровать даже список покупок в магазине. А еще вылезают такие вещи как Heartrbleed, или скандал с Ubuntu, под которой несколько лет генерились идентичные сертификаты на разных серверах и т.д. и т.п. В общем идею вы поняли... Но у этой  проблемы есть не только абстрактная но и практическая часть. Заключается она вот в чем: сертификаты для https стоят денег. Чаще всего от 100 до 300 долларов в год. Сразу удорожает хостинг, т.к. при использовании HTTPS вы шифруете заголовки и и веб сервер не знает к какому dns имени вы идете. В результате на 1 ip адрес приходится 1 https сайт. (Да я знаю, что Microsoft научила IIS заглядывать в заголовки HTTPS сообщений, и даже объясняет что это безопасно))). Но задайте себе вопрос сколько веб серверов работает на MS Windows Server/IIS и чтобы "2 раза не вставать" подумайте почему.)
И не то что бы сложно в настройке но требует от инженера дополнительных телодвижений и отвлекает его от WoW/WoT/Dota/(подставьте сами). Создает дополнительную головную боль при высоких нагрузках и балансировке. (Дадада. Знаю Cisco. А цену вы знаете?) Адреса IPv4 давно закончились. На IPv6 мир перейдет лет через 10. Может раньше. Но не в 2016 году.
В общем и в частностях HTTPS это безопасно(пока), но дорого и для мелких ресурсов и для крупных.
ИМХО перфекционизм Apple лучше направить на контроль качества своей продукции и ПО. Т.к. при покупке нового iphone 6 plus в аппсторе я получил кирпич и 2 недели ждал пока его заменят по гарантии. При покупке mb pro 13 в аппсторе я получил не работающее зарядное устройство. Но т.к. Apple нас..ть на мое мнение, то приступим к  решению проблемы:
Добавляем в Info.plist :
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>YOURSITE.COM</key>
<dict>
<key>NSIncludeSubdomains</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>

</dict>

Естественно YOURSITE.COM нужно заменить на url искомого сайта.

четверг, 17 сентября 2015 г.

Как конвертировать Адрес в Координаты в iOS


let address += "ул. Ленина, 4,Омск, Россия"
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) -> Void in
    if let err = error { println(err.localizedDescription) }
    if DEBUG { println(address) }
    if let placemark = placemarks?[0] as? CLPlacemark {
       let latitude: CLLocationDegrees = placemark.location.coordinate.latitude
       let longitude: CLLocationDegrees = placemark.location.coordinate.longitude
    }
})
Решение из : stackoverflow.com

вторник, 11 августа 2015 г.

Trailing closure syntax in Swift

В Swift поддерживается удобная фича, видимо пришедшая из Perl : trailing closure syntax .
Допустим нам нужно отсортировать массив строк в лексикографическом порядке. Придумываем имена для двух переменных и сортируем:
searchResults.sort({ (result1: String, result2String) -> Bool in
    return result1.localizedStandardCompare(result2) == NSComparisonResult.OrderedAscending
})
Имена придуманных мною переменных я подсветил желтым. А вот более приятный глазу синтаксис:

searchResults.sort {   
    $0.name.localizedStandardCompare($1.name) == NSComparisonResult.OrderedAscending 
}
Вопрос читабельности остается открытом, на вкус и цвет как говорится...

понедельник, 10 августа 2015 г.

Методика отладки в XCode



  • Если приложение завершилось с ошибкой EXC_BAD_INSTRUCTION или SIGABRT,  отладчик Xcode обычно покажет сообщение об ошибке и место где эта ошибка случилась.
  • Если Xcode думает, что проблема находится в AppDelegate (не очень полезно!), включите Exception Breakpoint для получения большего количества информации.
  • Если приложение "падает" сигналом SIGABRT, но без сообщения об ошибке, тогда выключите Exception Breakpoint и дайте приложению "упасть" снова. (Или нажмите кнопку Continue program execution в откачки несколько раз. Это также покажет сообщение об ошибке.)
  • Ошибка EXC_BAD_ACCESS обычно означает, что что-то пошло не так с  memory management. Объект может быть “released” слишком много раз или  “retained” полностью. Со Swift эти проблемы большей частью остались в прошлом, потому что компилятор будет будет следить за этими вещами. Однако, все еще возможно получить эти ошибки если вы используете  Objective-C код или низкоуровневые API.
  • EXC_BREAKPOINT это не ошибка. Приложение останавливается в breakpoint  голубая стрелка указывает на ту линию где приложение приостанавливается. Вы устанавливаете breakpoints для того что бы поставить свое приложение на "паузу" в определенном месте кода, таким образом, что вы можете отслеживать состояние приложения в отладчике. Кнопка “Continue program execution” возобновляет работу приложения.
Это должно помочь решить большую часть проблем.

Проблема выделения ячейки UITableViewCell


Часто встречающаяся проблема при "тапе" на ячейку UITableViewCell , ячейка сохраняет свое выделение и остается выделенной до тех пор пока вы не выделите другую ячейку и т.д.
Для решения этой проблемы добавьте следующие методы в ваш UITableViewDelegate extension:
page29image17064
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    tableView.deselectRowAtIndexPath(indexPath, animated: true
}

func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
    if searchResults.count == 0
        return nil
    } else {
        return indexPath

    } 

Как убрать промежуток над строкой поиска UISearchBar


Часто разработчик не знаетт как убрать уродливый промежуток над строкой поиска. Приложение выглядит намного лучше если строка состояния (status bar) имеет тот же стиль, что и строка поиска (search bar)

Для решения этой проблемы добавьте в ваш класс (являющийся SearchBarDelegate) следующий метод:


func positionForBar(bar: UIBarPositioning) -> UIBarPosition 
    return .TopAttached

Если вы будете искать этот метод в API документации для UISearchBarDelegate protocol, то вы его не найдете. Все потому что этот метод принадлежит протоколу UIBarPositioningDelegate , который расширяется протоколом UISearchBarDelegate (как и классы, протоколы могут наследовать друг другу).

Как в iOs скрыть клавиатуру жестом

В storyboard, выберите Table View. Идите Attributes inspector и установите значение Keyboard в Dismiss interactively




Программное задание границ (edges) UITableView

tableView.contentInset = UIEdgeInsets(top: 64, left: 0, bottom: 0, right: 0)

суббота, 8 августа 2015 г.

Установка фокуса на элемент при открытии View

Для установки фокуса на элементе необходимо вызвать метод becomeFirstResponder()  этого элемента. 
Пример:
textField.becomeFirstResponder()

Для установки фокуса при каждом открытии View (см. подробней View Lifecycle) необходимо переопределить метод viewWillAppear .
Пример:
override func viewWillAppear(animated: Bool) { 
    super.viewWillAppear(animated) 
    textField.becomeFirstResponder() 
    // ваш код...

пятница, 7 августа 2015 г.

Background Threads

И так, начнем с простого. Мы ставим перед собой задачу: перемещаться "в" и "из" фоновый поток.
И сразу натыкаемся на легкий случай - синтаксис не слишком отличается от Objective-C. В первом приближении это выглядит так:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 // do some task
 dispatch_async(dispatch_get_main_queue(), ^{
  // update some UI
 });
});
Единственное заметное отличие - то, что код на Swift может использовать замыкания за пределами параметров функции (ориг.: trailing closures), исключая необходимость в закрытии родительских скобок в конце:
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
 // do some task
 dispatch_async(dispatch_get_main_queue()) {
  // update some UI
 }
}


Совет: Для того чтобы сказать, в какой потоке выполняется конкретный кусок кода (в главном или нет), добавьте следующий код:
println("On the main thread? " + (NSThread.currentThread().isMainThread ? "Yes" : "No"))

Если используя Swift,  вы часто сталкиваетесь со следующей ситуацией: вы делаете что-то в фоне и по окончанию работы в фоновом потоке вам нужно что-то выполнить в главном потоке (например обновить UI или tableView.reloadData() и т.д. ), то вам может пригодиться умный оператор от Josh Smith:
{ /* do some task */ } ~> { /* update some UI */}
Вот демонстрация





















Более полная демонстрация возможностей для оператора ~> здесь .

Оригинал: https://thatthinginswift.com/background-threads/

суббота, 1 августа 2015 г.

Swift observers


Ниже приведен пример willSet и didSet в действии. В примере определен новый class, названный StepCounter, который отслеживает полное количество шагов пройденное пользователем. 

class StepCounter {
   var totalSteps: Int = 0 {
       willSet(newTotalSteps) {
           println("About to set totalSteps to \(newTotalSteps)")
       }
       didSet {
           if totalSteps > oldValue {
               println("Added \(totalSteps - oldValue) steps")
           }
       }
   }
}

Документация: 


четверг, 30 июля 2015 г.

Примитивные типы в Swift: замыкания и перечисления

1. Замыкания (closures)

Closure Expression Syntax


  • { (parameters) -> return type in
  •     statements
  • }

Пример:

  • reversed = sorted(names, { (s1: String, s2: String) -> Bool in
  • return s1 > s2
  • })


Документация: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html


2. Перечисления (Enum)

Enumeration Syntax

Синтаксис:
  1. enum SomeEnumeration {
  2. // enumeration definition goes here
  3. }
Пример:
  1. enum CompassPoint {
  2. case North
  3. case South
  4. case East
  5. case West
  6. }


Документация: 

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html


среда, 29 июля 2015 г.

Добавление и удаление строк в UITableView

1. Добавление строки

Метод @IBAction связанный с кнопкой Add,  вызываемый для добавления элементов/строк в UITableView :

четверг, 23 июля 2015 г.

Swipes and shakes in swift

1. Обработка и добавление swipe-жеста
 //...
       let swipeRightRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("onSwipe:"))
        swipeRightRecognizer.direction = UISwipeGestureRecognizerDirection.Right
        self.view.addGestureRecognizer(swipeRightRecognizer)
    }
   
    func onSwipe(gesture: UIGestureRecognizer) {
        if let swipe = gesture as? UISwipeGestureRecognizer {
            switch swipe.direction {
            case UISwipeGestureRecognizerDirection.Right:
                println("right")
            default:
                break
            }
        }
    }


2.  Обработка и добавление shake-жеста

override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
        if motion == UIEventSubtype.MotionShake {
            println("shake")
        }
 }


пятница, 17 июля 2015 г.

Keyboard Control

Для того чтобы клавиатура исчезала при тапе на свободное место экрана необходимо добавить :

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        self.view.endEditing(true)

}

Для того чтобы клавиатура исчезала при тапе на UIButton :
@IBOutlet weak var textView: UITextView!
@IBAction func click(sender: AnyObject) {
        textView.resignFirstResponder()
}

Для того чтобы клавиатура исчезала при тапе на кнопку Return (UITextField):

// Добавляем protocol UITextFieldDelegate
class ViewController: UIViewController, UITextFieldDelegate
    @IBOutlet weak var field: UITextField// Добавляем ссылку на UITextField

    override func viewDidLoad() {
        super.viewDidLoad()
        //Устанавливаем ссылку delegate на self   
        self.field.delegate = self

    }
   
    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
//...
}

вторник, 7 июля 2015 г.

Where am I App

Запускаем XCode и Создаем приложение:

Выбираем Single View Application.

Language: Swift
Devices: IPhone

Добавляем зависимости:

Для этого щелкаем по знаку + в разделе Link Binary With Libraries.

Добавляем 2 фреймворка:

CoreLocation

И MapKit





Выбираем в Project Navigator (Левое меню) Main.storyboard :

Добавляем из Object Library MapKitView:


Добавляем Delegate k ViewController правым щелчком по MapKitView вызвав меню:


Для того что бы ОС разрешила нам определять текущее местоположение устройства необходимо запросить у пользователя разрешение. В Project Manager выбираем Supporting Files -> Info.pllist
Открываем как Source Code:
Добавляем строки:
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App need to know(when it starts) your location</string>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>App tracking your location to calculate your activity time</string>


 Естественно, что для настоящего приложения необходимы более убедительные объяснения.


Переходим во ViewController.swift и импортируем фреймворки:

import MapKit
import CoreLocation



Добавляем интерфейсы делегатов MKMapViewDelegate, CLLocationManagerDelegate к классу:

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

Добавляем outlet MKMapView с именем map:


@IBOutlet weak var map: MKMapView!


Исходный текст с комментариями

import UIKit
import MapKit
import CoreLocation

class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var map: MKMapView!
   
    var manager = CLLocationManager() // CoreLocation manager
   
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        initializeManager() // CoreLocation manager initializations
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

   
    func initializeManager() {
        manager.delegate = self // Устанавливаем delegate
        manager.desiredAccuracy = kCLLocationAccuracyBest //Устанавливаем точность
        manager.requestWhenInUseAuthorization() // Запрашиваем разрешение на получение данных о местонахождении (1 раз)
        manager.startUpdatingLocation() // Начинаем получать данные о текущем положении
    }
   
    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) { //callback вызываемый после получения данных о местоположении
//        println("locations : \(locations)") // отладка
       
        var currentLocation:CLLocation = locations[0] as! CLLocation // Получаем самые свежие данные о текущем положении
        var latitude = currentLocation.coordinate.latitude // широта
        var longitude = currentLocation.coordinate.longitude // долгота
        let speed = currentLocation.speed
        let altitude = currentLocation.altitude
        let accuracy = currentLocation.horizontalAccuracy
       
        var dict = ["latitude": latitude, "longitude": longitude]
       
        label.text = "latitude \(latitude), longitude \(longitude), altitude \(altitude), speed \(speed), accuracy \(accuracy)"
       
        setRegion(setLocation(dict)) //устанавливаем текущее местоположение на карте
    }
   
    func setLocation(dict: Dictionary<String, CLLocationDegrees>) -> CLLocationCoordinate2D{ // helper для получения location:CLLocationCoordinate2D
       
       
        var latitude:CLLocationDegrees = dict["latitude"]! // получаем широту из словаря - параметр функции
        var longitude:CLLocationDegrees = dict["longitude"]! // получаем долготу из словаря - параметр функции
       
        var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
       
        return location
    }
   
    func setRegion(location: CLLocationCoordinate2D) {  // helper для установки текущего местоположения на карте
        var latDelta:CLLocationDegrees = 0.01 // точность при определении широты
        var lonDelta:CLLocationDegrees = 0.01 // точность при определении долготы
       
        var span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta) // точность
        var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span) // текущее местоположение
       
        map.setRegion(region, animated: true) // установка текущего местоположения на карте
    }
}