iOS programozás

Sajnos belefutottam egy problémába, amit nem tudok megoldani. Így néz ki az app:

A lényeg: alul van egy collectionView, amiben egy item-re kattintva a játékos kikerül a pályára.(Gyakorlatilag egy imageView-t hozzáadok subview-ként a pályát tartalmazó imageView-hoz.) Ezeknek a kis imageView-knak van 2 UIGestureRecognizere, egy TapGestureRecognizer, és egy PanGestureRecognizer. A tap az arra van, hogy dupla tappolással vissza lehessen helyezni a collectionView-ba, a Pan pedig, hogy a pályán belül szabadon lehessen mozgatni bárhova. Ez eddig teljesen jól is működött. Ekkor a következő task az lett, hogy a pályába bele lehessen zoomolni.
Erre jött az ötlet, hogy a nagy imageView mögé tegyünk egy UIScrollView-t ezzel gyakorlatilag meg is oldva a zoomolást. A probléma az az, hogy ebben az esetben a Scrollview miatt a kis imageview-k nem kapják meg a Gesture-öket, mert a Scrollview “elfogja” az összeset. (ha a scrollviewnak adok subview-ként a kis képeket, akkor meg nem zoomolódnak együtt a pályával.)
Szóval a kérdésem az lenne, hogy megoldható e valahogy, hogy a Gesture event-eket továbbítsam a kis képeknek valahogy? Stackoverflown láttam pár megoldást, (pl a scrollviewnak adni egy tapgesture recognizert/ UITapGestureRecognizerDelegate-nek implementáljam pár függvényét stb) de sajnos egyik sem működött.

Úgy emlékszem, hogy volt valami opció, hogy átadja az UIScrollView az eseményeket a subview-öknek, de már régen volt ez a probléma nálam, így nem emlékszem, nem tartom kizártnak, hogy egy subclass-t kellett volna létrehozni az UIScrollView-ből.

Viszont miért nem kezeled az összes gesture-t az UIScrollView-ben? Ráadásul szerintem ha a subview-re teszed a pan gesture-t, akkor ha gyorsan húzod az ujjad, akkor lehet nem fogja követni az ujjadat, mert könnyen kihúzhatod a képből és akkor nem kapja meg a pan gesture eseményt.

Oké, akkor ezt átgondolom.

De ez mondjuk miért segít?

open class MyClass: UIScrollView{    }

Ezt a függvényt ajánlották sokan, ahogy néztem Stackoverflown: https://developer.apple.com/documentation/uikit/uiview/1622469-hittest

Azért nem kezelem, mert akkor nem vagyok benne biztos, hogy le tudnám e kérdezni az adott kis képet. A collectionview datasource tömbjében string-ek vannak, a képek nevei. (pl player_1.png, player_2.png) és ezekből leveszem a számot a végéről, és mindig az aktuális imageView-nak a tag-jának adom, így amikor kitörlöm, akkor tudom, hogy melyik képet kell visszarakni a tömbbe.

@objc func deleteView(_ sender: UITapGestureRecognizer){
    let index = sender.view?.tag
    playerImages.append("player_\(index!).png")
    sender.view?.removeFromSuperview()
    playersCollectionView.insertItems(at: [IndexPath(item: playerImages.count-1, section: 0)])
    playerCounter -= 1
}

Ha nem a kis képnek adnám a GestureRecognizert, akkor nem tudnám a sender.view?.tag-ot lekérni, mert a sender az nem a kis kép lenne.

Persze, hogy meg tudod mondani a tag-et, mivel eleve meg kell tudnod, hogy melyik ImageView-on nyomtál rá. Ehhez le kell kérdezned a locationt:
sender.location(in: self) // talán self vagy self.view
Aztán végigmész az összes imageview-.on majd rákérdezel, hogy a frame értéke tartalmazza-e a location-t. Ha igen, akkor az a kiválasztott UIImageView.

