Prática 1: Melhorias de Usabilidade

Assista e acompanhe a prática em sala dos exercícios "Melhorias de Usabilidade". Execute as instruções conforme a apresentação.

Exercício 1. Abstraindo o View Controller de Atividades

1.1. Criar a Classe ActivitiesViewController herdando de SwiftyIOTableViewController no grupo ViewControllers

1.2. Substitua a Implementaçnao da classe ActivitiesViewController

Substitua a Implementaçnao da classe ActivitiesViewController com o código abaixo:

class ActivitiesViewController: CoreDataTableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        // Carrega as atividades pelo Fetched Results Controller
        self.fetchedResultsController = TraqtDataContext.sharedInstance.activities.getFetchedResultsController(sortDescriptors: NSSortDescriptor(key: "name", ascending: true))
    }

}

1.3. Inclua uma constante para o Identitiy da Célula de protótipo

No início do arquivo ActivitiesViewController.swift inclua o código abaixo:

/// Identifier para a célula de atividade
let kActivityCell = "ActivityCell"

1.4. Inclua uma seção de métodos de Apoio

Inclua uma seção de métodos de Apoio usando o código da listagem abaixo:

//
// MARK: - Métodos de Apoio

func getActivityAtIndexPath(indexPath: NSIndexPath) -> Activity {
    let activity = self.fetchedResultsController?.objectAtIndexPath(indexPath) as Activity
    return activity
}

1.5. Inclua uma seção de Overridables

Inclua o código abaixo para criar uma seção de overridables:

//
// MARK: - Overridables

/**
    Chamado depois que o usuário seleciona uma atividade na listagem.
    Esse método deve ser sobrescrito pela classe concreta para que ele implemente a funcionalidade desejada quando o usuário selecionar uma atividade.
    :param: activity Atividade selecionada.
 */
func selectedActivity(activity: Activity) {
    //
    // Esse método deve ser sobrescrito pela classe concreta para incluir a funcionalidade desejada quando o usuário selecionar uma atividade
}

1.6. Inclua os Métodos do Datasource e do Delegate do Table View

Inclua os Métodos do Datasource e do Delegate do Table View Controller conforme o código da listagem abaixo:

//
// MARK: - Table View Data Source/Delegate

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    // Tenta obter a célula, se não existir cria uma nova instância
    var cell = tableView.dequeueReusableCellWithIdentifier(kActivityCell) as UITableViewCell!
    if cell == nil {
        cell = UITableViewCell(style: .Default, reuseIdentifier: kActivityCell)
    }

    // Configura a célula
    let activity = self.getActivityAtIndexPath(indexPath)
    cell.textLabel?.text = activity.name

    return cell
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    // Executa a ação especificada pelo
    let activity = self.getActivityAtIndexPath(indexPath)
    self.selectedActivity(activity)
}

1.7. Criar a classe ConfigActivitiesViewController herdando de ActivitiesViewController no grupo ViewControllers

1.8. Substituir a implementação da classe

Substitua a implementação padrão da classe pelo código abaixo:

class ConfigActivitiesViewController: ActivitiesViewController {

    //
    // MARK: - Navegação

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "EditActivity" {
            let destVC = segue.destinationViewController as! ActivityFormViewController
            let indexPath = self.tableView.indexPathForCell(sender as! UITableViewCell)!
            let activity = self.getActivityAtIndexPath(indexPath)
            destVC.activity = activity
        }
    }

}

1.9. Localize o View Controller de Atividades e Atualize a sua referência de classe para ConfigActivitiesViewController


Exercício 2. Melhorando a Tela de Início

2.1. Abra o arquivo Main.Storyboard e desenhe um Table View Controller próximo ao View Controller de Início

2.2. Insira o novo View Controller em um Navigation Controller

2.3. Criar a classe SelectActivityViewController herdando de ActivitiesViewController no grupo ViewControllers

2.4. Inclua um protocolo para o delegate do View Controller

Inclua o código abaixo para declarar um protocolo de delegate do View Controller:

protocol SelectActivityControllerDelegate {

    /**
        Chamado quando o usuário seleciona uma atividade.

        :param: controller O View Controller que disparou a ação.
        :param: activity A atividade selecionada pelo usuário.
     */
    func selectActivityController(controller: SelectActivityViewController, didSelectActivity activity: Activity)

}

2.5. Substitua a implementação da classe

Substitua a implementação padrão da classe pelo código abaixo:

