Kurzusok
/
Swift
Swift
/
Feladatok
/
Ablakkezelő rendszer
Ablakkezelő rendszer

Ablakkezelő rendszer

Tanulófeladat

Bevezetés

Struktúrák és osztályok

A struktúrák és az osztályok a Swift-programozás két legfontosabb építőköve. Mindkettő arra való, hogy a kapcsolódó adatokat és függvényeket önálló funkcionális egységekbe csoportosítsuk. Amikor pedig struktúrát vagy osztályt definiálsz, valójában egy új típust hozol létre a Swiftben, éppúgy, ahogy a korábban megismert típusokat is használtad, például az Int-et és a String-et.

A Swiftben a struktúrák és az osztályok között számos hasonlóság van. Többek között mindkettő képes értékeket tárolni tulajdonságokban, és funkcionalitást nyújtani metódusok segítségével.

Mindkettő nyújt emellett néhány további funkciót, amelyekre ebben a feladatban nem térünk ki.

Struktúrák és osztályok definiálása

A struktúrákat és az osztályokat nagyjából ugyanúgy definiáljuk. Egy megfelelő kulcsszóval kezdődnek, amelyet a definiált típus neve követ, majd a struktúra vagy osztály törzse következik, kapcsos zárójelek között.

A törzs tárolt tulajdonságokból állhat, amelyeket ugyanúgy definiálunk, és ugyanúgy viselkednek, mint a hagyományos konstansok vagy változók.

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()
}

Példányok

Ahogy fentebb említettük, egy struktúra vagy osztály definiálása csupán egy új típus definiálása. Ez csak egy tervrajz arra, hogy milyenek lesznek az adott típus értékei, de valójában nem hoz létre egyetlen ilyen típusú értéket sem, amellyel dolgozhatnál.

Ahhoz, hogy létrehozz egy példányt ebből a típusból, a típus nevét kell leírnod, majd utána egy pár zárójelet.

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

Ez létrehozza ezen típusok értékeit, ahol a tulajdonságok a definícióban megadott alapértelmezett értékekkel töltődnek fel. Vedd figyelembe, hogy opcionális esetekben, mint például a GameCharacter name tulajdonsága, ha nem adunk meg értéket, a tulajdonság értéke nil lesz, éppúgy, mint amikor hagyományos opcionális típusokat definiálunk úgy, hogy nem adunk meg azonnal értéket.

Struktúrák esetében a Swift automatikusan biztosít valamit, amit tagonkénti kezdőértéknek nevezünk. Ennek segítségével a struktúra tulajdonságaihoz a zárójelek között adhatsz meg értékeket, amelyek felülírják a definícióban szereplő alapértelmezett értékeket.

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

Tulajdonságok elérése

A struktúrák és osztályok tulajdonságai pont jelöléssel érhetők el, ahol az érték nevét egy pont (.) követi, majd a tulajdonság neve. Ha egy struktúra vagy osztály valamely tulajdonságának saját tulajdonságai vannak, ezzel a pont jelöléssel ezek az egymásba ágyazott tulajdonságok is elérhetők.

Ez a jelölés arra is használható, hogy lekérdezzük a tulajdonság értékét, és ahol megengedett, meg is változtassuk azt.

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

Metódusok

A tulajdonságokhoz hasonlóan, amelyek az adatokat tárolják a struktúráidban és osztályaidban, metódusokat is definiálhatsz, amelyek függvényeket tárolnak a struktúrádban vagy osztályodban.

A metódusokat ugyanúgy definiáljuk, mint egy hagyományos függvényt, csak éppen a struktúra vagy osztály törzsén belül. Vedd figyelembe, hogy ha egy függvény megváltoztatja egy struktúra valamely tulajdonságának értékét, azt a mutating kulcsszóval kell megelőzni. Továbbá, ha egy tulajdonságot egy metódus megváltoztathat, akkor azt a tulajdonságot let helyett var-ral kell definiálni, éppúgy, mint a hagyományos változókat.

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."
    }
  }
}

Ezek a metódusok pont jelöléssel hívhatók meg, akárcsak a tulajdonságok.

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

A struktúrák és osztályok minden példányának van egy implicit self nevű tulajdonsága, amely magára a példányra utal. A self leggyakoribb használata, hogy megkülönböztesse egymástól a példány egy tulajdonságát és egy vele azonos nevű paramétert:

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
    }
}

A Swift-típusok emellett biztosítanak egy nagybetűs Self kulcsszót is, amely a példány típusára utal, nem magára a példányra.

Utasítások

Ebben a feladatban egy ablakokon alapuló számítógépes rendszert fogsz szimulálni. Létrehozol néhány ablakot, amelyeket mozgatni és átméretezni lehet, és amelyek megjelenítik a tartalmukat. A következő ábra szemlélteti azokat az értékeket, amelyekkel az alábbiakban dolgozni fogsz.

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

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

1. Definiálj egy Size struktúrát