Ugyanakkor én nem használnám a tag értéket erre. Szerintem sokkal elegánsabb, ha csinálsz egy osztályt az UIImageView-ből, annak adsz mondjuk egy képneve értéket és abban mented el, majd ezt persze el kell menteni egy tömbben, de ha lekérdezed, akkor egyértelműen lesz egy képneve értéke. (vagy ha nem mented el tömbben, akkor lesz egy (imageview as? MyImageView)?.kepneve… )

Az UIScrollView subclassnál természetesen úgy gondoltam, hogy egy beépített függvényt felül kell írni, nem csak csinálni egy üres osztályt :D

1 Like

Nyilván az üres classban overridoltam pár függvényt, amit írtak SO-n de nem működött. Akkor tényleg, kipróbálom, ezt, hogy a Scrollview-ban kezelem a gesture-öket, bár itt 2 dolog merülhet fel: 1/ mi van akkor, ha 2 kis kép van egymáson, ez esetben a locationInView az több képnél is lesz? Meg a másik, hogy a Scrollview-nak alapból egy Pan és egy Pinch gesturerecognizere-van, ami egyrészt azt jelenti, hogy a TapGesture Recognizert nekem kell hozzáadni, másrészt, ha átírom a PanGestureRecognizerét, akkor lehet nem lehet majd scrollozni.

A tag az elvileg pont erre van kitalálva, hogy ha programmatically akarok View-kat kirakni a screenre, akkor meg lehessen különböztetni őket. Persze ebben az esetben, hogy nem csak egy szimpla szám ez, hanem gyakorlatilag egy fájlnév lesz belőle, ezért lehet tényleg logikusabb/elegánsabb sublclassolni a UIImageView-t és felvenni, új propertyként.
Köszi!

UPDATE: Sikerült megcsinálni, most lehet húzogatni, és törölni is, annyi van, hogy 1 ujjal nem lehet scrollozni (gondolom mert felülírtam a gyári Pant), de 2-vel meg lehet, érdekes…
Ja, és most jobban kell figyelni, mert a gyors mozgatásnál tényleg nem érzékel a Pan mert a locationInView az már “kimegy” a kis képből.
De egyébként működik nagyon jól, köszi még egyszer! :)

  1. Szóval az, hogy egyszerre hány képet mogzatsz, ha egymáson van, az rajtad múlik (így legalább azt is meg lehet oldani, hogy egyszerre több képet mozgatsz).
  2. Ha a scrollview-t egy újjal mozgatod, akkor a pan lekezelésekor ha az ujjad alatt nincs kép, akkor a scrollview-t kell mozgatni. Bár én két újjal oldanám meg eleve a mozgatást, mivel kicsinyíteni-nagyítani is két újjal kell és a méretezéssel egyszerre lehet is pozicionálni.
  3. Igen, a tag-et fel lehet használni egyébként csak akkor van bibi, ha egy külső framework is felhaszálja. Bár általában ezt nem szokták felhasználni. Viszont szerintem kényelmesebb subclasst létrehozni és nem pedig tagből számolgatni a fájlneveket. Ráadásul úgy akárhogy elnevezheted a képeket ellenben a tag használatával.
  4. Gyors mozgatásnál meg nyilván úgy kell megcsinálni, hogy a pan gesture kezdetekor (state == .begin) el kell menteni, hogy mit mozgatsz és akkor eleve mozgatás közben már le sem kell kérdezni, hogy mi mozog és nincs gond, ha kihúzod az ujjad a képből.

https://www.thedevelopersunion.org

Kíváncsi vagyok mire jutnak az Apple-lel.

Xcode builddel kicsit elakadtam.

