Body zdarma Java týden
Využij podzimních slev a získej od nás až 40 % bodů zdarma! Více zde
Pouze tento týden sleva až 80 % na Java e-learning!

Diskuze: Pomoc při optimalizaci UITableView a dotaz na velikost aplikace

Aktivity (2)
Avatar
CoffeeMAN
Člen
Avatar
CoffeeMAN:10. dubna 19:47

Ahoj,

v rámci vytvoření prvního projektu a aktuálního testování jsem narazil na celkem vážný problém při procházení záznamů v UITableView (stav se objevuje při cca 10 záznamů), přesněji "neplynulý/sekavý" průchod v seznamu - zdá se mi, že se nestačí "přednačítat" informace do cell. Níže uvádím UITableView a UITableViewCell.

A další můj dotaz spočívá v poměrně velké velikosti aplikace - přesněji: 81MB aplikace, dokumenty a data 4kB Při čisté instalaci (pomocí Xcode: 10.2, Projekt je již zmigrován na Swift5 (Pro moje ego: bez jediné chyby a upozornění) 8-) , Použité pods: Realm, Lottie, BEMCheckBox, IQKeyboardMana­gerSwift), Někde jsem četl, že je to z důvodu, že Xcode neprovádí komprimaci aplikace a rozlišení konkrétního modelu iPhone při "otestování", nicméně ta velikost se mi moc nezdá, jelikož se nejedná o jak velký projekt. Pokud by to pomohlo, tak bych někam hodil svůj projekt a "pochlubil se". :)

Zkusil jsem:

//UITableView:

import UIKit
import RealmSwift
import Lottie

class ListPlantVC: UIViewController {
    // MARK: Empty - label and animation plant.
    @IBOutlet weak var emptyListStackView: UIStackView!
    @IBOutlet weak var emptyListAnimationView: AnimationView!
    @IBOutlet weak var emptyListLabelOutlet: UILabel!
    // MARK: TableView.
    @IBOutlet private weak var tableView: UITableView!
    @IBAction private func unwindToMain(_ sender: UIStoryboardSegue) {} // MARK: UnwindMain - Segue for back to PlantList.
    // MARK: Var and Let.
    public var plantsFromDB: Results<Plant>?
    /************************************************/
    override func viewDidLoad() {
        super.viewDidLoad()
        DBManager.printRealmURL()
        configureUI()
        LottieManager.animationLottie(lottieView: emptyListAnimationView, name: "BG_Plant", loopAnimation: true, play: true)
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        getDataFromDB()
        tableView.reloadData()
        showWelcomeDialog()
    }
    /************************************************/
    // MARK:  Set UI for VC.
    private func configureUI() {
        // MARK: Configure UITableView
        tableView.delegate          = self
        tableView.dataSource        = self
        tableView.tableFooterView   = UIView() // MARK: Delete empty Cell.
        // MARK: Button for add new plant.
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Přidat", style: .plain, target: self, action: #selector(addNewPlant))
    }

    // MARK: Get all objects from Realm database with sorted "Date create" and asceding - false.
    private func getDataFromDB() {
        DispatchQueue.global(qos: .userInitiated).sync { [weak self] in
            let realm: Realm = try! Realm()
            self?.plantsFromDB = realm.objects(Plant.self).sorted(byKeyPath: "dateCreate", ascending: false)
        }
    }

    // MARK: Check state about show dialog and call Alert.
    private func showWelcomeDialog() {
        let actualValueForShowTips: Int = {
            let actualValueFromUserDefaults: Int = UserDefaults.standard.integer(forKey: userDefualtsForShowTip)
                return actualValueFromUserDefaults
        }()
        if actualValueForShowTips == 1 { AlertManager.showPrimeTips(on: self) }
    }

    // MARK: Func for add new item (Segue to CreateOrEditPlantVC).
    @objc private func addNewPlant() {
        FeedbackManager.selectFeedback()
        performSegue(withIdentifier: segueListToCreate, sender: nil)
    }

    // MARK: Prepare data for detail plant (segue to MainPageVC).
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == segueListToDetail {
            let listToMainPageVCDestination = segue.destination as! MainPageVC
            listToMainPageVCDestination.plant = sender as? Plant
        }
    }

    // MARK: Show or hide label and animation about empty list plants.
    private func showOrHideEmptyListInfo(with show: Bool) {
        if show == true {
            emptyListStackView.isHidden = false
        }else {
            emptyListStackView.isHidden = true
        }

    }
}

