Ответы пользователя по тегу Swift
  • Swift iOS Как измерить скорость соединения?

    doublench21
    @doublench21 Куратор тега Swift
    Если делать надёжно, можно взять всем известный ipef3, исходники на Си благополучно находятся здесь https://github.com/esnet/iperf
    Пишите простую обёртку на Swift с нужными Вам типами измерений над этой библиотекой. (Это делается очень легко, информации куча)
    Ну собственно теперь подключается свою обёртку на Swift(import MySwiftIperf) и начинаете измерять.

    Если делать проще, то можно качать обычным URLSession некий большой файл. У URLSession есть отличный делегат, в котором можно глянуть текущую порцию скаченных данных. Этого достаточно для измерения скорости. Начальное время, текущее время, текущее кол-во скаченных байт, всё это делим на нужное измерение в кб/мб/...

    P.S. Красивая стрелка это тема для отдельного разговора.
    Ответ написан
    1 комментарий
  • Как сделать филтер структуры?

    doublench21
    @doublench21 Куратор тега Swift
    Покажу вам два варианта.

    --------------------------------------------------------
    Вариант простой(aka Думми):
    Обычная фильтрация, после того как память уже выделена. Просто, но разброс по памяти и скорость ниже.
    Код с подсветкой
    5d1398ab0b187603385123.png


    --------------------------------------------------------
    Вариант продвинутый:
    Исправляем наименование свойств.
    Преобразуем даты в соответствующий формат.
    Проводим свою кастомную кодировку, отслеживая статус и выкидывая ошибку, дабы не выделять зря память.
    Код с подсветкой
    2QkNeGLn.png
    Код текстом
    import Foundation
    
        let json = """
        [{
            "idSite": 204233,
            "status": 0,
            "Name": "",
            "timeOfStatus": null
        },
        {
            "idSite": 204234,
            "status": 0,
            "Name": "",
            "timeOfStatus": null
        },
        {
            "idSite": 204235,
            "status": 0,
            "Name": "",
            "timeOfStatus": null
        },
        {
            "idSite": 204236,
            "status": 1,
            "Name": "OK",
            "timeOfStatus": "2019-06-26 12:58:16"
        }]
        """
    
        struct SomeStruct: Decodable {
          let idSite: Int
          let status: Bool
          let name: String
          let timeOfStatus: Date?
    
          // Преобразуем Name -> name
          private enum CodingKeys: String, CodingKey {
            case idSite
            case status
            case name = "Name"
            case timeOfStatus
          }
    
          init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
    
            self.idSite = try container.decode(Int.self, forKey: .idSite)
    
            // Выбрасываем ошибку, если статус не равен 1. Не выделяем зря память.
            let status = try container.decode(Int.self, forKey: .status)
            if status == 1 {
              self.status = true
            } else {
              throw DecodingError.dataCorruptedError(forKey: .status, in: container, debugDescription: "Status mast be `1`")
            }
    
            self.name = try container.decode(String.self, forKey: .name)
    
            // Преобразуем строку с датой в объект для работы с датами Date
            let date = try? container.decode(String.self, forKey: .timeOfStatus)
            let dateFormatter: DateFormatter = {
              $0.dateFormat = "yyyy-MM-dd HH:mm:ss"
              $0.calendar = Calendar(identifier: .iso8601)
              $0.timeZone = TimeZone(secondsFromGMT: 0)
              $0.locale = Locale(identifier: "en_US_POSIX")
              return $0
            }(DateFormatter())
            self.timeOfStatus = date != nil ? dateFormatter.date(from: date!) : nil
          }
        }
    
        // Тип обёртка, которая даст легко отфильтровать данные, от тех, которые не прошли из-за статуса.
        enum Throwable<T: Decodable>: Decodable {
          case success(T), failure(Error)
    
          init(from decoder: Decoder) throws {
            do {
              let decoded = try T(from: decoder)
              self = .success(decoded)
            } catch let error {
              self = .failure(error)
            }
          }
    
          var value: T? {
            switch self {
            case .failure(_):
              return nil
            case .success(let value):
              return value
            }
          }
        }
    
    
        let decoder = JSONDecoder()
        let arrayOfSomeStructs = try! decoder.decode([Throwable<SomeStruct>].self, from: json.data(using: .utf8)!)
        let filteredArrayOfSomeStructs = arrayOfSomeStructs.compactMap{ $0.value }
        print(filteredArrayOfSomeStructs.count)
    Ответ написан
    Комментировать
  • Как решить ошибку "unrecognized selector sent to instance"?

    doublench21
    @doublench21 Куратор тега Swift
    5d10efdae0912561988174.png

    Код
    func playVideo(filename: String) {
        let path = Bundle.main.path(forResource: filename, ofType:"mp4")!
        player = AVPlayer(url: NSURL(fileURLWithPath: path) as URL)
    
        playerLayer = AVPlayerLayer(player: player)
        playerLayer.frame = CGRect(x: 880, y: 270, width: 960, height: 540)
        scene!.view!.layer.addSublayer(playerLayer)
    
        player.actionAtItemEnd = .none
        player.play()
    
    
    
        DispatchQueue.main.async {
          NotificationCenter.default.addObserver(self,
                                                 selector: #selector(playerItemDidReachEnd),
                                                 name: NSNotification.Name.AVPlayerItemDidPlayToEndTime ,
                                                 object: self.player.currentItem)
        }
      }
    
      @objc func playerItemDidReachEnd(notification: NSNotification) {
        print("repeating...")
    
        let seconds : Int64 = 0
        let preferredTimeScale : Int32 = 1
        let seekTime : CMTime = CMTimeMake(value: seconds, timescale: preferredTimeScale)
    
        player.seek(to: seekTime)
        player.play()
      }
    Ответ написан
    1 комментарий
  • Как правильно сделать структуру?

    doublench21
    @doublench21 Куратор тега Swift
    Простой пример:

    2ffZng2.png

    Код
    let json = """
        [{
        "invoiceNumber": "FV/MON/X/369",
        "date": "2019-06-01 00:00:00",
        "dueDate": "2019-06-01 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/368",
        "date": "2019-06-01 00:00:00",
        "dueDate": "2019-06-01 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/367",
        "date": "2019-06-01 00:00:00",
        "dueDate": "2019-06-01 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/366",
        "date": "2019-06-01 00:00:00",
        "dueDate": "2019-06-01 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/363",
        "date": "2019-05-04 00:00:00",
        "dueDate": "2019-05-04 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/362",
        "date": "2019-05-04 00:00:00",
        "dueDate": "2019-05-04 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/361",
        "date": "2019-05-04 00:00:00",
        "dueDate": "2019-05-04 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        },
        {
        "invoiceNumber": "FV/MON/X/360",
        "date": "2019-05-04 00:00:00",
        "dueDate": "2019-05-04 00:00:00",
        "overdue": true,
        "valueNoTax": 100,
        "valueWithTax": 123,
        "valueToPay": 123
        }]
        """
    
        struct FakturyObject: Decodable {
          let date: String
          let dueDate: String
          let invoiceNumber: String
          let overdue: Bool // <---- ОШИБКА БЫЛА ТУТ! 
          let valueNoTax: Int
          let valueToPay: Int
          let valueWithTax: Int
        }
    
        let data = json.data(using: .utf8)
        let decoder = JSONDecoder()
        let parsedData = try! decoder.decode([FakturyObject].self, from: data!)
    Ответ написан
    1 комментарий
  • Как отловить изменения динамической строки в таблице в SWIFT?

    doublench21
    @doublench21 Куратор тега Swift
    Этот код абсолютно ужасен.
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "chat") as! ChatsTableViewCell;
            cell.imageUser.image = UIImage.convertBase64ToImage(imageString: (data?.list[indexPath.row].imageUser)!)
            cell.imageUser.layer.cornerRadius =  cell.frame.size.height / 2;
            cell.imageUser.clipsToBounds = true;
            return cell;
    }


    Во-первых, не нужно делать каждый раз то, что можно и нужно сделать один раз.
    Во-вторых, вы ведь всегда можете отследить изменения размеров, просто переопределив соответсвующее св-во.
    (Если у вас ячейка определена в сториборде, то первые два метода можно удалить и записать всё в методе awakeFromNib)
    Ещё внутри предопределенного св-ва я написал функцию printf. Посмотрите, сколько раз вызывается эта функция и сравните с тем, сколько раз будет вызываться ваш код в
    tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)


    5d0f6aaef237c045355418.png

    Код
    import UIKit
    
    final class Cell: UITableViewCell {
    
      override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    
        imageView?.clipsToBounds = true
        imageView?.contentMode = .scaleAspectFit
      }
    
      required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
      }
    
      override var frame: CGRect {
        didSet {
          guard frame.height != oldValue.height else { return }
          imageView?.layer.cornerRadius = frame.height / 2.0
          print(#function)
        }
      }
    }
    
    final class TableViewController: UITableViewController {
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
    
        tableView.register(Cell.self, forCellReuseIdentifier: "Cell")
      }
    
      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 21;
      }
    
      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.imageView?.image = UIImage(named: "c-1-8")!
        cell.textLabel?.text = "Ooops";
        return cell;
      }
    
      override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return tableView.frame.width * 0.25;
      }
    }



    5d0f6c640a520243262603.png

    P.S. Надеюсь я Вас правильно понял.
    Ответ написан
  • Как исправить ошибку Declaration ‘pressesEnded(presses:withEvent:)’ has different argument labels from any potential overrides?

    doublench21
    @doublench21 Куратор тега Swift
    Ну Вам ведь даже сам Xcode пишет:
    Слушай, я вижу что такой метод есть, но почему-то у тебя отличаются названия агрументов. Может глянешь?


    Для Вас даже в Xcode есть встроенная документация - ⌘ + Shift + 0. Если ввести название этого метода и глянуть на аргументы, то становится ясно, что presses нужно заменить _ presses, а withEvent нужно заменить на with Тоже самое сделать и со вторым методом.

    func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?)


    P.S. Если Вы позиционируете себя iOS разработчиком, то как вы дальше та будете, если такая простая ошибка вызывает диссонанс.
    Ответ написан
    1 комментарий
  • Почему в Swift вижу чаще использование let, чем var?

    doublench21
    @doublench21 Куратор тега Swift
    Иммутабельность переменной - это достаточно важное свойство. Переменные нужно помечать ключевым словом let не просто так. Опустим очевидное, исходящее из определения - неизменяемость. Но что стоит за этим на уровне компилятора? А стоят за этим нужные вещи.

    Если мыслить широко, то ключевым словом let можно ведь пометить не только, скажем переменную содержащую число, а коллекцию или структуру или объект.

    Коллекция, помеченная как неизменяемая, даёт возможность компилятору выделить для неё непрерывную память, что позволит ускорить операцию чтения, в сравнении с обычной, динамической коллекцией. (Именно по этой причине, для особых нужд, в Swift, есть специальная коллекция ContiguousArray)

    Для изменяемых объектов, компилятору может потребоваться сгенерировать код, который копирует изменяемый объект, когда он используется как свойство другого объекта, а вот для неизменяемых лишнего кода мы не получим.

    Сравнение для неизменяемых объектов также может быть оптимизировано компилятором.

    Даже у обычных структур, методы которые изменяют поля структуры, нужно помечать ключевым словом mutable В свою очередь это делает тоже не просто так.
    Ответ написан
    Комментировать
  • В чем отличие данной записи с дженериком и без?

    doublench21
    @doublench21 Куратор тега Swift
    Два типа Anyи AnyObject являются так называемыми Nonspecific types. Неспецифичные типы служат указателем компилятору о возможности хранения любого типа. НО за такую возможность нужно платить и Вы платите:

    Во-первых отсутствием информации о типе на момент компиляции, вы не можете вызвать, скажем некоторое св-во у String, даже зная, что присвоенная переменная на самом деле является типом String. У вас просто не будет такой информации ровно до тех пор, пока вы не преобразуете этот тип обратно.
    Конечно во время выполнения Swift прекрасно понимает, какой именно тип находится в переменной типа Any, но это только во время выполнения кода, на момент компиляции информация об этом отсутсвует.

    Во-вторых вы платите скоростью, при работе с такими типами. У компилятора нету информации о типе, а значит он не может произвести необходимые оптимизации.

    Функция print(_:separator:terminator:) получает информацию о том, что вывести в консоль, исходя из того, что все стандартные типы реализуют протоколы CustomStringConvertible, CustomDebugStringConvertible,CustomReflectable в реализации которых содержится информация о типе(текстовая). Если у типа нет реализации этих протоколов, print(_:separator:terminator:) выводит просто название типа.

    Более того, вы всегда можете вывести так называемый дамп у абсолютного любого типа в консоль посредством функции dump(_:name:indent:maxDepth:maxItems:).

    За более подробной информации о всех возможностях и разновидностях функций для работы с Debugging and Reflection можно глянуть тут

    ------------------------------------------------------------------------------------------------------------------------

    Дженерики же - это просто заместители(placeholders) типов. На момент компиляции, компилятор вместе вашего заместителя, скажем T подставит конкретный тип, скажем String. Что в свою очередь приводит к тому, что у дженериков отсутствуют проблемы указанные выше. (Даже моё "во-первых" у дженериков отсутствуют, если вы накладываете на них ограничения. )

    ------------------------------------------------------------------------------------------------------------------------

    P.S. На самом деле Дженерики одна из самых прекрасных и Сильных сторон языка Swift, и моя жалкая попытка вам пояснить - не объясняет должном счётом ничего. Изучайте их.
    Ответ написан
    Комментировать
  • Синхронное выполнение кода SWIFT alamofire?

    doublench21
    @doublench21 Куратор тега Swift
    Добро пожаловать в мир DispatchQueue или OperationQueue на ваш выбор. Первый легче. Так как сама задача у вас является асинхронной, нужно будет немного поднапрячься. Для DispatchQueue нужно будет использовать барьеры, так как обычная serial очередь не подойдёт.

    Кидайте свои наработки, будем обсуждать дальше.
    Ответ написан
    Комментировать
  • Как правильно реализовать градиент в tableview cell?

    doublench21
    @doublench21 Куратор тега Swift
    При всём моём к вам уважении, у меня глаза из орбит вылезли.

    В свой день рождения я не буду ругаться, а постараюсь задать вам вопросы для более ясной картины. И так:

    1) В курсе ли вы как работает жизненный цикл ячеек таблицы/коллекции? Судя по коду, предположу, что есть небольшие непонятки.

    2) Работа с асинхронностью в данном контексте тоже абсолютно бесполезна. Вы просто тратите время на переключения контекста.
    Помню вы хотели начать изучать асинхронность. Начали, но есть снова непонятки? Судя по коду.

    Ответьте на эти вопросы и далее мы затронем сам градиент.

    Эти два вопроса так же прилично тормозят ваш код.

    Ответы получены. Двигаемся дальше.

    1) Представьте себе что у вас ячейки высотой в 100, а вся таблица высотой в 500. Получаем 5 видимых ячеек. Таблица крайне экономно использует память телефона и на самом деле инстанцирует примерно видимое количество ячеек плюс 1-2 про запас для плавной прокрутки. Итого мы имеем 7 ячеек, которые вне зависимости сколько вы прописываете в дата соурс , будь то 50, использоваться для всех 50 будут лишь эти 7.

    Метод же cellForItem вызывается для всех ячеек которые должны появиться. То есть это больше видимых снова и снова при скролле. То есть очень часто и много.

    Этот метод должен быть настолько быстрым насколько это возможно и должен быть практически чистым от лишнего кода.

    Надеюсь теперь ясно что глаза из орбит вылезли не зря, глядя на то, что сделали Вас.

    Выводы.

    Так как таблица будет использовать лишь 7 наших инстанцов, лучшим вариантом будет перенести создание градиента внутрь самой ячейки. Градиент будет создаваться внутри инициализатора ячейки.

    А ваш код создаёт градиенты каждый раз как ячейка переиспользуется. Это как пытаться забить гвоздь в то место, где он уже есть.

    2) Асинхронность нужно использовать для затратных по времени операций. Достать значения из массива такой очевидно не является. Асинхронно нужно получать данные. В вашем случае, всю асинхронность уберите полностью.

    3) Уберите весь код по обновлению данных ячейки внутрь самой ячейки. Создайте метод, скажем func configure(...) и вызывайте аккуратно в методе cellForItem.

    4) Попробуйте выставить градиенту(слою) св-во shouldRasterize в true. При этом так же добавьте rasterizationScale = UIScreen.main.scale
    Ответ написан
    8 комментариев
  • Каким образом можно запросить только заголовки URL?

    doublench21
    @doublench21 Куратор тега Swift
    Ну вообще говоря для того, что бы получить лишь заголовки ответа сервера, достаточно сказать самому серверу, что бы он отдал только заголовки. Для этой цели существует Head запрос(не Post с Get едины)

    И да, Alamofire такие очевидные штуки тоже умеет.
    public enum HTTPMethod: String {
        case options = "OPTIONS"
        case get     = "GET"
        case head    = "HEAD"
        case post    = "POST"
        case put     = "PUT"
        case patch   = "PATCH"
        case delete  = "DELETE"
        case trace   = "TRACE"
        case connect = "CONNECT"
    }
    Ответ написан
    Комментировать
  • Почему view controller неактивен после перехода на него с другого view controller через navigation controller, используя custom animation transition?

    doublench21
    @doublench21 Куратор тега Swift
    Быть может в completion вашей анимации надо все же сказать системе, что анимация завершена.

    transitionContext.completeTransition(true)
    Ответ написан
    1 комментарий
  • Может ли использовать iOS SDK для какого-нибудь сайта во всех типах приложений на Swift?

    doublench21
    @doublench21 Куратор тега Swift
    Какой ещё к чёрту сайт?! Если написано iOS - это означает только одно... использование внутри приложения под iOS.

    Сайты пишут на HTML/CSS/JS, причём тут Swift и iOS не ясно. Бэкенд на Swift не имеет никакого отношения к визуальной составляющей.
    Ответ написан
  • Как сделать кастомизированную панель навигации в приложении для iOS?

    doublench21
    @doublench21 Куратор тега Swift
    Возможно всё!

    В гугле куче статей на эту тему. Сделайте свои подклассы UITabBarController/UITabBar/UITabBarItem. Переопределите нужные методы, добавьте нужные сабвью.

    А что касается переходов, это достаточно большая тема, но опять таки, она хорошо расписана. У делегата UITabBarControllerDelegate, есть два метода отвечающие за переход:
    https://developer.apple.com/documentation/uikit/ui...
    https://developer.apple.com/documentation/uikit/ui...

    Каждый из этих методов должен вернуть соответсвующий аниматор. Как создавать такие? Куча статей.
    Ответ написан
    Комментировать
  • Изучение iOS-разработки и дизайна мобильных приложений. Как быть?

    doublench21
    @doublench21 Куратор тега Swift
    Привет из Ташкента. Дизайн мобильных приложений или таки разработка мобильных приложений? Разные ипостаси.

    Ну что тебе сказать, друг мой. Если ты уверен, что хотя бы через пол года у тебя появится мак, то можешь смело начинать. Для того, что бы погрузить с полной уверенностью в разработку, абсолютно не важно в какую именно на данном этапе, тебе неплохо было бы начать с изучения языка.

    Просто пойми, что для новичка не имеет никакого смысла бояться выбрать что-то неправильное или потерять время. Ты сейчас изучаешь языки, конструкции, подходы. В конце концов формируешь своё мышление.

    Если ответ на первый вопрос утвердительный и макбук таки будет, то смело скачивай книжку по Swift от Apple. Устанавливай Swift на Linux и начинай приключение в мир нового для тебя языка.
    Ответ написан
  • Как решить проблему с авто размером collection view?

    doublench21
    @doublench21 Куратор тега Swift
    Потому-что, так никто не делает, ну кроме Вас. Для работы auto layout нужно построить цепочку ограничений сверху вниз.

    Что бы не спрашивать что у Вас есть, а что нет, сразу напишу полный список./

    1) Прописать у layout коллекции:
    // ...
    layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    layout.itemSize = UICollectionViewFlowLayout.automaticSize


    2) Соврал, нужно уточнение.

    pronunciationLabel - Сколько строк должен занимать? (Только одну?) Увеличивается только в ширину?
    wordLabel - Тоже, что и pronunciationLabel? Или увеличивается в высоту?

    3) Этот бред больше никогда не нужно делать.

    contentView.translatesAutoresizingMaskIntoConstraints = false
       
            NSLayoutConstraint.activate([
                contentView.leftAnchor.constraint(equalTo: leftAnchor),
                contentView.rightAnchor.constraint(equalTo: rightAnchor),
                contentView.topAnchor.constraint(equalTo: topAnchor),
                contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
            ])


    Уточнение получены. Продолжим.

    Для начала уточним, как нам добиться динамического размера вьюхи в ширину и высоту. Приведу простой и наглядный пример. Создам вьюхи и помещу туда два лейбла. Пишу всё исключительно кодом, т.к. не использую сториборд, но думаю свести проблем не составит. И так:

    5cab4bce2a9a6308684642.png
    Код текстом
    let v = UIView(frame: .zero)
    
        let l1 = UILabel(frame: .zero)
            l1.translatesAutoresizingMaskIntoConstraints = false
            l1.font = .systemFont(ofSize: 27.0)
            l1.text = "asdlas"
    
        v.addSubview(l1)
        NSLayoutConstraint.activate([
          l1.topAnchor.constraint(equalTo: v.topAnchor),
          l1.leadingAnchor.constraint(equalTo: v.leadingAnchor),
        ])
        l1.setContentHuggingPriority(.required, for: .vertical)
        l1.setContentCompressionResistancePriority(.required, for: .vertical)
    
        let trailing = l1.trailingAnchor.constraint(equalTo: v.trailingAnchor)
            trailing.priority = .defaultHigh
        NSLayoutConstraint.activate([
          trailing
        ])
    
        let l2 = UILabel(frame: .zero)
            l2.translatesAutoresizingMaskIntoConstraints = false
            l2.font = .systemFont(ofSize: 17.0)
            l2.text = ""
    
        v.addSubview(l2)
        NSLayoutConstraint.activate([
          l2.topAnchor.constraint(equalTo: l1.bottomAnchor),
          l2.leadingAnchor.constraint(equalTo: v.leadingAnchor),
        ])
        l2.setContentHuggingPriority(.required, for: .vertical)
        l2.setContentCompressionResistancePriority(.required, for: .vertical)
    
        let trailing_ = l2.trailingAnchor.constraint(equalTo: v.trailingAnchor)
            trailing_.priority = .defaultHigh
        let bottom = l2.bottomAnchor.constraint(equalTo: v.bottomAnchor)
            bottom.priority = .defaultHigh
        NSLayoutConstraint.activate([
          trailing_,
          bottom
        ])
    
    
        let s = v.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
    
        view.addSubview(v)
        v.frame.size = s
        v.center = view.center
        v.layer.borderColor = UIColor.red.cgColor
        v.layer.borderWidth = 1.0


    В принципе мы делаем обычные констрениты, задавая топ и лидинг. Примечателен тут другой момент, а именно трейлинг. Перед его заданием, мы обязательно установили св-ва сопротивление сжатию и сопротивления расстяжения обоим лейблам, давая тем самым системе понять, что хотим видеть лейбл ровно такой ширины, какой он есть на самом деле. После мы делаем констрейнт трейлинг и указываем ему приоритет немного ниже чем 1000(required). Кстати, быть может в сториборде приоритет снижать не придется. Проверьте. (Кажись система делаем это за вас)

    С шириной всё готово. Смотрим на костреинты для высоты. Тут вполне обычно, начинаем с топ. После нижний лейбл крепится к боттом первого. И снова небольшая магия. Последний нижний констрейнт снова с пониженным приоритетом. Кстати, быть может в сториборде приоритет снижать не придется. Проверьте. (Кажись система делаем это за вас)

    Если быть точнее снижая приоритет(по крайне мере если задавать их кодом) такой подход даёт понять системе, что высоту и ширину он прямиком и полностью должен расчитывать исходя из высоты и ширины его сабвьюх, а не назначить свои значения.

    Поглядим на примеры:
    Первая имеет меньший размер шрифта, но больше символов.
    HC2w483.png
    Первая имеет больший размер шрифта, но больше символов.
    5MhP2nj.png
    Первая имеет меньший размер шрифта, но меньше символов.
    V00A7Qc.png
    Первая имеет больший размер шрифта, но меньше символов.
    zXH8glH.png

    Как видимо размер вьюхи система посчитала вольностью верным. То есть мы динамически научились задавать высоту и ширину ячейки.

    Поехали дальше.

    Но для ячеек есть ещё одна небольшая проблема. Система лайаута коллекции имеет все нужные констреинты, но она ничего не знает о данных в этих ячеек. А ведь именно они задают ширину ячеейки. Для этого нам нужно в какой-то момент времени немного подсказать системе.

    Воспользуемся методом:
    5cab50033a6e2506454233.png
    Код текстом
    func preferredLayoutAttributesFitting(
        _ layoutAttributes: UICollectionViewLayoutAttributes
      ) -> UICollectionViewLayoutAttributes
      {
        let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
    
        let fittingSize = UIView.layoutFittingCompressedSize
    
        layoutAttributes.frame.size = systemLayoutSizeFitting(
          fittingSize,
          withHorizontalFittingPriority: .fittingSizeLevel,
          verticalFittingPriority: .fittingSizeLevel
        )
    
        return layoutAttributes
      }


    Так как все констреинты у нас настроены, нам осталась попросить систему подсчитать нужные размеры и передать новые размеры системе лайаута коллекции. В этот момент кстати, все данные ячейка уже получила. (Текст для лейблов)

    И малая ремарка, возможно в метод preferredLayoutAttributesFitting после вызова super. надо будет добавить вызов метода layoutIfNeeded(). А может и не нужно.

    Итого:
    Добавляем то, что в пункте 1.
    Устанавливаем нужные констреинты.
    Удаляем ваш код в пункте 3.
    Добавляем метод preferredLayoutAttributesFitting
    Ответ написан
    3 комментария
  • Как исправить ошибку (optional swift)?

    doublench21
    @doublench21 Куратор тега Swift
    Для начала нужно научится оформлять свой вопрос, а уже после посмотреть на такой забавный момент. Если вы зашли в блок if, то вы уже уверены в том, что ваша опциональная строка содержит значение. Вот только с чего вы взяли, что ваши кракозябра в строке сведётся к числу? Очевидно не сведётся и она и не сводится. Выражение Int(number2!) возвращает nil

    Плюс надо бы вам уже начинать читать про Опциональный биндинг.
    Ответ написан
    4 комментария
  • Не срабатывает dataSource?

    doublench21
    @doublench21 Куратор тега Swift
    Доброго времени суток. Вы меня конечно извините, но вряд ли найдётся герой, который осилит эти шматки кода. Могу лишь дать небольшой совет, если ваша таблица или коллекция не обновляется при вызове перезагрузки, смотреть нужно в соответствующих методах cell for item at.
    Ответ написан
    2 комментария
  • Как преобразовать структуру Firebase в объект swift?

    doublench21
    @doublench21 Куратор тега Swift
    Что из перечисленного списка Вас смущает и Вы не умеете это делать: ?
    • Получить JSON данные через URLSession/Alamofire/FirebaseSDK
    • Использование Codable для преобразования JSON в класс/структуру
    Ответ написан
    2 комментария
  • Как решить ошибку в коле Swift с использованием CocoaPods, Firebase?

    doublench21
    @doublench21 Куратор тега Swift
    Так может потому, что НЕ нужно определять типы входных параметров, если они уже определены в самом методе createUser. Более того, вы определили типы этих параметров не верно.

    5c7d6b050fce6838464701.png
    Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
      // ...
    }


    https://firebase.google.com/docs/auth/ios/start
    Ответ написан
    Комментировать