A következő az alaphelyzet:
Épp egy framewörköt (Cocoa Touch, Swiftben) készítek, és vele együtt párhuzamosan fejlesztek egy alkalmazást, ahova pedig a végleges verzió kerül.
Az elképzelés az, hogy egy projekten fejlesztem a frameworköt és az alkalmazást is. Pontosabban egy workspace alatt, de külön projektenként, azaz az alkalmazásnak is van külön xcode projektje külön github projekttel és a frameworknek is.
Szóval Xcodeban csináltam két külön Targetet. Az egyikben az utoljára kiadott framework verziót szeretném használni, míg a másik Targetben pedig írom a frameworköt az alkalmazással együtt. Ha eljutok a framework írásában egy adott pontig, akkor azt megjelölöm githubon taggel és Carthage segítségével töltöm le ezt a verziót és ez van berakva az első targethez. Szóval az első targethez a Carthage/Build… útvonal van beállítva, hogy keresse a frameworköt (Build Settings - Framework Search Paths), valamint az $inherited érték elé van húzva a Carthage/Build… útvonala.
A másik targetnél pedig az Xcode projekt build mappája van csak megadva, azaz ott keresi a frameworköt.

A porblémám:
iPhone-on tökéletesen működik. Azaz a megfelelő frameworköt teszi be mindkét targetnél. Viszont iPhone szimulátorban futó első targethez mindig az Xcode build mappából adja hozzá a framewörköt, azaz nem a carthage által letöltött verziót, hanem a fejlesztés alatt álló verziót.
Valószínűleg azért, mert ott is keresi a framewörköket és név szerint megtalálja.

Többször átnéztem már, hogy az első targetnél csak a Carthage útvonalon lévő framework van megadva, a Framework Search Pathsnél az inherited-en kívül szintén csak a Carthage mappája van megadva. (Az inheritedet azért szeretném bent hagyni, mert nem kizárt, hogy más frameworkre is fogok majd támaszkodni a későbbiekben.). Átnéztem a Build Phasesben is, hogy csak a Carthage mappából kellene átmásolnia a Framework mappába a frameworköt.

Szóval mit lehetne tenni, hogy rendesen működjön, hol hibázhatok még?

Megoldottam “parasztban”. Mivel rájöttem, hogy csak akkor van baj, ha a Derived Data mappában már szerepel a teszt alatt lefordított Framework. Ha előtte kézzel eltávolítom, akkor a helyes Frameworköt teszi be.
És betettem egy scripttel Build Phase elejére (több framework is van):

REMOVABLE_FRAMEWORK_NAMES=("Framework1" "Framework2" "Framework3")
for FRAMEWORK_NAME in "${REMOVABLE_FRAMEWORK_NAMES[@]}"
do
    REMOVABLE_FRAMEWORK_PATH="${BUILT_PRODUCTS_DIR}/${FRAMEWORK_NAME}.framework"
    if [[ -d "${REMOVABLE_FRAMEWORK_PATH}" ]];then
        rm -r "${REMOVABLE_FRAMEWORK_PATH}"
    fi
done
2 Likes

Közben úgy néz ki, hogy nem csak a Szimulátoron való futtatáskor van ez a gond, hanem amikor archiválom a projektet, hogy feltöltsem az App Store-ba. Még jó, hogy észrevettem, hogy a develop verziót teszi be az Archívumba, mielőtt feltöltöttem volna úgy az Appot, hogy félkész framewörkök vannak benne…

[UIColor anomália]

Van egy függvényem, ami egy egy hexa színkódból UIColor-t csinál.

extension UIColor {
    static func hexStringToUIColor (_ hex: String) -> UIColor {
            var colorString: String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
            
            if (colorString.hasPrefix("#")) {
                colorString.remove(at: colorString.startIndex)
            }
            
            if (colorString.count != 6) {
                return UIColor.gray
            }
            
            var rgbValue: UInt32 = 0
            Scanner(string: colorString).scanHexInt32(&rgbValue)
            
            return UIColor(
                red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
                green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
                blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
                alpha: CGFloat(1.0)
            )
        }
}

A probléma az az, hogy van 2 uiView-m egymás mellet, az egyik színe kódból van állítva:

view.backgroundColor = UIColor.hexStringToUIColor("006737")

míg a másik színe a storyboardon van beállítva, UGYANAZZAL a színkóddal, és az eredmény mégis ez:

Teljesen más az árnyalat. (bal oldalt a kódból állított)

Na, most vagy az van, hogy a függvényem ■■■■, vagy nem tudom mi lehet a gond.