/**************************** Extension ****************************/
extension ListPlantVC: UITableViewDelegate, UITableViewDataSource {
    // MARK: Func numberOfRowsInSection.
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // MARK: Check count plant and show/hide label about emty list.
        if plantsFromDB?.count ?? 0 < 1 {
            showOrHideEmptyListInfo(with: true)
        }else {
            showOrHideEmptyListInfo(with: false)
        }
        // MARK: Return count for this func.
        return plantsFromDB?.count ?? 0
    }

    // MARK: Func cellForRowAt indexPath
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let plant = plantsFromDB?[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "plantCell", for: indexPath) as! TMPlantCell
        cell.setPlants(actualPlant: plant!)
        return cell
    }

    // MARK: Func didSelectRowAt
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let plant = plantsFromDB?[indexPath.row]
        FeedbackManager.selectFeedback()
        performSegue(withIdentifier: segueListToDetail, sender: plant)
    }

    // MARK: Func canEditRowAt (for set editing mode)
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    // MARK: Func for edit title for delete.
    func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
        return "Smazat"
    }

    // MARK: Func for deleting plant with action sheet alert.
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // MARK: ActionTitle
            let actionSheet = UIAlertController(title: "Opravdu si přejete smazat záznam?", message: "Jedná se o nevratné odstranění.", preferredStyle: .actionSheet)
            // MARK: Action Button - Alert Action
            let cancel = UIAlertAction(title: "Storno", style: .cancel, handler: nil)
            let delete = UIAlertAction(title: "Smazat", style: .destructive) { action in
                if let deletePlant = self.plantsFromDB?[indexPath.row] {
                    DBManager.sharedInstanceRealm.deleteObject(object: deletePlant)
                }
                FeedbackManager.succeseedback()
                tableView.deleteRows(at: [indexPath], with: .fade)
            }
            actionSheet.addAction(delete)
            actionSheet.addAction(cancel)
            present(actionSheet, animated: true, completion: nil)
        }
    }
}



//UITableViewCell:

import UIKit
import Lottie

class TMPlantCell: UITableViewCell {
    @IBOutlet weak private var namePlantLabel: UILabel!
    @IBOutlet weak private var growRoomLabel: UILabel!
    @IBOutlet weak private var pointLabelCell: UILabel!
    @IBOutlet weak private var trophyView: AnimationView!
    @IBOutlet weak private var isMonday: TMLabelDay!
    @IBOutlet weak private var isTuesday: TMLabelDay!
    @IBOutlet weak private var isWednesday: TMLabelDay!
    @IBOutlet weak private var isThursday: TMLabelDay!
    @IBOutlet weak private var isFriday: TMLabelDay!
    @IBOutlet weak private var isSaturday: TMLabelDay!
    @IBOutlet weak private var isSunday: TMLabelDay!
    @IBOutlet weak private var imagePlant: TMImageViewPlant!
    @IBOutlet weak private var wateredButtonOutlet: UIButton!
    @IBOutlet weak private var statePlantButtonOutlet: UIButton!
    // MARK: Private variable for check watered and show or hide button with disabled UserInteraction.
    private var wateredPlant: Bool = false

    // MARK: Func for set UI Actual Cell.
    func setPlants(actualPlant: Plant) {
        LottieManager.animationLottie(lottieView: trophyView, name: "trophy", loopAnimation: false, play: true)

        // MARK: Set value to UI.
        self.namePlantLabel.text = actualPlant.plantName
        self.growRoomLabel.text = (actualPlant.growRoom).isEmpty ? "" : "Umístění: \(actualPlant.growRoom)"
        self.pointLabelCell.text = "\(actualPlant.point)"
        self.isMonday.isOn = actualPlant.isMonday
        self.isTuesday.isOn = actualPlant.isTuesday
        self.isWednesday.isOn = actualPlant.isWednesday
        self.isThursday.isOn = actualPlant.isThursday
        self.isFriday.isOn = actualPlant.isFriday
        self.isSaturday.isOn = actualPlant.isSaturday
        self.isSunday.isOn = actualPlant.isSunday
        self.imagePlant.showOrHideImage(actualPlant.imagePlant)
        self.imagePlant.animationState = false
        self.wateredPlant = actualPlant.isCurrentWatered

        // MARK: Check and show or hide info about care plant.
        if actualPlant.isCheckedInterval == true {
            self.statePlantButtonOutlet.isHighlighted = false
            self.statePlantButtonOutlet.isEnabled = true
        }else {
            self.statePlantButtonOutlet.isHighlighted = true
            self.statePlantButtonOutlet.isEnabled = false
        }
        // MARK: Show or hide button for watered.
        if actualPlant.checkDayForWatered() == true && actualPlant.isCurrentWatered == false {
            self.wateredButtonOutlet.isHighlighted = false
            self.wateredButtonOutlet.isEnabled = true
        }else {
            self.wateredButtonOutlet.isHighlighted = true
            self.wateredButtonOutlet.isEnabled = false
        }
    }
}

