Трюк использования #function
UserDefaults
let isInstalledErlier = UserDefaults.standard.bool(forKey: "isInstalledErlier") ?? false if isInstalledErlier { UserDefaults.standard.set(true, forKey: "isInstalledErlier") }
Если создать расширение для UserDefaults
extension UserDefaults { var isInstalledErlier: Bool { get { return bool(forKey: #function) } set { set(newValue, forKey: #function) } } }
то использовать будет проще
if UserDefaults.standard.isInstalledErlier == false { // тут какой-то полезный код UserDefaults.standard.isInstalledErlier = true }
Пример
extension UserDefaults { var firstLaunch: Bool { get { return bool(forKey: #function) } set { set(newValue, forKey: #function) } } }
extension UserDefaults { var isActivated: Bool { get { return bool(forKey: #function) } set { set(newValue, forKey: #function) } } var appName: String { get { return string(forKey: #function)! } set { set(newValue, forKey: #function) } } }
UserDefaults.standard.appName = "Tinder"
(Обсуждение) Вопросы собеседований на Junior Developer
Темы для собеседований:
- Простые типы и алгоритмы работы с ними
- Многопоточность (MultiThreading)
- Жизненный цикл MVC
- Runloop
- CoreData
Простые типы и алгоритмы работы с ними
обычно, эта тема называется Структуры данных и алгоритмы
Связанный список
Стек
Очередь
RxSwift
RxGesture
labelView.rx .anyGesture(.longPress()) .when(.began) .subscribe(onNext: {_ in //react to long Press UIView.animate(withDuration: 1, animations: { self.labelView.bounds.size.width = 200 }, completion: { ok in self.labelView.bounds.size = self.sizeOfButton }) print("long press") }).addDisposableTo(disposeBag)
UISwitch
var uiDone = UISwitch() uiDone.rx.isOn .subscribe(onNext: { isOn in print( isOn ? "it's ON" : "it's OFF" ) try! self.uiRealm.write { ()->Void in self.taskModel.isCompleted = isOn } }).addDisposableTo(disposeBag)
RunLoop
Run loop делает две вещи
- ожидает пока что-то не произойдёт.
- отправляет сообщение к получателю.
Несколько интересных моментов про таймер
- runloop к которому ты добавляешь таймер держит на него сильную ссылку (то есть тебе не обязательно локально складывать таймер в strong property).
- Таймер ретейнит свой таргет (тот у которого он будет вызывать селектор).
- запуск в бекграунде. Селектор таймера не вызывается т. к. на background queue другой ранлуп
- интересный момент про таймер, чтобы запустить его не на мейн потоке надо сперва стартануть ранлуп
- scheduledTimerWithTimeInterval добавляет таймер в существующий ранлуп главного треда, не надо ничего стартовать
Ссылки по теме:
http://macbug.ru/cocoa/mthreadrunloops
https://habrahabr.ru/post/199130/
UIPanGestureRecognizer PanGesture
вот и всё что нужно чтобы повесить PanGesture программно, просто пишем его кодом
func setupGestures(){ let pan = UIPanGestureRecognizer(target: self, action: #selector(actionPan(_:))) scaleView.addGestureRecognizer(pan) }
сам обработчик касания
func actionPan(_ sender:UIPanGestureRecognizer){ let moving = sender.translation(in: self) print("Pan Action: \(moving)") let toPoint = CGPoint(x: scaleView.center.x + moving.x, y: scaleView.center.y + moving.y) scaleView.center = toPoint sender.setTranslation(CGPoint(x: 0, y: 0), in: self) }
@discardableResult
полезный модификатор возвращаемого типа, если не всегда хотим получать warning при вызове функции, не обрабатывая её результат
@discardableResult func getItem(x:Int) -> Int { return x*x }
getItem()
пример кода с CocoaHeads Май 2017 Alexander Zimin
protocol CellViewAnyModel { static var cellAnyType: UIView.Type { get } func setupAny(cell: UIView) } protocol CellViewModel: CellViewAnyModel { associatedtype CellType: UIView func setup(cell: CellType) } extension CellViewModel { static var cellAnyType: UIView.Type { return CellType.self } func setupAny(cell: UIView) { if let cell = cell as? CellType { setup(cell: cell) } else { assertionFailure("Wrong usage") } } } extension UITableView { func dequeueReusableCell(withModel model: CellViewAnyModel, for indexPath: IndexPath) -> UITableViewCell { let indetifier = String(describing: type(of: model).cellAnyType) let cell = self.dequeueReusableCell(withIdentifier: indetifier, for: indexPath) model.setupAny(cell: cell) return cell } func register(nibModels: [CellViewAnyModel.Type]) { for model in nibModels { let identifier = String(describing: model.cellAnyType) let nib = UINib(nibName: identifier, bundle: nil) self.register(nib, forCellReuseIdentifier: identifier) } } }
Как получить имя класса из статического метода ?
вариант 1
через рефлексию
static func getEntityName() -> String { return String(describing: Mirror(reflecting: self).subjectType).components(separatedBy: ".").first! }
вариант 2
немного устаревший вариант
static func getEntityName() -> String { return NSStringFromClass(self).components(separatedBy: ".").last! }
ChemNote
Кодогенерация — это просто
Sourcery
Пример файла настроек для генерации
sources: - src/ templates: - Templates/ output: out/ args:
у ключей sources и templates обязательно должны стоять символы —
В качестве шаблонизатора используется Stencil
для кастомизации имён генерируемых файлов можно использовать спец теги // sourcery:file:
// sourcery:file:TemplateName.swift ... какой-то наш код // sourcery:end
пример:
// sourcery:file:TemplateName.swift // template of UIView import UIKit class EventModelView: UIView, EventProtocol { } // sourcery:end // sourcery:file:Views/TemplateName2.swift // template of UIView import UIKit class EventModelView2: UIView, EventProtocol { } // sourcery:end
UI по мотивам Марсианин
Parallax Effect with Motion Device
Формула Parallax
var parallaxImageHeight: CGFloat { let maxOffset = (sqrt( cellHeight * cellHeight + 4 * parallaxSpeed * self.tableView.frame.height) - cellHeight) / 2 return maxOffset + self.cellHeight }
hitTest
Задача
У вас есть UIView c N количеством кнопок внутри
и вот прикол, высота этой вьюхи 1 пиксель,
а теперь вопрос как вы обработаете Tap'ы по каждой из кнопок ?
решение 1
//MARK: hitTest override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { if buttonOne.frame.contains(point){ return buttonOne } else if buttonOne.frame.contains(point){ return buttonTwo } else if buttonOne.frame.contains(point){ return buttonThree } else { return nil } }
решение 2 (более приятное)
//MARK: hitTest override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { return getButton(point, buttons: [buttonOne, buttonTwo, buttonThree]) } func getButton(_ point:CGPoint, buttons: [UIView] ) -> UIView? { for button in buttons { if (button.frame.contains(point)){ return button } } return nil }
GestureRecognizer
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) greenButton.addGestureRecognizer(singleTap)
Generamba: как использовать ?
пример rambafile
### Headers settings company: adeveloper ### Xcode project settings project_name: SkillViperT xcodeproj_path: SkillViperT.xcodeproj ### Code generation settings section # The main project target name project_target: SkillViperT ### project_file_path: SkillViperT/Modules ### Templates templates: #- {name: local_template_name, local: 'absolute/file/path'} #- {name: remote_template_name, git: 'https://github.com/igrekde/remote_template'} #- {name: catalog_template_name} - {name: swifty_viper}
CSS для UIKit
import UIKit struct ButtonStyles { static var circle: Decoration<UIButton> { return { (view:UIButton) in let size = max(view.bounds.width, view.bounds.height) view.layer.cornerRadius = size/2 view.layer.masksToBounds = true } } }