Syysleimu


NSCollectionView - matriisinäkymä


Matriisinäkymässä tyypillisesti esitetään kuvia ja niiden ohella mahdollisesti kuvan nimiä tai vastaavaa. Koodaaminen on hieman haastavaa lähinnä sen takia, että netistä löytyy lähinnä vain vanhentuneita ja sen takia toimimattomia ohjeita. Ja niistäkin vain osa Swift-kielellä. Ja esimerkiksi Applen tuorein koodausesimerkki on ikkunoiden ja ohjelmatiedostojen käsittelyssä toivottoman monimutkainen ja hukkaa muun sälän alle NSCollectionView:n keskeisen koodaustavan.

Seuraavassa luokka, joka sisältää NSCollectionView:n, on nimeltään Matriisi. Sitä varten tehdään xib- ja swift-tiedostot. Lisäksi matriisin yhtä elementtiä varten on luokka Matritem, jolle tehdään xib- ja swift-tiedostot. Lisäksi on erillinen tiedosto MatritemView.swift, jossa kuvataan eräät View:n poikkeukset. Ohjelmointiesimerkissä NSCollectionView:n sisältämä luokka on nimeltään Tabikku. Esimerkki on koodattu: Swift 4, macOS 10.13, Xcode 9.3.

Konffaus käyttöliittymäeditorissa

Tee uusi Cocoa-tiedosto Matriisi valikon kautta (File->New->File): Cocoa class, Subclass of NSCollectionViewItem, Also creat XIB file.

Matritem.swift ja MatritemView.swift:
- laita datarakenteet paikoilleen (tietolähde, delegaattifunktiot, MatritemView mallin mukaan), ks. edellämainittu ohjelmointiesimerkki
- kiitokset: https://github.com/klaas/CollectionViewElCapitan

Matritem.xib:

Raahaa dokumenttisarakkeeseen Collection View Item, ja merkitse: Identity Inspector / Class = Matritem.

Aluksi on vain Custom View. Nyt sinne lisätään paikka kuvalle ja tekstille. Älä laita kummallekaan mittoja (width, height), se tehdään koodissa. Mutta korjaa tekstin korkeudeksi 16. Toimenpiteet:
-    From the Object Library, add an Image View to View
-    Select and click Pin from the Auto Layout toolbar to set its constraints
      pin button tarkoittaa tätä
        Huom! Jotta Pin näkyisi, oikean ylälaidan kohdassa "File Inspector" on oltava ruksattuna "Use Auto Layout"
-    Set the top, leading and trailing constraints to 0 (tai esim 1 mutta muuta kaikkia), the bottom to 22 and click Add 4 Constraints
-    Select Editor \ Resolve Auto Layout Issues \ Update Frames (ei aina tarpeen)
-    Add a Label below the Image View, and click the Pin button.
-    Set the top, bottom, trailing and leading constraints to 0 (tai esim 1 mutta muuta kaikkia), and then click Add 4 Constraints
-    Select Editor \ Resolve Auto Layout Issues \ Update Frames (ei aina tarpeen)

Tee outletit imageview:lle (imaviewKuva) ja labelille (labelSelite), huomaa Objectin valinta:
object selection

Tee Connections Inspector -näytössä tälle Matritem-objektille outlet -> View ctrl-vetämällä view:n oikean reunan ympyrästä vasemmalle:
tehdään outlet

View:
-    Identity Inspector: Class = MatritemView (jolle on swift-faili)
-    (muille ei lisättävää)

imaviewKuva:
-    Attributes Inspector:  Alignment alas keskelle
-    Bindings Inspector / Value:
    -    Bind to = Matritem
    -    Model Key Path = representedObject.kuva  (kirjoita kenttään)
-    Connections Inspector: täällä pitäisi näkyä em. Bindings ja Referencing outlet

labelSelite:
-    Attributes Inspector: Alignment keskelle, Font tarvittaessa pienemmäksi, Line Break: Truncate Tail
-    Bindings Inspector / Value:
    -    Bind to = Matritem
    -    Model Key Path = representedObject.selite  (kirjoita kenttään)
