Trilhas
/
Swift
Swift
/
Exercícios
/
Sistema de janelas
Sistema de janelas

Sistema de janelas

Exercício de aprendizagem

Introdução

Structs e classes

Structs e classes são dois dos principais blocos de construção da programação em Swift. Ambos são formas de agrupar dados e funções relacionados em unidades de funcionalidade autocontidas. E quando você define um struct ou uma classe, você está definindo um novo tipo para usar dentro do Swift, assim como usou os tipos com os quais já trabalhou, como Int e String.

Existem muitas semelhanças entre structs e classes em Swift. Entre outras semelhanças, ambos conseguem armazenar valores em propriedades e oferecer funcionalidade por meio do uso de métodos.

Cada um deles oferece algumas funcionalidades adicionais, que estão fora do escopo deste exercício.

Definindo structs e classes

Tanto structs quanto classes são definidos mais ou menos da mesma forma. Eles começam com a palavra-chave apropriada, seguida do nome do tipo que está sendo definido; em seguida vem o corpo do struct/classe, colocado entre chaves.

O corpo pode consistir em propriedades armazenadas, que são definidas e se comportam exatamente como constantes ou variáveis comuns.

struct CharacterStats {
  var health = 0.0
  var speed = 0
  var strength = 0
}

class GameCharacter {
  var stats = CharacterStats()
  var characterClass: String?
  var name: String?
  var active = false
  let id = makeRandomID()
}

Instâncias

Como observado acima, definir um struct ou uma classe é apenas definir um novo tipo. É só o modelo de como serão os valores desse tipo, mas não cria de fato nenhum valor desse tipo para você trabalhar.

Para criar uma instância desse tipo, você precisa escrever o nome do tipo seguido de um par de parênteses.

let someStats = CharacterStats()
let someCharacter = GameCharacter()

Isso criará valores desses tipos, com as propriedades preenchidas com os valores padrão fornecidos na definição. Repare que, em casos opcionais como a propriedade name de GameCharacter, a menos que um valor seja fornecido, a propriedade terá o valor padrão nil, exatamente como ao definir tipos opcionais comuns em que um valor não é fornecido de imediato.

Com structs, o Swift fornece automaticamente algo chamado inicializador membro a membro, em que valores para as propriedades do struct podem ser fornecidos dentro dos parênteses e substituem os valores padrão da definição.

let differentStats = CharacterStats(health: 100.0, speed: 6, strength: 18)

Acessando propriedades

As propriedades de structs e classes podem ser acessadas com a notação de ponto, em que o nome do valor é seguido de um ponto (.) e do nome da propriedade. Se uma propriedade de um struct ou classe tiver propriedades próprias, essa notação de ponto também pode ser usada para acessar essas propriedades aninhadas.

Essa notação pode ser usada tanto para obter o valor da propriedade quanto, quando for permitido, para alterá-lo.

someStats.health
// => 0
someCharacter.name
// => nil

someStats.health = 87.3
someStats.health
// => 87.3
someCharacter.name = "Luther"
someCharacter.name
// => "Luther"

someCharacter.id  = "new id"
// Error: Cannot assign to property: 'id' is a 'let' constant

Métodos

Assim como as propriedades, que armazenam dados nos seus structs e classes, você também pode definir métodos, que armazenam funções no seu struct ou classe.

Os métodos são definidos da mesma forma que uma função comum, só que dentro do corpo do struct ou da classe. Repare que, se uma função altera o valor de uma propriedade em um struct, ela precisa ser precedida da palavra-chave mutating. Além disso, se uma propriedade puder ser alterada por um método, ela precisa ser definida com var em vez de let, assim como as variáveis comuns.

struct CharacterStats {
  var health = 0.0
  var speed = 0
  var strength = 0

  mutating func takeHit(_ damage: Double) {
    health = max(0.0, health - damage)
  }
  func canLift(_ weight: Int) -> Bool {
    weight < strength * 100
  }
}

class GameCharacter {
  var stats = CharacterStats()
  var characterClass: String?
  var name: String?
  var active = false
  let id: String = makeRandomID()

  func takesDamage(_ damage: Double) {
    stats.takeHit(damage)
    if stats.health <= 0 {
      active = false
    }
  }
  func sayName() -> String {
    return "My name is \(name ?? "no one"), my class is \(characterClass ?? "undetermined")"
  }
  func lift(_ weight: Int) -> String {
    if stats.canLift(weight) {
      return "No problem!"
    } else {
      return "Ooof! No way."
    }
  }
}

Esses métodos podem ser chamados com a notação de ponto, assim como as propriedades.

var myChar = GameCharacter()
myChar.stats = CharacterStats(health: 72.8, speed: 19, strength: 6)
myChar.active = true

myChar.lift(750)
// => "Ooof! No way."
myChar.takesDamage(80)
myChar.active
// => false

Self

Toda instância de um struct ou classe tem uma propriedade implícita chamada self, que se refere à própria instância. O uso mais comum de self é desambiguar entre uma propriedade de instância e um parâmetro com o mesmo nome:

struct Position {
    var x = 0

    mutating func move(x: Int) {
        // Here, 'self.x' refers to the struct's property,
        // while 'x' refers to the parameter passed to the method.
        self.x = x
    }
}

Os tipos do Swift também oferecem a palavra-chave Self com S maiúsculo, que se refere ao tipo da instância e não à instância em si.

Instruções

Neste exercício, você vai simular um sistema computacional baseado em janelas. Você vai criar algumas janelas que podem ser movidas e redimensionadas e que exibem o próprio conteúdo. A imagem a seguir é representativa dos valores com os quais você vai trabalhar daqui em diante.

                  <--------------------- screenSize.width --------------------->

       ^          ╔════════════════════════════════════════════════════════════╗
       |          ║                                                            ║
       |          ║          position.x,_                                      ║
       |          ║          position.y  \                                     ║
       |          ║                       \<----- size.width ----->            ║
       |          ║                 ^      *──────────────────────┐            ║
       |          ║                 |      │        title         │            ║
       |          ║                 |      ├──────────────────────┤            ║