Chci docílit: Optimalizace a upřesnění důvodu nárostu velikosti aplikace.

Odpovědět 10. dubna 19:47
Není čas, ztrácet čas.
Avatar
Filip Němeček
Redaktor
Avatar
Filip Němeček:10. dubna 20:30

Testuješ na reálném zařízení? Table view je hodně dobře optimalizovaný a měl by out-of-the-box zvládnout stovky řádků v poho. Velikost app je Realm no, bohužel. Moje app s Realm má po instalaci 65 MB. Ale Core Data určitě pro menší velikost dávat nebudu :D

Jestli se to seká na zařízení, tak první věc, dal bych pryč dočasně Lottie.

Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět 10. dubna 20:30
Avatar
CoffeeMAN
Člen
Avatar
Odpovídá na Filip Němeček
CoffeeMAN:10. dubna 22:00

Ahoj Filipe, ano testují to na fyzickém zařízení (iphone 7+). Klasicky se ti povedlo odhalit mě potíže. Dělá to Lottie, jakmile jsem zakomentoval tu statickou metodu LottieManager, tak se to neuvěřitelně zrychlilo. :-? To zpomalení se pravděpodobně objevilo, když jsem aktualizoval Lottie, které by mělo být celé napsané ve Swift 4. :-X ;-(

Nahoru Odpovědět  +1 10. dubna 22:00
Není čas, ztrácet čas.
Avatar
Filip Němeček
Redaktor
Avatar
Odpovídá na CoffeeMAN
Filip Němeček:10. dubna 22:06

Tak super, že vyřešeno. To Lottie jsi použil na nějakou komplexní animace? Animovat v iOS je celkem jednoduché, klidně napiš, jaký si představuješ výsledek a mohu tě zkusit nasměrovat :-)

 
Nahoru Odpovědět 10. dubna 22:06
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
CoffeeMAN
Člen
Avatar
Odpovídá na Filip Němeček
CoffeeMAN:11. dubna 18:48

Nejedná se o velkou animaci, jen jsem chtěl rozpohybovat aspoň jeden prvek v Uitableview - kde budou vyzobrazeny body (https://lottiefiles.com/677-trophy). Jelikož data jsou v “json” čekal jsem menší zátěž vůči výkonu, tak nakonec je to naopak. 8-|

Nahoru Odpovědět 11. dubna 18:48
Není čas, ztrácet čas.
Avatar
Filip Němeček
Redaktor
Avatar
Filip Němeček:11. dubna 18:58

No souhlas, že tohle asi není animace, kterou by se někomu chtělo vytvářet "from scratch" :D Pořád to ale není nic komplexního, co by dnešní iPhone neměl zvládnout v pohodě. Neumí Lottie nějak tu animaci v předstihu načíst z JSON, ať ji nemusíš před puštěním načítat ze souboru?

 
Nahoru Odpovědět 11. dubna 18:58
Avatar
CoffeeMAN
Člen
Avatar
Odpovídá na Filip Němeček
CoffeeMAN:12. dubna 7:12

Popravdě moc jsem se s tím nezaobýral, jelikož to fungovalo před aktulizací Lottie na akt. verzi, kdy došlo k přepsání frameworku do swiftu. Něco jsem našel, ale nejsem z toho moc moudrý: AnimationCache­Provider (AnimationCache­Provider is a protocol that describes an Animation Cache. Animation Cache is used when loading Animation models. Using an Animation Cache can increase performance when loading an animation multiple times.
Lottie comes with a prebuilt LRU Animation Cache). Takže to vypadá, že použiji základní návrh a to Kiss "Keep It Simple, stupid!" - takže se nyní vykašlu na animaci a vložím tam statický obrázek. 8-)

Nahoru Odpovědět 12. dubna 7:12
Není čas, ztrácet čas.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 7 zpráv z 7.