NSPopupButton -
alasvetovalikko
Konffaus käyttöliittymäeditorissa
Interface Builderissa raahaa ikkunaan NSPopupButton
Tietosisältö otetaan taulukosta eli Array Controllerista. Raahaa
sellainen työkalupakista xib-näkymän vasempaan laitaan, ja konffaa
se:
- avaa Assistant editor ja ctrl-raahaa siihen viiva vasemman
reunan Array Controller -kuvasta, tee sinne Outlet
- Identity Inspector: Label eli anna sille jokin kutsumanimi
(esim. arrayHeimot), ei tarvitse olla sama kuin muuttujan nimi
- Attributes Inspector: Laita oikea Class Name, esimerkiksi
Pari (katso lopussa). Avaimia ei tarvitse lisätä
Seuraavaksi konffaa NSPopupButton:
- avaa Assistant editor ja ctrl-raahaa siihen viiva
näytön PopupButtonista, tee sinne Outlet, ja anna osuva nimi,
esimerkiksi popupbtnHeimot
- Identity Inspector: Label varmaankin voidaan antaa
mutta en ole kokeillut
- Attributes Inspector: ei mitään
- Bindings Inspector / Content
Bind to: edellä annettu Array Label, eli esim.
arrayHeimot
Controller Key: arrangedObjects
Model Key Path: jätä tyhjäksi
- Binding Inspector / Content Objects: ei saa olla
ruksattuna 'Bind to'
- Bindings Inspector / Content Values
Bind to: edellä annettu Array Label, eli esim.
arrayHeimot
Controller Key: arrangedObjects
Model Key Path: se avain (Key) Array:sta mikä
näytetään popupButtonissa (Tieto jos Pari)
- Bindings Inspector / Selected Index
Bind to: edellä annettu Array Label, eli esim.
arrayHeimot
Controller Key: selectionIndex
Model Key Path: jätä tyhjäksi
Array Controlleriin tiedot viedään objektitaulukkona, objektina
oltavana luokka (Dictionary ei kelpaa), esimerkiksi class Pari (ks.
alempana)
Koodausesimerkkejä (Swift 4.2)
Jos NSArrayController ei ole käytössä, niin tässä hyödyllisimmät
perusfunktiot. Jäljempää löytyy lisää soveltuvia.
Älä oleta missään, että jotain on valittuna alasvetovalikossa tai
taulukossa, vaan aina tarkista asia.
popupKolme.addItems(withTitles:
["yks", "kaks", "kol"]) // luodaan arvot valikkoon
popupKolme.addItem(withTitle: "kuus")
let vali = popupKolme.indexOfSelectedItem // mikä on
valittuna, vali=-1 jos ei ole valintaa
let zz = popupKolme.itemTitle(at: vali) //
haetaan sen arvo
// jos vali==-1, sovellus kaatuu;
// zz = "" jos ei löydy tällä indeksillä
popupKolme.selectItem(withTitle: "") // poistaa
valinnan
popupKolme.selectItem(withTitle: "nel") // jos "nel" ei ole,
poistaa valinnan
// valitse tyhjä paikka valikosta:
let ii = popupSisa.indexOfItem(withTitle: "") // tarkista
ettei ole -1
popupSisa.selectItem(at: ii)
let xx = popupKolme.indexOfItem(withTitle: "kaks") //
nimeltään tiedetyn vaihtoehdon sijainti
if xx == -1 { // ei löydy }
popupKolme.selectItem(at: xx) // jos xx==-1, sovellus
kaatuu
popupSisa.removeAllItems() // tyhjennetään koko taulukko
Näissä seuraavissa NSArrayController on käytössä.
Älä oleta missään, että jotain on valittuna alasvetovalikossa tai
taulukossa, vaan aina tarkista asia.
Seuraavassa array-alkuinen muuttuja on tyyppiä NSArrayController.
Edellä on ehdotettu, että alasvetovalikon yhteydessä käytettäisiin
luokkaa Pari, jossa on Nimi ja id. Se on tällainen. Sen tietosisältö
on Dictionarynä.
import Cocoa
class Pari: NSObject
{
@objc var idit: Int = -1
@objc var Tieto: String = ""
@objc func Nimi() -> String
{
return Tieto
}
@objc func idPari() -> Int
{
return idit
}
@objc init(idi: Int, nimi: String)
{
idit = idi
Tieto = nimi
}
}
Alasvetovalikkoa varten tiedot kerätään seuraavasti. Tämä esimerkki
käyttää tietokantaa, mutta tiedot voi toki viedä
NSArrayControlleriin vastaavalla tavalla vakiotaulukostakin. Tämän
kutsumisen jälkeen meillä on tiedot paikoillaan alasvetovalikossa:
//
tietokantafunktiot: FMDB 2.7
// kysz = SQL-kysely, jossa kysyttävä Int ja String, esim.
// let kysz = "SELECT idHei AS eka, Nimi AS tokaz FROM Heimo ORDER
BY tokaz"
// eka = true, jos valikon eka kenttä jätetään tyhjäksi
// outputtina true tai false (jos virheitä)
// c = paritListaan(kysz, taulu: arrayHeimot, db: db!, eka: true)
func paritListaan(kysz: String, taulu: NSArrayController, db:
FMDatabase, eka: Bool) -> Bool
{
if kysz.isEmpty
{
print("M74-01: input virhe, kysz on
tyhjä")
return false
}
// tyhjennetään ensin taulu
taulu.content = nil
do
{
let rs = try db.executeQuery(kysz,
values: nil)
if eka==false //
Laitetaan ensimmäiseksi alkioksi valikkoon tyhjä
{
taulu.addObject(Pari(idi: -1, nimi: "")
}
while rs.next()
{
let idi =
Int(rs.int(forColumn: "eka")) // jos kannassa NULL, saadaan
0
let z =
rs.string(forColumn: "tokaz")
let tokaz = (z ==
nil) ? "" : z!
taulu.addObject(Pari(idi: idi, nimi: tokaz)
}
}
catch
{
print("M74-02
\(error.localizedDescription)")
return false
}
taulu.setSelectionIndex(0) //
alasvetovalikon ylin kenttä valituksi
return true
}
Kun käytössä on NSArrayController, käytä sitä mahdollisimman moneen,
äläkä suoraan popupbuttoniin, ellei ole pakko.
Ylimmän heimon asettaminen näkyviin:
arrayHeimot.setSelectionIndex(0)
// ei kaada sovellusta vaikka taulukko tyhjä
arrayHeimot.setSelectionIndex(-1)
// ei toimi terveesti:
// Ei kaada sovellusta, näytöllä eka on ruksattuna, mutta
arrayHeimot.selectedObjects jää tyhjäksi
Tietynnimisen heimon valitseminen (eli asettaminen näkyviin):
let ik =
popupHeimo.indexOfItem(withTitle: "Fabaceae") // -1 ei oo
if ik == -1
{
print("Arvoa 'Fabaceae' ei löydy")
return false
}
arrayHeimot.setSelectionIndex(ik)
Alasvetovalikosta valitun heimon noutaminen:
let tempx =
arrayHeimot.selectedObjects as NSArray
if tempx.count == 0
{
print("Ei ole mitään valittuna
(ohjelmointivirhe tai arvo valitsematta)")
return false
}
let hhh: Pari = tempx.object(at: 0) as! Pari
let iHeimo = hhh.idPari() // Int
let Heimonimi = hhh.Nimi() // String
Tarkastetaan onko mitään heimoa valittu:
if
arrayHeimot.selectionIndex==Foundation.NSNotFound
{
// mitään ei ole valittuna
}
Tyhjennetään valikko
arrayHeimot.content =
nil // tyhjentää vain sisällön
// tyhjentää vain kontrollin, eikä sitä enää voi valita:
popupYksi.removeAllItems()
Jos edellä ei ole mitään heimoa valittuna, niin tuloksena on tyhjä
taulukko tempx. Ilman tarkistuksia seuraava rivi kaataa sovelluksen.
Jos jostain syystä haluat poistaa kaikki valinnat, niin se tapahtuu
näin:
arrayHeimot.setSelectionIndexes(NSIndexSet()
as IndexSet)
Tyhjennetään valikko
arrayHeimot.content =
nil
Alasvetovalikko piilotetaan kokonaan:
popupbtnHeimo.isHidden
= true
Valikon elementtien lukumäärä:
let kpl =
(arrayHeimot.arrangedObjects as! [Pari]).count
Valikon arvon valitsemisen pyydystys, tapa 1: tehdään
käyttöliittymäeditorissa alasvetovalikolle Action. Tämä on
suositeltavin tapa asian hoitamiseen. Kun käyttäjä valitsee arvon
valikosta (saman arvon tai vaihtaa arvon), niin Action-moduuli
käynnistyy. Huomattava on, että jos ohjelmallisesti asettaa
näkyville haluamansa valikon arvon, niin Action-moduuli ei
käynnisty. Sen sijaan seuraavassa kuvattu tarkkailijan Action
käynnistyy.
Valikon arvon valitsemisen pyydystys, tapa 2: #selector
Laitetaan windowDidLoad()
-funktioon rivi:
popupAlbumi.action =
#selector(onALBselected)
Ja luokan muiden funktioiden joukkoon vastaava funktio:
@objc private
func onALBselected()
{
let tt = popupAlbumi!
//let irj = tt.indexOfSelectedItem
// valikon indeksi
let irz = tt.titleOfSelectedItem! //
valikon teksti
var iri = -2
for item in arrayAlbumi.arrangedObjects as!
[AnyObject]
{
let hh: Pari = item as!
Pari // haetaan tekstiä vastaava Parin id
if hh.Nimi() == irz
{
iri =
hh.idPari()
break
}
}
// iri==-2 tarkoittaa virhettä
// tee jotain arvolla iri ja irz
}
Valikon arvon valitsemisen pyydystys, tapa 3: lisää rivien
valinnalle tarkkailija:
arrayAlbumi.addObserver(self,
forKeyPath:"selectionIndex",
options:NSKeyValueObservingOptions.new, context:nil)
Tarkkailun tulos hyödynnetään korvaamalla (override) tarkkailijan
action omalla. Funktion inputeilla selvitetään mistä action tuli:
override func
observeValue(forKeyPath keyPath: String?, of object: Any?, change:
[NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
// if keyPath == "selectionIndex" { }
// if object as! NSObject == arrayAlbumi { }
let tempx = arrayAlbumi.selectedObjects as
NSArray
let hhh: Pari = tempx.object(at: 0) as! Pari
let iAlbumi = hhh.idPari() // Int
let albumiz = hhh.Nimi() // String
if iAlbumi == -1
{
println("Valittiin tyhjä")
return
}
print("valittu \(albumiz)")
// tee tuloksella jotain
}
Tarkkailija pitää poistaa sitten, kun sitä ei enää tarvita, esim
luokan deinitiliaisoinnin yhteydessä:
deinit
{
arrayAlbumi.removeObserver(self, forKeyPath:
"selectionIndex", context: nil)
}
--------------------------------
(sivua muokattu 22.1.2020)