Цена за 48 часов в ленте | 1750,00 |
Цена за 1 час закрепления | N/A |
Взаимопиар | Нет |
NotificationCenter
), KVO
или Combine
в жизненном цикле UIViewController
лучше размещать в методах, где гарантируется её актуальность и корректное удаление. override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(handleNotification),
name: .someNotification,
object: nil
)
}
@objc func handleNotification(_ notification: Notification) {
print("Получено уведомление")
}
viewWillAppear()
.var cancellable: AnyCancellable?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
cancellable = NotificationCenter.default.publisher(for: .someNotification)
.sink { _ in
print("Событие получено")
}
}
NotificationCenter
:override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .someNotification, object: nil)
}
Combine
подписки (cancellable
)override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
cancellable?.cancel()
cancellable = nil
}
deinit {
NotificationCenter.default.removeObserver(self)
}
Float
. Double
имеет 64 бита, а Float
– 32 бита. Это значит, что Double
может хранить более точные значения, что особенно важно при математических вычислениях.sin()
, cos()
, pow()
) работают именно с Double
. let x = 3.14 // По умолчанию это Double
let y = sin(x) // sin() принимает Double
Double
выполняются так же быстро или даже быстрее, чем с Float
, из-за оптимизаций в аппаратном обеспечении.Float
может округлять числа с потерей точности, что может привести к неожиданным результатам. Float
:let a: Float = 0.1 + 0.2
print(a == 0.3) // false 😱
Float
, надо указать это явно:let number: Float = 3.14
let number = 3.14 as Float
UserDefaults
— это хранилище для сохранения простых данных (строки, числа, массивы). Но если нужно сохранить сложные объекты, их сначала кодируют в `Data` (Codable), а затем сохраняют. UserDefaults
работает без кодирования:let defaults = UserDefaults.standard
// Сохранение
defaults.set("Иван", forKey: "username")
defaults.set(25, forKey: "age")
// Чтение
let name = defaults.string(forKey: "username") ?? "Нет имени"
let age = defaults.integer(forKey: "age")
print(name, age) // Иван 25
Data
.struct User: Codable {
let name: String
let age: Int
}
UserDefaults
let user = User(name: "Иван", age: 25)
let defaults = UserDefaults.standard
if let encoded = try? JSONEncoder().encode(user) {
defaults.set(encoded, forKey: "user")
}
UserDefaults
if let savedData = defaults.data(forKey: "user"),
let savedUser = try? JSONDecoder().decode(User.self, from: savedData) {
print(savedUser.name, savedUser.age) // Иван 25
}
defaults.removeObject(forKey: "user")
let users = [
User(name: "Иван", age: 25),
User(name: "Анна", age: 30)
]
if let encoded = try? JSONEncoder().encode(users) {
defaults.set(encoded, forKey: "users")
}
// Читаем массив обратно
if let savedData = defaults.data(forKey: "users"),
let savedUsers = try? JSONDecoder().decode([User].self, from: savedData) {
print(savedUsers) // [{name: Иван, age: 25}, {name: Анна, age: 30}]
}
struct MyStruct {
func printMessage() {
print("Hello from MyStruct")
}
}
let instance = MyStruct()
instance.printMessage() // Вызов определяется на этапе компиляции
class BaseClass {
func printMessage() {
print("Hello from BaseClass")
}
}
class SubClass: BaseClass {
override func printMessage() {
print("Hello from SubClass")
}
}
let instance: BaseClass = SubClass()
instance.printMessage() // Вызов определяется в runtime с использованием vtable
protocol MyProtocol {
func printMessage()
}
struct MyStruct: MyProtocol {
func printMessage() {
print("Hello from MyStruct")
}
}
let instance: MyProtocol = MyStruct()
instance.printMessage() // Вызов определяется в runtime с использованием witness table
dynamic
или методов Objective-C. Метод определяется в runtime с использованием Objective-C runtime. Для методов, которые должны быть динамически разрешены в runtime, обычно для взаимодействия с Objective-C. import Foundation
class MyClass: NSObject {
@objc dynamic func printMessage() {
print("Hello from MyClass")
}
}
let instance = MyClass()
instance.printMessage() // Вызов определяется в runtime с использованием Objective-C runtime
layoutIfNeeded()
или layoutSubviews()
в iOS-приложении, нужно изменить значение констрейнта и вызвать анимацию, если необходимо. Вот основные шаги:layoutIfNeeded()
, измените свойство констрейнта (например, constant
у NSLayoutConstraint
).class ViewController: UIViewController {
@IBOutlet weak var button: UIButton!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.changeButtonHeight()
}
}
func changeButtonHeight() {
heightConstraint.constant = 100 // Меняем значение констрейнта
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded() // Перестраиваем макет
}
}
}
var expanded = false
@IBOutlet weak var smallHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var largeHeightConstraint: NSLayoutConstraint!
func toggleHeight() {
expanded.toggle()
if expanded {
NSLayoutConstraint.deactivate([smallHeightConstraint])
NSLayoutConstraint.activate([largeHeightConstraint])
} else {
NSLayoutConstraint.deactivate([largeHeightConstraint])
NSLayoutConstraint.activate([smallHeightConstraint])
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
viewDidLoad()
элементы ещё не отрисованы, поэтому layoutIfNeeded()
не сработает. Используйте viewDidAppear()
или вызовите layoutIfNeeded()
после view.layoutIfNeeded()
.override func viewDidLoad() {
super.viewDidLoad()
heightConstraint.constant = 100
view.layoutIfNeeded() // НЕ обновит макет, потому что он ещё не загружен
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
heightConstraint.constant = 100
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
layoutIfNeeded()
и setNeedsLayout()
heightConstraint.constant = 100
view.setNeedsLayout() // Обновление произойдет на следующем цикле рендера