Наряду с привычными способами, цвета UIColor могут создаваться на основе изображений UIImage. Такое бывает полезно, например, если нужно закрасить фон паттерном.
import UIKit
var view: UIView
var image: UIImage
var context: CGContext
// Draw pattern image using Core Graphics functions
UIGraphicsBeginImageContextWithOptions(CGSize(width: 20, height: 20), false, 0)
context = UIGraphicsGetCurrentContext()!
CGContextSetStrokeColorWithColor(context, UIColor(white: 0.5, alpha: 1).CGColor)
CGContextStrokeRectWithWidth(context, CGRect(x: 0, y: 0, width: 20, height: 20), 0.5)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
view = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 120))
// Set view background as image color
view.backgroundColor = UIColor(patternImage: image)

Изменение радиуса углов (cornerRadius), толщины (borderWidth) и цвета (borderColor) рамки объектов UIView доступно через свойство layer.
import UIKit
var view = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 120))
view.layer.borderWidth = 1
view.layer.cornerRadius = 10
view.layer.borderColor = UIColor.redColor().CGColor
view.layer.cornerRadius = view.bounds.width / 2
view.layer.borderColor = UIColor.blueColor().CGColor
view.layer.borderWidth = 0
view.layer.cornerRadius = view.bounds.width
view.backgroundColor = UIColor.blackColor()