screenSize.height ║                 |      │                      │            ║
       |          ║            size.height │                      │            ║
       |          ║                 |      │       contents       │            ║
       |          ║                 |      │                      │            ║
       |          ║                 |      │                      │            ║
       |          ║                 v      └──────────────────────┘            ║
       |          ║                                                            ║
       |          ║                                                            ║
       v          ╚════════════════════════════════════════════════════════════╝

1. Defina uma struct Size

Defina uma struct chamada Size com duas propriedades Int, width e height, que armazenam a largura e a altura atuais da janela, respectivamente. A largura e a altura iniciais devem ser 80 e 60, respectivamente. Inclua um método resize(newWidth:newHeight:) que recebe novos parâmetros de largura e altura e altera as propriedades para refletir o novo tamanho.

let size1080x764 = Size(width: 1080, height: 764)
// Returns Size
var size1200x800 = size1080x764
// Returns Size
size1200x800.resize(newWidth: 1200, newHeight: 800)
size1200x800.height
// Returns 800

2. Defina uma struct Position

Defina uma struct chamada Position com duas propriedades Int, x e y, que armazenam a posição horizontal e vertical atuais, respectivamente, do canto superior esquerdo da janela. Os valores iniciais de x e y devem ser ambos 0. A posição (0, 0) é o canto superior esquerdo da tela, com os valores de x aumentando conforme você se move para a direita e os valores de y aumentando conforme você se move para baixo.

Inclua um método moveTo(newX:newY:) que recebe novos parâmetros x e y e altera as propriedades para refletir a nova posição.

var point = Position(x: 10, y: 20)
// Returns Position
point.moveTo(newX: 100, newY: -100)
point.y
// Returns -100

3. Defina uma classe Window

Defina uma classe de janela com as seguintes propriedades:

  • title : String, o valor inicial é "New Window"
  • screenSize : Size, valor constante com width = 800 e height = 600
  • size : Size, o valor inicial é o valor padrão da struct Size
  • position : Position, o valor inicial é o valor padrão da struct Position
  • contents : String?, o valor inicial é nil

Você também deve definir um inicializador vazio para a classe.

let window = Window()
window.title
// Returns "New Window"

4. Adicione um método para redimensionar janelas

  • resize(to:) : (Size) -> () - Este método recebe uma struct Size como entrada e tenta redimensionar a janela para o tamanho especificado. No entanto, o novo tamanho não pode ultrapassar certos limites.
  • A altura ou largura mínima permitida é 1. Alturas ou larguras solicitadas menores que 1 serão limitadas a 1.
  • A altura e a largura máximas dependem da posição atual da janela, pois as bordas da janela não podem ultrapassar as bordas da tela. Valores maiores que esses limites serão limitados ao maior tamanho possível. Por exemplo, se a posição da janela estiver em x = 400, y = 300 e for solicitado um redimensionamento para height = 400, width = 300, a janela será redimensionada para height = 300, width = 300, pois a tela não é grande o suficiente na direção y para acomodar totalmente a solicitação.

5. Adicione um método para mover janelas

  • move(to:) : (Position) -> () - Este método é parecido com resize(to:), mas ajusta a posição da janela para o valor solicitado, e não o tamanho. Assim como em resize, a nova posição não pode ultrapassar certos limites.
  • A menor posição é 0 tanto para x quanto para y.
  • A posição máxima em qualquer direção depende do tamanho atual da janela; as bordas não podem ultrapassar as bordas da tela. Valores maiores que esses limites serão limitados ao maior tamanho possível. Por exemplo, se o tamanho da janela estiver em x = 250, y = 100 e for solicitada uma movimentação para x = 600, y = 200, a janela será movida para x = 550, y = 200, pois a tela não é grande o suficiente na direção x para acomodar totalmente a solicitação.

6. Adicione métodos para atualizar o texto da janela e exibir informações da janela

  • update(title:) : (String) -> () - Este método define a propriedade title com o valor da string passada.
  • update(text:) : (String?) -> () - Este método define a propriedade contents com o valor da string opcional passada.
  • display() : () -> String - Este método retorna uma string que descreve o estado atual da janela. Por exemplo, se a janela tiver o title "My First Window" com posição: x = 10, y = 100; tamanho: width = 200, height = 150; e contents: "I 😍 my window", ele deve retornar a string: "My First Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n" - Se contents for nil, a última linha deve ser "[This window intentionally left blank]"

7. Crie um inicializador para a classe Window

O sistema de janelas deve ter um inicializador para que a janela possa ser criada com entradas personalizadas. Crie outro inicializador para a classe Window. O inicializador deve receber os seguintes parâmetros: title, contents e dois parâmetros opcionais, size e position. Os parâmetros size e position devem assumir como padrão os valores padrão das structs Size e Position.

let window = Window(title: "My First Window", contents: "I 😍 my window")
window.display()
// Returns "My First Window\nPosition: (0, 0), Size: (80 x 60)\nI 😍 my window\n"

let window2 = Window(title: "My Second Window", contents: "I 😍 my window", size: Size(width: 200, height: 150), position: Position(x: 10, y: 100))
window2.display()
// Returns "My Second Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n"
Editar via GitHub O link abre em uma nova janela ou aba
Swift Exercism

Tudo pronto para começar Sistema de janelas?

Crie sua conta no Exercism para aprender e dominar Swift com 35 conceitos116 exercícios e mentoria humana de verdade, tudo de graça.