Definiálj egy Size nevű struktúrát két Int tulajdonsággal, width és height, amelyek rendre az ablak aktuális szélességét és magasságát tárolják. A kezdeti szélesség és magasság rendre 80 és 60 legyen. Helyezz el benne egy resize(newWidth:newHeight:) metódust, amely átveszi az új szélesség és magasság paramétereit, és úgy módosítja a tulajdonságokat, hogy azok az új méretet tükrözzék.

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. Definiálj egy Position struktúrát

Definiálj egy Position nevű struktúrát két Int tulajdonsággal, x és y, amelyek rendre az ablak bal felső sarkának aktuális vízszintes és függőleges pozícióját tárolják. Az x és az y kezdeti értéke egyaránt 0 legyen. A (0, 0) pozíció a képernyő bal felső sarka, ahol az x értéke jobbra haladva, az y értéke pedig lefelé haladva növekszik.

Helyezz el benne egy moveTo(newX:newY:) metódust, amely átveszi az új x és y paramétereket, és úgy módosítja a tulajdonságokat, hogy azok az új pozíciót tükrözzék.

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

3. Definiálj egy Window osztályt

Definiálj egy ablakosztályt a következő tulajdonságokkal:

  • title : String, a kezdeti értéke "New Window"
  • screenSize : Size, konstans érték, ahol width = 800 és height = 600
  • size : Size, a kezdeti értéke a Size struktúra alapértéke
  • position : Position, a kezdeti értéke a Position struktúra alapértéke
  • contents : String?, a kezdeti értéke nil

Az osztályhoz egy üres kezdőértéket is definiálnod kell.

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

4. Adj hozzá egy metódust az ablakok átméretezéséhez

  • resize(to:) : (Size) -> () - Ez a metódus egy Size struktúrát vesz bemenetként, és megpróbálja a megadott méretre átméretezni az ablakot. Az új méret azonban nem léphet túl bizonyos határokat.
  • A megengedett legkisebb magasság vagy szélesség 1. Az 1-nél kisebb kért magasság vagy szélesség 1-re lesz levágva.
  • A legnagyobb magasság és szélesség az ablak aktuális pozíciójától függ, az ablak szélei nem kerülhetnek túl a képernyő szélein. Az ezeknél a határoknál nagyobb értékek a lehető legnagyobb méretre lesznek levágva. Ha például az ablak pozíciója x = 400, y = 300, és height = 400, width = 300 méretre kérsz átméretezést, akkor az ablak height = 300, width = 300 méretre lesz átméretezve, mivel a képernyő az y irányban nem elég nagy ahhoz, hogy teljesen befogadja a kérést.

5. Adj hozzá egy metódust az ablakok mozgatásához

  • move(to:) : (Position) -> () - Ez hasonló a resize(to:) metódushoz, csakhogy ez a metódus az ablak pozícióját állítja a kért értékre a mérete helyett. Ahogy a resize esetében, az új pozíció sem léphet túl bizonyos határokat.
  • A legkisebb pozíció x és y esetében is 0.
  • A legnagyobb pozíció mindkét irányban az ablak aktuális méretétől függ; a szélek nem kerülhetnek túl a képernyő szélein. Az ezeknél a határoknál nagyobb értékek a lehető legnagyobb méretre lesznek levágva. Ha például az ablak mérete x = 250, y = 100, és x = 600, y = 200 pozícióra kérsz mozgatást, akkor az ablak x = 550, y = 200 pozícióra lesz mozgatva, mivel a képernyő az x irányban nem elég nagy ahhoz, hogy teljesen befogadja a kérést.

6. Adj hozzá metódusokat az ablak szövegének frissítéséhez és az ablak adatainak megjelenítéséhez

  • update(title:) : (String) -> () - Ez a metódus a title tulajdonságot a neki átadott string értékére állítja.
  • update(text:) : (String?) -> () - Ez a metódus a contents tulajdonságot a neki átadott opcionális string értékére állítja.
  • display() : () -> String - Ez a metódus egy stringet ad vissza, amely leírja az ablak aktuális állapotát. Ha például az ablak title értéke "My First Window", a pozíciója x = 10, y = 100, a mérete width = 200, height = 150, a tartalma pedig "I 😍 my window", akkor a visszaadott string ez legyen: "My First Window\nPosition: (10, 100), Size: (200 x 150)\nI 😍 my window\n" - Ha a contents értéke nil, akkor az utolsó sor ez legyen: "[This window intentionally left blank]"

7. Hozz létre egy kezdőértéket a Window osztályhoz

Az ablakrendszernek rendelkeznie kell egy kezdőértékkel, hogy az ablak egyéni bemenetekkel is létrehozható legyen. Hozz létre még egy kezdőértéket a Window osztályhoz. A kezdőérték a következő paramétereket vegye át: title, contents, valamint két opcionális paraméter, a size és a position. A size és a position paraméter alapértéke a Size és a Position struktúra alapértéke legyen.

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"
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Swift Exercism

Készen állsz elkezdeni a(z) Ablakkezelő rendszer feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Swift nyelvet 35 fogalom116 feladat segítségével, valódi emberi mentorálással, mindez ingyen.