-    Connections Inspector: täällä pitäisi näkyä em. Bindings ja Referencing outlet

Matritem object
Ylläolevalle objektille
-  Identity Inspector: Class = Matritem

Connections Inspector lopuksi:
connection inspector näkymä

Koodaus:

Luokan nimessä määrittele TableView:tä varten tieto, että taulukkonäkymän ja tietolähteen tuki löytyvät samasta luokasta:
class Matriisi: NSWindowController, NSCollectionViewDataSource, NSCollectionViewDelegate

Luokkamuuttujiksi myös taulukko, josta data otetaan (luokkien taulukko):
var kuvat = [Kuvainfo]()
@IBOutlet weak var colleView: NSCollectionView!


Tässä Kuvainfo on luokka, joka pitää erikseen määritellä, esimerkki alla. Luokan muuttujista ei tarvitse kaikkia käyttää taulukkonäkymässä, käytettävät kerrotaan koodissa:
class Kuvainfo: NSObject
{
   @objc var idKuva: Int = -1
   @objc var Pvm: String = ""
   @objc var Nimi: String = ""
   @objc var kuva: NSImage? = nil
   @objc var polkuz: String = ""

   @objc init(idKuva: Int, .....)
   {
      //....
   }
}


Luokan muodostajaan windowDidLoad() lisää rivit:
   colleView.delegate = self
   colleView.dataSource = self

Seuraavat taulukkonäkymän tukifunktiot tarvitaan (eka ja kolmas ovat pakollisia):
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem   {  }

func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {  }

func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int
{
    return kuvat.count
}

Ensimmäinen käytännössä vie sisällön taulukosta kuvat matriisinäkymään.
Toinen tulee kutsutuksi silloin kun käyttäjä valitsee matriisin jonkun elementin.
Kolmas kertoo matriisin elementtien lukumäärän.
Näillä funktioilla taustajärjestelmät hoitavat matriisinäkymää ja käyttäjän toimenpiteitä.

Funktioiden sisällä on omia muuttujia ja vakioita, katso ohjelmointiesimerkki.

Sovelluksen alussa viedään taulukkoon kuvat sisältö, ja kun aloitusfunktiot on suoritettu, myös taulukkonäkymässä ovat tiedot paikoillaan. Tietolähteen (kuvat) tietoja voi myöhemmin muuttaa vaikkapa vain yhden tiedon osalta (esimerkiksi päivämäärää),  minkä jälkeen seuraavalla komennolla taulukkonäkymä päivittyy ja säilyy samalla kohdalla:
colleView.reloadData()

Taulukko ja matriisi tyhjennetään kokonaan näin:
kuvat.removeAll()
colleView.reloadData()

 
Mikä elementti on kuvamatriisissa valittuna:
let valitut = colleView.selectionIndexPaths
if valitut.count > 0  // 0 = ei ole mitään valittuna
{
    let indpath = valitut.first
    let inde = indpath!.item
    print("Valitun id=\(kuvat[inde].idKuva)")
}

Tehdään valituksi matriisin kuva, jonka idKuva = idi, esimerkki alla. Tässä myös skrollataan valittu kuva näkyviin:
var merk = false
for i in 0..<kuvat.count
{
    if kuvat[i].idKuva == idi
    {
        colleView.selectItems(at: [IndexPath(item: i, section: 0)], scrollPosition: NSCollectionView.ScrollPosition.top)
        merk = true
    }
    else  // vanhojen valintojen poisto on tarpeen!
    {
        colleView.deselectItems(at: [IndexPath(item: i, section: 0)])
        if colleView.selectionIndexPaths.count == 1 && merk == true
        {
           break
        }
    }
}


Matriisin elementin klikkaamisella halutaan tapahtuvan jotain: kirjoita haluamaasi koodia tähän delegaatin funktioon:
func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set<IndexPath>) {  }






--------------------------------
(sivua muokattu 7.4.2018)