SelectActivityViewController: ActivitiesViewController {

    //
    // MARK: - Properties

    var delegate: SelectActivityControllerDelegate?

    //
    // MARK: - Overrides

    override func selectedActivity(activity: Activity) {
        self.delegate?.selectActivityController(self, didSelectActivity: activity)
    }

}

2.6. Localize o View Controller de Atividades e Atualize a sua referência de classe para SelectActivityViewController

2.7. Atualize o título do View Controller para Selecionar a Atividade

2.8. Exclua o Picker View de seleção de atividade

2.9. Inclua a Constraint Vertical Align in Container no Controle de Início da Sessão

2.10. Desenhe um UIButton para seleção da atividade

Desenhe um Button para seleção da atividade abaixo do botão de início da sessão, com o frame (16, 407, 568, 80), e inclua as constraints:

  • Leading Space to: Superview - Equals: 16
  • Trailing Space to: Superview - Equals: 16
  • Top Space to: Long Press button - Equals: 8
  • Height Equals: 80

Configure as propriedades do botão para:

  • Text: Escolha sua Atividade
  • Font: System 20.0

2.11. Crie um Segue para a tela de seleção

Crie um Segue a partir do botão criado anteriormente com o tipo Present Modally e com o Identifier SelectActivity.

2.12. Crie um Outlet para o botão

Crie um Outlet para o botão conforme o código abaixo:

@IBOutlet weak var selectActivityButton: UIButton!

2.13. Remova o Outlet do Picker View

Remova o Outlet do Picker View conforme o código abaixo:

@IBOutlet weak var activitiesPickerView: UIPickerView!

2.14. Atualize as propriedades

Atualize as propriedades da classe conforme o código abaixo:

lazy var activities: [Activity] = TraqtDataContext.sharedInstance.activities.getAll()
lazy var selectedActivity: Activity? = self.activities.first

2.15. Remova o método viewWillAppear

Remova o método viewWillAppear conforme o código abaixo:


override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    // Mantém o picker view sincronziado com o banco de dados do Core Data sempre que ela é exibida
    self.activities = TraqtDataContext.sharedInstance.activities.getAll()
    self.activitiesPickerView.reloadAllComponents()
}

2.16. Remova o Picker View Delegate e Data Source

Remova os métodos do UIPickerViewDelegate e UIPickerViewDataSource conforme a listagem abaixo:


//
// MARK: - UIPickerViewDataSource

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return self.activities.count
}

//
// MARK: - UIPickerViewDelegate

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    self.selectedActivity = self.activities[row]
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    return self.activities[row].name
}

2.17. Atualize a declaração da classe

Atualize a declaração da classe NewSessionViewController conforme o código abaixo:

class NewSessionViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
class NewSessionViewController: UIViewController, SelectActivityControllerDelegate {

2.18. Inclua a implementação do protocolo do Delegate

Inclua a implementação do protocolo do delegasse no final da classe conforme o código abaixo:

//
// MARK: - SelectActivityControllerDelegate

func selectActivityController(controller: SelectActivityViewController, didSelectActivity activity: Activity) {
    // Carrega a atividade e atualiza o título do botão
    self.selectedActivity = activity
    self.selectActivityButton.setTitle(activity.name, forState: .Normal)

    // Fecha o View Controller de Seleção
    self.dismissViewControllerAnimated(true, completion: nil)
}

2.19. Atualize o método prepareForSegue:sender:

Atualize o método prepareForSegue:sender: conforme o código abaixo:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "StartSession" {
        let destVC = segue.destinationViewController as SessionViewController
        destVC.activity = self.selectedActivity
    }<strong> else if segue.identifier == "SelectActivity" {
        let nvc = segue.destinationViewController as! UINavigationController
        let destVC = nvc.topViewController as! SelectActivityViewController
        destVC.delegate = self
    }</strong>
}

2.20. Atualize o Action Method do botão iniciar sessão

Atualize o código do Action Method do botão iniciar sessão conforme o código abaixo:

@IBAction func startSession(sender: LongPressButton) {
    if let a = self.selectedActivity {
        self.performSegueWithIdentifier("StartSession", sender: self)
    } else {
        <strong>self.selectActivityButton.setTitle("Oi! Antes escolha sua atividade", forState: .Normal)</strong>

        <del>// Se não tiver selecionado nenhuma atividade apresenta uma mensagem
        let alertC = UIAlertController(title: "Traqt", message: "Selecione uma atividade para iniciar.", preferredStyle: UIAlertControllerStyle.Alert)
        alertC.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
        self.presentViewController(alertC, animated: true, completion: nil)</del>
    }

    sender.pressHandled()
}

Exercício 3. Incluindo a barra de título na tela inicial