Én egy ilyet használok (még ez a kód swift2 vagy swift3-ban van). Elvileg ezt akkoriban ellenőriztem, hogy egyezik-e. Viszont itt osztást használ, ami meg kereket és ki tudja, hogy jól-e vagy sem :)

public extension UIColor {
    convenience init(hexString: String) {
    
        var workingString = hexString
        if workingString.hasPrefix("#") {
            workingString = String(workingString.characters.dropFirst())
        }
    
        var hexRed = "00"
        var hexGreen = "00"
        var hexBlue = "00"
    
        if workingString.characters.count == 6 {
            hexRed = workingString[0...1]
            hexGreen = workingString[2...3]
            hexBlue = workingString[4...5]
        } else if workingString.characters.count == 3 {
            let redValue = workingString[0]
            let greenValue = workingString[1]
            let blueValue = workingString[2]
            hexRed = "\(redValue)\(redValue)"
            hexGreen = "\(greenValue)\(greenValue)"
            hexBlue = "\(blueValue)\(blueValue)"
        }
    
        let red = CGFloat(hexRed.hexToInteger())
        let green = CGFloat(hexGreen.hexToInteger())
        let blue = CGFloat(hexBlue.hexToInteger())
    
        self.init(red: CGFloat(red / 255.0), green: CGFloat(green / 255.0), blue: CGFloat(blue / 255.0), alpha: 1.0)
    }
}
1 Like

Szinprofil. A jobb oldali szinre hatassal van a megjelenito szinprofilja. Ugyonezt tapasztalhatod, ha kepet raksz be hatternek. http://www.vsanthanam.com/writing/2017/7/6/colors-in-ios-ensuring-consistency-between-designs-interface-builder-and-uicolor

1 Like

Ez egy nagyon jó cikk volt, köszi :)

Közben linkeltek egy cikket lejjebb, de köszi, kipróbálom, hátha a függvény sem jó.

Jelenleg ez a projektem struktúrája:

Alapvetően egy sakkos alkalmazás lesz, ahol pgn fájlokat lehet beolvasni, lejátszani, vagy akár 2 játékos is tud egymás ellen játszani, gép tudja az állást értékelni. A lényeg megvan, viszont lenne egy kérdésem:
Készítettem a táblának, a bábukkal együtt egy frameworkot, így gyakorlatilag 2 sor kóddal egy UIView-ből tudok csinálni egy fully functional sakktáblát. (figyeli a szabályos lépéseket, stb)

Viszont ahogy a képen látszik is, 2 assets mappám van, egy magának a projektnek, egy a frameworknek. Értelemszerűen a frameworkben szeretném tárolni a bábuk képeit, hiszen ez teljes mértékben független magától az apptól.
A probléma az, hogy bár az Xcode látja a képeket ha ide behelyezem, mégis ha le akarom futtatni az appot, akkor elszáll, mert nem találja a képeket (nil). Ha a projekt asset folderében van akkor megy gond nélkül.
Hogyan tudnám áthelyezni a képeket úgy, hogy “lássa”?

Kód elérhető githubon: GitHub - grimmdaniel/szakdolgozat: Bsc szakdolgozat

Azt tudod, hogy ezek egyaltalan atkerulnek-e az eszkozre? Azt kellene elsore kideriteni, hogy mi a konkret problema.

Azt tudom elképzelni, hogy az elérési út a rossz, mert nem a gyökér könyvtárban van az assets folder, ezért, a UIImage(named: “black_rook.png”) nem találja meg. Viszont akkor nem értem, hogy ha konkrétan behúzom mint image literal akkor elvileg eléri, de futás közben mégsem.
Hogy érted, hogy átkerülnek e?

Ha exportolsz az appodbol egy ipa-t, akkor ha az ipa-t atnevezed zipre, benne vannak a kepek? A legtobbszor csak annyi a baj, hogy nem kerulnek at a kepek az eszkozre.

Kiexportáltam, az assets.car-ban megnéztem mik vannak, és úgy tűnik valóban nem kerültek be a képek, minden más viszont benne van.