Одной из особенностей языка Swift являются протоколы. По сути — это интерфейсы, определяющие список свойств и методов, которые должны быть реализованы принимающими классами, структурам или перечислениями. Для выстраивания иерархии, протоколы поддерживают наследование, в том числе множественное. Также их разрешается использовать как типы; отличительной чертой служит возможность создавать типы-композиции через protocol<Protocol1, Protocol2, ..., ProtocolN>.
import UIKit
protocol Selectable {
var selected: Bool { get set }
func select()
func deselect()
}
protocol Colorable {
func paint()
}
// Shape protocol inherits Selectable and Colorable protocols
protocol Shape: Selectable, Colorable {
var type: String { get }
}
protocol Rounded {
var radius: Double { get set }
}
// Oval inherits UIView class and adopts protocols Rounded and Shape
class Oval: UIView, Rounded, Shape {
let type = "oval"
var radius = 10.0
var selected = false
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func select() {
selected = true
}
func deselect() {
selected = false
}
func paint() {
backgroundColor = UIColor.redColor()
}
}
class Squircle: UIView, Rounded, Shape {
let type = "squircle"
var radius = 3.0
var selected = false
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func select() {
selected = true
}
func deselect() {
selected = false
}
func paint() {
backgroundColor = UIColor.greenColor()
}
}
// Define composition protocol type
typealias RoundedShape = protocol<Rounded, Shape>
// Array initialization as [protocol<Rounded, Shape>]() doesn't work
var roundedShapes = [RoundedShape]()
roundedShapes.append(Oval())
roundedShapes.append(Squircle())
С выходом Swift 2.0 протоколы обзавелись расширениями. Они позволяют добавлять реализацию по умолчанию методов и свойств (новых или уже существующих). Если необходимо, область действия расширений ограничивается только нужными типами, делается это конструкцией where.
import UIKit
// Make Selectable protocol adoptable only by classes (not structs, enums)
protocol Selectable: class {
var selected: Bool { get set }
func select()
func deselect()
}
/*
Add default implementations of select() and deselect() protocol methods
and new toggleSelection() method only for UIView subclasses that adopt Selectable protocol
*/
extension Selectable where Self: UIView {
func select() {
selected = true
}
func deselect() {
selected = false
}
func toggleSelection() {
selected = !selected
}
}
class Shape: UIView, Selectable {
var selected = false
}
Встроенные протоколы тоже можно расширять.
import Foundation
extension BooleanType {
var trueReverse: Bool {
if self {
return !Bool(self)
} else {
return Bool(self)
}
}
}
print(true.trueReverse)
// Output: false
print(false.trueReverse)
// Output: false
Дополнительные материалы:
До выхода iOS 7.0 единственным интерфейсом для загрузки данных по сети служил класс NSURLConnection, затем был добавлен NSURLSession, который со временем стал полноценным преемником, обладающим расширенными и более гибкими возможностями. Начиная с iOS 9.0 NSURLConnection более не рекомендуется использовать.
NSURLSession поддерживает основные протоколы передачи информации, в том числе защищенные. Работа осуществляется как с обычными файлами, так и объектами NSData.
Для простых задач у класса есть метод NSURLSession.sharedSession(), возвращающий преднастроенный синглтон-экземпляр. Настройки по умолчанию достаточно оптимальны, единственным серьезным минусом является невозможность задать делегата, отслеживающего все события процесса, а не только завершение загрузки посредством функции-обработчика.
import Foundation
import XCPlayground
// Playground configuration
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let url = NSURL(string: "http://valery.bashkatov.org/favicon.png")!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url,
completionHandler:
// This function is called when the task is completed
{
(data: NSData?,
response: NSURLResponse?,
error: NSError?) -> Void in
if error == nil {
print("Downloading completed")
} else {
print(error)
}
})
// Run task
task.resume()
Если же требования выше, NSURLSession конфигурируется через объект NSURLSessionConfiguration. Сначала необходимо создать одну из базовых конфигураций вызовом соответствующего метода класса (возвращается новый экземпляр, а не синглтон):
- NSURLSessionConfiguration.defaultSessionConfiguration() — включает настройки по умолчанию, для кэширования используется диск.
- NSURLSessionConfiguration.ephemeralSessionConfiguration() — аналогично defaultSessionConfiguration за исключением кэширования, все хранится только в оперативной памяти.
- NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(_ identifier: String) — загрузка осуществляется в фоновом режиме даже при неактивном приложении, кроме того, после внештатного завершения задание может быть продолжено по идентификатору. Этот режим обладает рядом ограничений.
Затем возможно изменить неустраивающие значения свойств.
Во время инициализации NSURLSession разрешается также указать делегата, получающего контроль над процессом загрузки через реализацию протоколов:
import UIKit
class ViewController: UIViewController, NSURLSessionDataDelegate {
var data: NSMutableData!
var dataURL: NSURL!
var session: NSURLSession!
var sessionConfiguration: NSURLSessionConfiguration!
var sessionTask: NSURLSessionDataTask!
override func viewDidLoad() {
super.viewDidLoad()
data = NSMutableData()
dataURL = NSURL(string: "http://valery.bashkatov.org/favicon.png")!
// Used ephemeral session for disable disk-based cache
sessionConfiguration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
sessionConfiguration.timeoutIntervalForRequest = 10
session = NSURLSession(configuration: sessionConfiguration,
delegate: self,
delegateQueue: nil)
sessionTask = session.dataTaskWithURL(dataURL)
sessionTask.resume()
}
// Method defined in NSURLSessionDataDelegate protocol
func URLSession(session: NSURLSession,
dataTask: NSURLSessionDataTask,
didReceiveResponse response: NSURLResponse,
completionHandler: (NSURLSessionResponseDisposition) -> Void) {
print("Received server response")
print("Expected file size: \(response.expectedContentLength) bytes")
completionHandler(NSURLSessionResponseDisposition.Allow)
print("Downloading started")
}
// Method defined in NSURLSessionDataDelegate protocol
func URLSession(session: NSURLSession,
dataTask: NSURLSessionDataTask,
didReceiveData data: NSData) {
print(" Received: \(data.length) bytes")
// Collect full data, chunk by chunk
data.enumerateByteRangesUsingBlock
{
[unowned self] (bytes: UnsafePointer<Void>,
byteRange: NSRange,
stop: UnsafeMutablePointer<ObjCBool>) in
self.data.appendBytes(bytes, length: byteRange.length)
}
}
// Method defined in NSURLSessionTaskDelegate protocol
func URLSession(session: NSURLSession,
task: NSURLSessionTask,
didCompleteWithError error: NSError?) {
guard error == nil else {
print(error)
return
}
print("Downloading completed")
}
}
Как и другие современные языки, Swift работает со строками в Юникоде, поэтому соответствие один символ — один байт не всегда верно.
import Foundation
var string = "Hello"
print(string.characters.count)
// Output: 5
print(string.utf8.count)
// Output: 5
string = "H?llo"
print(string.characters.count)
// Output: 5
print(string.utf8.count)
// Output: 6
Чтобы получить список именно символов, а не отдельных скаляров, есть свойство characters (типа String.CharacterView). Кроме прочих возможностей, оно поддерживает посимвольные циклы for-in и индексное обращение через [].
Особенностью является специальный тип индексов — String.CharacterView.Index (он же String.Index). Причем сами значения индексов задаются не абсолютно, а вычисляются относительно позиционных маркеров в начале и конце строки путем сдвига от них на нужное количество символов.
import Foundation
var string = "H?llo"
for character in string.characters {
print(character)
}
/*
Output:
H
?
l
l
o
*/
print(string.characters[0])
// Output: Cannot subscript a value of type 'String.CharacterView' with an index of type 'Int'
print(string.characters[string.startIndex])
// Output: H
string.replaceRange(string.startIndex.successor()...string.endIndex.advancedBy(-4), with: "a")
print(string)
// Output: Hallo