3.1. Criar um grupo Assets no projeto abaixo do grupo Traqt

3.2. Baixar o arquivo Fonts.zip descomprimir e copiar para dentro do grupo Assets

Baixe o arquivo com as Fonte personalizada do Traqt a partir deste link e e copie para dentro do grupo Assets.

3.3. Incluir a referência para a fonte no arquivo de configuração

Abra o arquivo Info.plist, adicione ou localize a chave Fonts provided by the application, adicione um novo item com o nome Tecno-Trance.otf.

3.4. Desenhar o Label de Título no View Controller de Início

Desenhe um novo Label no View Controller de Início e ajuste o frame para (0, 0, 600, 120), e inclua as constriants:

  • Height Equals: 120
  • Leading Space to: Superview - Equals 0
  • Top Space to: Superview - Equals 0
  • Trailing Space to: Superview - Equals 0

Ajuste as propriedades do Label para:

  • Text: Traqt
  • Color: White Color
  • Font: TecnoTrance-Regular 82.0
  • Alignment: Center
  • Background: #357EAA

Exercício 4. Incluindo a Busca na Listagem de Atividades

4.1. Abra o arquivo ActivitiesViewController e atualize suas propriedades

Atualize as propriedades do arquivo conforme o código abaixo:

//
// MARK: - Properties

var filteredActivities = [Activity]()

4.2. Atualize o método tableView:numberOfRowsInSection

Atualize o método tableView:numberOfRowsInSection conforme a listagem abaixo:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if tableView == self.searchDisplayController!.searchResultsTableView {
        return self.filteredActivities.count
    } else {
        return super.tableView(tableView, numberOfRowsInSection: section)
    }
}

4.3. Atualize o método tableView:cellForRowAtIndexPath:

Atualize o método tableView:cellForRowAtIndexPath: conforme a listagem abaixo:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    // Tenta obter a célula, se não existir cria uma nova instância
    var cell = tableView.dequeueReusableCellWithIdentifier(kActivityCell)
    if cell == nil {
        cell = UITableViewCell(style: .Default, reuseIdentifier: kActivityCell)
    }

    // Configura a célula
    <del>let activity = self.getActivityAtIndexPath(indexPath)</del>
    <strong>var activity: Activity
    if tableView == self.searchDisplayController!.searchResultsTableView {
        activity = self.filteredActivities[indexPath.row]
    } else {
        activity = self.getActivityAtIndexPath(indexPath)
    }</strong>

    cell.textLabel.text = activity.name

    return cell
}

4.4. Atualize o método tableView:didSelectRowAtIndexPath:

Atualize o método tableView:didSelectRowAtIndexPath: conforme a listagem abaixo:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    // Executa a ação especificada pelo
    var activity: Activity
    if tableView == self.searchDisplayController!.searchResultsTableView {
        activity = self.filteredActivities[indexPath.row]
    } else {
        activity = self.getActivityAtIndexPath(indexPath)
    }
    self.selectedActivity(activity)
}

4.5. Adicione o Método de Apoio

Adicione o método da listagem abaixo na seção de métodos de apoio:

func filterContentForSearchText(searchText: String?) {
    if let searchText = searchText {
        let predicate = NSPredicate(format: "name CONTAINS[cd] %@", searchText)
        self.filteredActivities = TraqtDataContext.sharedInstance.activities.filter(predicate, sortDescriptors: [ NSSortDescriptor(key: "name", ascending: true) ])!
    }
}

4.6. Atualize a declaração da classe

Atualize a declaração da classe ActivitiesViewController conforme a listagem abaixo, substitua:

class ActivitiesViewController: SwiftyIOTableViewController {

Por:

class ActivitiesViewController: SwiftyIOTableViewController, UISearchBarDelegate, UISearchDisplayDelegate {

4.7. Inclui os métodos dos protocolos acrescentando

Inclua os métodos dos protocolos acrescentando na declaração da classe, conforme a listagem abaixo:

//
// MARK: - UISearchBarDelegate

func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
    self.filterContentForSearchText(searchString)
    return true
}

func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchScope searchOption: Int) -> Bool {
    self.filterContentForSearchText(self.searchDisplayController?.searchBar.text)
    return true
}

4.8. Desenhe um Search Bar and Search Display dentro do Table View de Atividades e de Seleção de Atividades e atualize suas propriedades

Desenhe um componente Search Bar and Search Display dentro do Table View de Atividades e dentro do Table View de Seleção de Atividades. Selecione e configure as propriedades conforme abaixo:

  • Placeholder: Procurar Atividade